diff --git a/src/bake/DevServer/ErrorReportRequest.zig b/src/bake/DevServer/ErrorReportRequest.zig index 2d0241f0d5..8cca4662e3 100644 --- a/src/bake/DevServer/ErrorReportRequest.zig +++ b/src/bake/DevServer/ErrorReportRequest.zig @@ -69,8 +69,8 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) ! .function_name = .init(function_name), .source_url = .init(file_name), .position = if (line > 0) .{ - .line = .fromOneBased(line + 1), - .column = .fromOneBased(@max(1, column)), + .line = .fromOneBased(line), + .column = if (column < 1) .invalid else .fromOneBased(column), .line_start_byte = 0, } else .{ .line = .invalid, @@ -147,10 +147,10 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) ! // Remap the frame const remapped = result.mappings.find( - frame.position.line.oneBased(), - frame.position.column.zeroBased(), + frame.position.line, + frame.position.column, ); - if (remapped) |remapped_position| { + if (remapped) |*remapped_position| { frame.position = .{ .line = .fromZeroBased(remapped_position.originalLine()), .column = .fromZeroBased(remapped_position.originalColumn()), diff --git a/src/bake/DevServer/SourceMapStore.zig b/src/bake/DevServer/SourceMapStore.zig index bd109a00bd..7a230886bb 100644 --- a/src/bake/DevServer/SourceMapStore.zig +++ b/src/bake/DevServer/SourceMapStore.zig @@ -207,8 +207,9 @@ pub const Entry = struct { .original_column = 0, }; - // +2 because the magic fairy in my dreams said it would align the source maps. - var lines_between: u32 = runtime.line_count + 2; + // The runtime.line_count counts newlines (e.g., 2941 for a 2942-line file). + // The runtime ends at line 2942 with })({ so modules start after that. + var lines_between: u32 = runtime.line_count; // Join all of the mappings together. for (0..map_files.len) |i| switch (map_files.get(i)) { diff --git a/src/bake/client/overlay.ts b/src/bake/client/overlay.ts index a87708ec2f..2ef7362a9e 100644 --- a/src/bake/client/overlay.ts +++ b/src/bake/client/overlay.ts @@ -251,8 +251,8 @@ export async function onRuntimeError(err: any, fatal = false, async = false) { writer.stringWithLength(browserUrl); writer.u32(parsed.length); for (const frame of parsed) { - writer.u32(frame.line ?? 0); - writer.u32(frame.col ?? 0); + writer.i32(frame.line ?? 0); + writer.i32(frame.col ?? 0); writer.stringWithLength(frame.fn ?? ""); const fileName = frame.file; if (fileName) { diff --git a/src/bun.js/SavedSourceMap.zig b/src/bun.js/SavedSourceMap.zig index a350f46436..3d303f7aab 100644 --- a/src/bun.js/SavedSourceMap.zig +++ b/src/bun.js/SavedSourceMap.zig @@ -298,13 +298,13 @@ pub fn get(this: *SavedSourceMap, path: string) ?*ParsedSourceMap { pub fn resolveMapping( this: *SavedSourceMap, path: []const u8, - line: i32, - column: i32, + line: bun.Ordinal, + column: bun.Ordinal, source_handling: SourceMap.SourceContentHandling, ) ?SourceMap.Mapping.Lookup { const parse = this.getWithContent(path, switch (source_handling) { .no_source_contents => .mappings_only, - .source_contents => .{ .all = .{ .line = line, .column = column } }, + .source_contents => .{ .all = .{ .line = @max(line.zeroBased(), 0), .column = @max(column.zeroBased(), 0) } }, }); const map = parse.map orelse return null; diff --git a/src/bun.js/VirtualMachine.zig b/src/bun.js/VirtualMachine.zig index a859c208f2..10591ec0e3 100644 --- a/src/bun.js/VirtualMachine.zig +++ b/src/bun.js/VirtualMachine.zig @@ -2606,8 +2606,8 @@ pub fn remapStackFramePositions(this: *VirtualMachine, frames: [*]jsc.ZigStackFr if (this.resolveSourceMapping( sourceURL.slice(), - @max(frame.position.line.zeroBased(), 0), - @max(frame.position.column.zeroBased(), 0), + frame.position.line, + frame.position.column, .no_source_contents, )) |lookup| { const source_map = lookup.source_map; @@ -2745,8 +2745,8 @@ pub fn remapZigException( else this.resolveSourceMapping( top_source_url.slice(), - @max(top.position.line.zeroBased(), 0), - @max(top.position.column.zeroBased(), 0), + top.position.line, + top.position.column, .source_contents, ); @@ -2834,8 +2834,8 @@ pub fn remapZigException( defer source_url.deinit(); if (this.resolveSourceMapping( source_url.slice(), - @max(frame.position.line.zeroBased(), 0), - @max(frame.position.column.zeroBased(), 0), + frame.position.line, + frame.position.column, .no_source_contents, )) |lookup| { defer if (lookup.source_map) |map| map.deref(); @@ -3440,8 +3440,8 @@ pub noinline fn printGithubAnnotation(exception: *ZigException) void { pub fn resolveSourceMapping( this: *VirtualMachine, path: []const u8, - line: i32, - column: i32, + line: Ordinal, + column: Ordinal, source_handling: SourceMap.SourceContentHandling, ) ?SourceMap.Mapping.Lookup { return this.source_mappings.resolveMapping(path, line, column, source_handling) orelse { diff --git a/src/bun.zig b/src/bun.zig index 9091ba0f8a..312c9f474c 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -3391,36 +3391,36 @@ pub fn OrdinalT(comptime Int: type) type { start = 0, _, - pub fn fromZeroBased(int: Int) @This() { + pub inline fn fromZeroBased(int: Int) @This() { assert(int >= 0); assert(int != std.math.maxInt(Int)); return @enumFromInt(int); } - pub fn fromOneBased(int: Int) @This() { + pub inline fn fromOneBased(int: Int) @This() { assert(int > 0); return @enumFromInt(int - 1); } - pub fn zeroBased(ord: @This()) Int { + pub inline fn zeroBased(ord: @This()) Int { return @intFromEnum(ord); } - pub fn oneBased(ord: @This()) Int { + pub inline fn oneBased(ord: @This()) Int { return @intFromEnum(ord) + 1; } /// Add two ordinal numbers together. Both are converted to zero-based before addition. - pub fn add(ord: @This(), b: @This()) @This() { + pub inline fn add(ord: @This(), b: @This()) @This() { return fromZeroBased(ord.zeroBased() + b.zeroBased()); } /// Add a scalar value to an ordinal number - pub fn addScalar(ord: @This(), inc: Int) @This() { + pub inline fn addScalar(ord: @This(), inc: Int) @This() { return fromZeroBased(ord.zeroBased() + inc); } - pub fn isValid(ord: @This()) bool { + pub inline fn isValid(ord: @This()) bool { return ord.zeroBased() >= 0; } }; diff --git a/src/sourcemap/CodeCoverage.zig b/src/sourcemap/CodeCoverage.zig index 58923ba0f8..2c4a13b657 100644 --- a/src/sourcemap/CodeCoverage.zig +++ b/src/sourcemap/CodeCoverage.zig @@ -554,7 +554,7 @@ pub const ByteRangeMapping = struct { } const column_position = byte_offset -| line_start_byte_offset; - if (parsed_mapping.mappings.find(@intCast(new_line_index), @intCast(column_position))) |*point| { + if (parsed_mapping.mappings.find(.fromZeroBased(@intCast(new_line_index)), .fromZeroBased(@intCast(column_position)))) |*point| { if (point.original.lines.zeroBased() < 0) continue; const line: u32 = @as(u32, @intCast(point.original.lines.zeroBased())); @@ -598,7 +598,7 @@ pub const ByteRangeMapping = struct { const column_position = byte_offset -| line_start_byte_offset; - if (parsed_mapping.mappings.find(@intCast(new_line_index), @intCast(column_position))) |point| { + if (parsed_mapping.mappings.find(.fromZeroBased(@intCast(new_line_index)), .fromZeroBased(@intCast(column_position)))) |point| { if (point.original.lines.zeroBased() < 0) continue; const line: u32 = @as(u32, @intCast(point.original.lines.zeroBased())); diff --git a/src/sourcemap/JSSourceMap.zig b/src/sourcemap/JSSourceMap.zig index 3222d56ffd..0414359019 100644 --- a/src/sourcemap/JSSourceMap.zig +++ b/src/sourcemap/JSSourceMap.zig @@ -245,7 +245,7 @@ extern fn Bun__createNodeModuleSourceMapEntryObject( pub fn findOrigin(this: *JSSourceMap, globalObject: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { const line_number, const column_number = try getLineColumn(globalObject, callFrame); - const mapping = this.sourcemap.mappings.find(line_number, column_number) orelse return jsc.JSValue.createEmptyObject(globalObject, 0); + const mapping = this.sourcemap.mappings.find(.fromZeroBased(line_number), .fromZeroBased(column_number)) orelse return jsc.JSValue.createEmptyObject(globalObject, 0); const name = try mappingNameToJS(this, globalObject, &mapping); const source = try sourceNameToJS(this, globalObject, &mapping); return Bun__createNodeModuleSourceMapOriginObject( @@ -260,7 +260,7 @@ pub fn findOrigin(this: *JSSourceMap, globalObject: *JSGlobalObject, callFrame: pub fn findEntry(this: *JSSourceMap, globalObject: *JSGlobalObject, callFrame: *CallFrame) bun.JSError!JSValue { const line_number, const column_number = try getLineColumn(globalObject, callFrame); - const mapping = this.sourcemap.mappings.find(line_number, column_number) orelse return jsc.JSValue.createEmptyObject(globalObject, 0); + const mapping = this.sourcemap.mappings.find(.fromZeroBased(line_number), .fromZeroBased(column_number)) orelse return jsc.JSValue.createEmptyObject(globalObject, 0); const name = try mappingNameToJS(this, globalObject, &mapping); const source = try sourceNameToJS(this, globalObject, &mapping); diff --git a/src/sourcemap/sourcemap.zig b/src/sourcemap/sourcemap.zig index 7a75934db7..700d1a139e 100644 --- a/src/sourcemap/sourcemap.zig +++ b/src/sourcemap/sourcemap.zig @@ -218,7 +218,7 @@ pub fn parseJSON( const mapping, const source_index = switch (hint) { .source_only => |index| .{ null, index }, .all => |loc| brk: { - const mapping = map.?.mappings.find(loc.line, loc.column) orelse + const mapping = map.?.mappings.find(.fromZeroBased(loc.line), .fromZeroBased(loc.column)) orelse break :brk .{ null, null }; break :brk .{ mapping, std.math.cast(u32, mapping.source_index) }; }, @@ -315,14 +315,14 @@ pub const Mapping = struct { this.impl = .{ .with_names = with_names }; } - fn findIndexFromGenerated(line_column_offsets: []const LineColumnOffset, line: i32, column: i32) ?usize { + fn findIndexFromGenerated(line_column_offsets: []const LineColumnOffset, line: bun.Ordinal, column: bun.Ordinal) ?usize { var count = line_column_offsets.len; var index: usize = 0; while (count > 0) { const step = count / 2; const i: usize = index + step; const mapping = line_column_offsets[i]; - if (mapping.lines.zeroBased() < line or (mapping.lines.zeroBased() == line and mapping.columns.zeroBased() <= column)) { + if (mapping.lines.zeroBased() < line.zeroBased() or (mapping.lines.zeroBased() == line.zeroBased() and mapping.columns.zeroBased() <= column.zeroBased())) { index = i + 1; count -|= step + 1; } else { @@ -331,7 +331,7 @@ pub const Mapping = struct { } if (index > 0) { - if (line_column_offsets[index - 1].lines.zeroBased() == line) { + if (line_column_offsets[index - 1].lines.zeroBased() == line.zeroBased()) { return index - 1; } } @@ -339,7 +339,7 @@ pub const Mapping = struct { return null; } - pub fn findIndex(this: *const List, line: i32, column: i32) ?usize { + pub fn findIndex(this: *const List, line: bun.Ordinal, column: bun.Ordinal) ?usize { switch (this.impl) { inline else => |*list| { if (findIndexFromGenerated(list.items(.generated), line, column)) |i| { @@ -383,7 +383,7 @@ pub const Mapping = struct { } } - pub fn find(this: *const List, line: i32, column: i32) ?Mapping { + pub fn find(this: *const List, line: bun.Ordinal, column: bun.Ordinal) ?Mapping { switch (this.impl) { inline else => |*list, tag| { if (findIndexFromGenerated(list.items(.generated), line, column)) |i| { @@ -1356,8 +1356,8 @@ pub const SourceContent = struct { pub fn find( this: *const SourceMap, - line: i32, - column: i32, + line: bun.Ordinal, + column: bun.Ordinal, ) ?Mapping { return this.mapping.find(line, column); } diff --git a/test/bake/dev/sourcemap.test.ts b/test/bake/dev/sourcemap.test.ts index 9e94d8eef4..2e1aac1408 100644 --- a/test/bake/dev/sourcemap.test.ts +++ b/test/bake/dev/sourcemap.test.ts @@ -121,7 +121,12 @@ function indexOfLineColumn(text: string, search: string) { } function charOffsetToLineColumn(text: string, offset: number) { - let line = 1; + // sourcemap lines are 0-based. + // > If present, the **zero-based** starting line in the original source. This + // > field contains a base64 VLQ relative to the previous occurrence of this + // > field, unless it is the first occurrence of this field, in which case the + // > whole value is represented. Shall be present if there is a source field. + let line = 0; let i = 0; let prevI = 0; while (i < offset) { @@ -133,5 +138,5 @@ function charOffsetToLineColumn(text: string, offset: number) { i = nextIndex + 1; line++; } - return { line: 1 + line, column: offset - prevI }; + return { line: line, column: offset - prevI }; }