aboutsummaryrefslogtreecommitdiff
path: root/src/sourcemap/sourcemap.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/sourcemap/sourcemap.zig')
-rw-r--r--src/sourcemap/sourcemap.zig181
1 files changed, 158 insertions, 23 deletions
diff --git a/src/sourcemap/sourcemap.zig b/src/sourcemap/sourcemap.zig
index f7a92a9c1..b7441ccc9 100644
--- a/src/sourcemap/sourcemap.zig
+++ b/src/sourcemap/sourcemap.zig
@@ -5,6 +5,7 @@ pub const VLQ_CONTINUATION_BIT: u32 = VLQ_BASE;
pub const VLQ_CONTINUATION_MASK: u32 = 1 << VLQ_CONTINUATION_BIT;
const std = @import("std");
const bun = @import("root").bun;
+const string = bun.string;
const JSAst = bun.JSAst;
const BabyList = JSAst.BabyList;
const Logger = @import("root").bun.logger;
@@ -315,7 +316,7 @@ pub const Mapping = struct {
};
};
-pub const LineColumnOffset = packed struct {
+pub const LineColumnOffset = struct {
lines: i32 = 0,
columns: i32 = 0,
@@ -329,8 +330,24 @@ pub const LineColumnOffset = packed struct {
.value => this.value.advance(input),
}
}
+
+ pub fn reset(this: *Optional) void {
+ switch (this.*) {
+ .null => {},
+ .value => this.value = .{},
+ }
+ }
};
+ pub fn add(this: *LineColumnOffset, b: LineColumnOffset) void {
+ if (b.lines == 0) {
+ this.columns += b.columns;
+ } else {
+ this.lines += b.lines;
+ this.columns = b.columns;
+ }
+ }
+
pub fn advance(this: *LineColumnOffset, input: []const u8) void {
var columns = this.columns;
defer this.columns = columns;
@@ -339,8 +356,7 @@ pub const LineColumnOffset = packed struct {
std.debug.assert(i >= offset);
std.debug.assert(i < input.len);
- columns += @intCast(i32, i - offset);
- offset = i;
+ offset = i + 1;
var cp = strings.CodepointIterator.initOffset(input, offset);
var cursor = strings.CodepointIterator.Cursor{};
@@ -366,8 +382,10 @@ pub const LineColumnOffset = packed struct {
},
}
}
+ }
- columns += @intCast(i32, input.len - offset);
+ pub fn comesBefore(a: LineColumnOffset, b: LineColumnOffset) bool {
+ return a.lines < b.lines or (a.lines == b.lines and a.columns < b.columns);
}
pub fn cmp(_: void, a: LineColumnOffset, b: LineColumnOffset) std.math.Order {
@@ -392,10 +410,103 @@ pub fn find(
return Mapping.find(this.mapping, line, column);
}
+pub const SourceMapShifts = struct {
+ before: LineColumnOffset,
+ after: LineColumnOffset,
+};
+
pub const SourceMapPieces = struct {
prefix: std.ArrayList(u8),
mappings: std.ArrayList(u8),
suffix: std.ArrayList(u8),
+
+ pub fn init(allocator: std.mem.Allocator) SourceMapPieces {
+ return .{
+ .prefix = std.ArrayList(u8).init(allocator),
+ .mappings = std.ArrayList(u8).init(allocator),
+ .suffix = std.ArrayList(u8).init(allocator),
+ };
+ }
+
+ pub fn hasContent(this: *SourceMapPieces) bool {
+ return (this.prefix.items.len + this.mappings.items.len + this.suffix.items.len) > 0;
+ }
+
+ pub fn finalize(this: *SourceMapPieces, allocator: std.mem.Allocator, _shifts: []SourceMapShifts) ![]const u8 {
+ var shifts = _shifts;
+ var start_of_run: usize = 0;
+ var current: usize = 0;
+ var generated = LineColumnOffset{};
+ var prev_shift_column_delta: i32 = 0;
+ var j = Joiner{};
+
+ j.push(this.prefix.items);
+ const mappings = this.mappings.items;
+
+ while (current < mappings.len) {
+ if (mappings[current] == ';') {
+ generated.lines += 1;
+ generated.columns = 0;
+ prev_shift_column_delta = 0;
+ current += 1;
+ continue;
+ }
+
+ var potential_end_of_run = current;
+
+ var decode_result = decodeVLQ(mappings, current);
+ generated.columns += decode_result.value;
+ current = decode_result.start;
+
+ var potential_start_of_run = current;
+
+ current = decodeVLQ(mappings, current).start;
+ current = decodeVLQ(mappings, current).start;
+ current = decodeVLQ(mappings, current).start;
+
+ if (current < mappings.len) {
+ var c = mappings[current];
+ if (c != ',' and c != ';') {
+ current = decodeVLQ(mappings, current).start;
+ }
+ }
+
+ if (current < mappings.len and mappings[current] == ',') {
+ current += 1;
+ }
+
+ var did_cross_boundary = false;
+ if (shifts.len > 1 and shifts[1].before.comesBefore(generated)) {
+ shifts = shifts[1..];
+ did_cross_boundary = true;
+ }
+
+ if (!did_cross_boundary) {
+ continue;
+ }
+
+ var shift = shifts[0];
+ if (shift.after.lines != generated.lines) {
+ continue;
+ }
+
+ j.push(mappings[start_of_run..potential_end_of_run]);
+
+ std.debug.assert(shift.before.lines == shift.after.lines);
+
+ var shift_column_delta = shift.after.columns - shift.before.columns;
+ const encode = encodeVLQ(decode_result.value + shift_column_delta - prev_shift_column_delta);
+ j.push(encode.bytes[0..encode.len]);
+ prev_shift_column_delta = shift_column_delta;
+
+ start_of_run = potential_start_of_run;
+ }
+
+ j.push(mappings[start_of_run..]);
+ j.push(this.suffix.items);
+
+ return try j.done(allocator);
+ }
};
// -- comment from esbuild --
@@ -407,16 +518,16 @@ pub const SourceMapPieces = struct {
// After all chunks are computed, they are joined together in a second pass.
// This rewrites the first mapping in each chunk to be relative to the end
// state of the previous chunk.
-pub fn appendSourceMapChunk(j: *Joiner, prev_end_state_: SourceMapState, start_state_: SourceMapState, source_map_: MutableString) !void {
+pub fn appendSourceMapChunk(j: *Joiner, allocator: std.mem.Allocator, prev_end_state_: SourceMapState, start_state_: SourceMapState, source_map_: bun.string) !void {
var prev_end_state = prev_end_state_;
var start_state = start_state_;
// Handle line breaks in between this mapping and the previous one
if (start_state.generated_line > 0) {
- j.append(try strings.repeatingAlloc(source_map_.allocator, @intCast(usize, start_state.generated_line), ';'), 0, source_map_.allocator);
+ j.append(try strings.repeatingAlloc(allocator, @intCast(usize, start_state.generated_line), ';'), 0, allocator);
prev_end_state.generated_column = 0;
}
- var source_map = source_map_.list.items;
+ var source_map = source_map_;
if (strings.indexOfNotChar(source_map, ';')) |semicolons| {
j.append(source_map[0..semicolons], 0, null);
source_map = source_map[semicolons..];
@@ -448,13 +559,13 @@ pub fn appendSourceMapChunk(j: *Joiner, prev_end_state_: SourceMapState, start_s
start_state.original_column += original_column_.value;
j.append(
- appendMappingToBuffer(MutableString.initEmpty(source_map.allocator), j.lastByte(), prev_end_state, start_state).list.items,
+ appendMappingToBuffer(MutableString.initEmpty(allocator), j.lastByte(), prev_end_state, start_state).list.items,
0,
- source_map.allocator,
+ allocator,
);
// Then append everything after that without modification.
- j.append(source_map_.list.items, @truncate(u32, @ptrToInt(source_map.ptr) - @ptrToInt(source_map_.list.items.ptr)), source_map_.allocator);
+ j.push(source_map);
}
const vlq_lookup_table: [256]VLQ = brk: {
@@ -638,18 +749,14 @@ pub const LineOffsetTable = struct {
pub const List = std.MultiArrayList(LineOffsetTable);
- pub fn findLine(list: List, loc: Logger.Loc) i32 {
- const byte_offsets_to_start_of_line = list.items(.byte_offset_to_start_of_line);
- var original_line: u32 = 0;
- if (loc.start <= -1) {
- return 0;
- }
-
- const loc_start = @intCast(u32, loc.start);
+ pub fn findLine(byte_offsets_to_start_of_line: []const u32, loc: Logger.Loc) i32 {
+ std.debug.assert(loc.start > -1); // checked by caller
+ var original_line: usize = 0;
+ const loc_start = @intCast(usize, loc.start);
{
- var count = @truncate(u32, byte_offsets_to_start_of_line.len);
- var i: u32 = 0;
+ var count = @truncate(usize, byte_offsets_to_start_of_line.len);
+ var i: usize = 0;
while (count > 0) {
const step = count / 2;
i = original_line + step;
@@ -1015,6 +1122,8 @@ pub const Chunk = struct {
prev_loc: Logger.Loc = Logger.Loc.Empty,
has_prev_state: bool = false,
+ line_offset_table_byte_offset_list: []const u32 = &.{},
+
// This is a workaround for a bug in the popular "source-map" library:
// https://github.com/mozilla/source-map/issues/261. The library will
// sometimes return null when querying a source map unless every line
@@ -1031,7 +1140,7 @@ pub const Chunk = struct {
pub const SourceMapper = SourceMapFormat(SourceMapFormatType);
- pub fn generateChunk(b: *ThisBuilder, output: []const u8) Chunk {
+ pub noinline fn generateChunk(b: *ThisBuilder, output: []const u8) Chunk {
b.updateGeneratedLineAndColumn(output);
if (b.prepend_count) {
b.source_map.getBuffer().list.items[0..8].* = @bitCast([8]u8, b.source_map.getBuffer().list.items.len);
@@ -1042,7 +1151,7 @@ pub const Chunk = struct {
.mappings_count = b.source_map.getCount(),
.end_state = b.prev_state,
.final_generated_column = b.generated_column,
- .should_ignore = !b.source_map.shouldIgnore(),
+ .should_ignore = b.source_map.shouldIgnore(),
};
}
@@ -1144,7 +1253,7 @@ pub const Chunk = struct {
b.prev_loc = loc;
const list = b.line_offset_tables;
- const original_line = LineOffsetTable.findLine(list, loc);
+ const original_line = LineOffsetTable.findLine(b.line_offset_table_byte_offset_list, loc);
const line = list.get(@intCast(usize, @max(original_line, 0)));
// Use the line to compute the column
@@ -1183,3 +1292,29 @@ pub const Chunk = struct {
pub const Builder = NewBuilder(VLQSourceMap);
};
+
+/// https://sentry.engineering/blog/the-case-for-debug-ids
+/// https://github.com/mitsuhiko/source-map-rfc/blob/proposals/debug-id/proposals/debug-id.md
+/// https://github.com/source-map/source-map-rfc/pull/20
+pub const DebugIDFormatter = struct {
+ id: u64 = 0,
+
+ pub fn format(self: DebugIDFormatter, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
+ // The RFC asks for a UUID
+ // We are not generating a UUID, our hash is a 64-bit integer
+ // So we just print it like a UUID
+ // pretty sure this number is always 16 bytes
+ const formatter = bun.fmt.hexIntUpper(self.id);
+ const expected_length = "85314830023F4CF1A267535F4E37BB17".len;
+ var buf: [expected_length]u8 = undefined;
+
+ const wrote = std.fmt.bufPrint(&buf, "{}", .{formatter}) catch unreachable;
+ @memset(
+ buf[wrote.len..].ptr,
+ // fill the remaining with B
+ 'B',
+ buf.len - wrote.len,
+ );
+ try writer.writeAll(&buf);
+ }
+};