diff options
author | 2022-03-08 04:46:25 -0800 | |
---|---|---|
committer | 2022-03-08 04:46:25 -0800 | |
commit | 880f6a17b84ad4ca09d075d22fdccb2256411435 (patch) | |
tree | 365d78f89378b00b31775d61e82ea52561a89ece /src/javascript | |
parent | 04568452567995e3aa42535ca356d83e60ed030f (diff) | |
download | bun-880f6a17b84ad4ca09d075d22fdccb2256411435.tar.gz bun-880f6a17b84ad4ca09d075d22fdccb2256411435.tar.zst bun-880f6a17b84ad4ca09d075d22fdccb2256411435.zip |
[bun.js] WIP sourcemap support
Diffstat (limited to 'src/javascript')
-rw-r--r-- | src/javascript/jsc/javascript.zig | 253 |
1 files changed, 163 insertions, 90 deletions
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 828bad39f..852c53562 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -19,7 +19,7 @@ const NetworkThread = @import("http").NetworkThread; pub fn zigCast(comptime Destination: type, value: anytype) *Destination { return @ptrCast(*Destination, @alignCast(@alignOf(*Destination), value)); } - +const IdentityContext = @import("../../identity_context.zig").IdentityContext; const Fs = @import("../../fs.zig"); const Resolver = @import("../../resolver/resolver.zig"); const ast = @import("../../import_record.zig"); @@ -1701,9 +1701,73 @@ pub const Task = TaggedPointerUnion(.{ // TimeoutTasklet, }); +const SourceMap = @import("../../sourcemap/sourcemap.zig"); + +pub const SavedSourceMap = struct { + // For bun.js, we store the number of mappings and how many bytes the final list is at the beginning of the array + // The first 8 bytes are the length of the array + // The second 8 bytes are the number of mappings + pub const SavedMappings = struct { + data: [*]u8, + + pub fn vlq(this: SavedMappings) []u8 { + return this.data[16..this.len()]; + } + + pub inline fn len(this: SavedMappings) usize { + return @bitCast(u64, this.data[0..8]); + } + + pub fn deinit(this: SavedMappings) []u8 { + default_allocator.free(this.data[0..this.len()]); + } + }; + + pub const Value = TaggedPointerUnion(.{ SourceMap, SavedMappings }); + pub const HashTable = std.HashMap(u64, *anyopaque, IdentityContext(u64), 80); + + map: HashTable, + + pub fn putMappings(this: *SavedSourceMap, source: logger.Source, mappings: MutableString) !void { + var entry = try this.map.getOrPut(std.hash.Wyhash.hash(0, source.path.text)); + if (entry.found_existing) { + var value = Value.from(entry.value_ptr.*); + if (value.get(SourceMap)) |source_map_| { + var source_map: *SourceMap = source_map_; + source_map.deinit(); + } else if (value.get(SavedMappings)) |saved_mappings| { + var saved = SavedMappings{ .data = @ptrCast([*]u8, saved_mappings) }; + + saved.deinit(); + } + } + + entry.value_ptr.* = Value.init(@ptrCast(*SavedMappings, mappings.list.items.ptr)).ptr(); + } + + pub fn get(this: *SavedSourceMap, allocator: std.mem.Allocator, path: string) ?*SourceMap { + var mapping = this.map.getEntry(std.hash.Wyhash.hash(0, path)) orelse return null; + switch (Value.from(mapping.value_ptr.*).tag()) { + SourceMap => { + return Value.from(mapping).as(SourceMap); + }, + SavedMappings => { + _ = allocator; + return null; + }, + else => return null, + } + } +}; + // If you read JavascriptCore/API/JSVirtualMachine.mm - https://github.com/WebKit/WebKit/blob/acff93fb303baa670c055cb24c2bad08691a01a0/Source/JavaScriptCore/API/JSVirtualMachine.mm#L101 // We can see that it's sort of like std.mem.Allocator but for JSGlobalContextRef, to support Automatic Reference Counting // Its unavailable on Linux + +// JavaScriptCore expects 1 VM per thread +// However, there can be many JSGlobalObject +// We currently assume a 1:1 correspondence between the two. +// This is technically innacurate pub const VirtualMachine = struct { global: *JSGlobalObject, allocator: std.mem.Allocator, @@ -1745,8 +1809,7 @@ pub const VirtualMachine = struct { regular_event_loop: EventLoop = EventLoop{}, event_loop: *EventLoop = undefined, - is_set_timeout_enabled: bool = false, - is_set_interval_enabled: bool = false, + source_mappings: SavedSourceMap = undefined, pub inline fn eventLoop(this: *VirtualMachine) *EventLoop { return this.event_loop; @@ -1940,7 +2003,7 @@ pub const VirtualMachine = struct { .flush_list = std.ArrayList(string).init(allocator), .blobs = if (_args.serve orelse false) try Blob.Group.init(allocator) else null, .origin = bundler.options.origin, - + .source_mappings = SavedSourceMap{ .map = SavedSourceMap.HashTable.init(allocator) }, .macros = MacroMap.init(allocator), .macro_entry_points = @TypeOf(VirtualMachine.vm.macro_entry_points).init(allocator), .origin_timer = std.time.Timer.start() catch @panic("Please don't mess with timers."), @@ -1973,12 +2036,11 @@ pub const VirtualMachine = struct { VirtualMachine.vm.regular_event_loop.global = VirtualMachine.vm.global; VirtualMachine.vm_loaded = true; - if (!source_code_printer_loaded) { + if (source_code_printer == null) { var writer = try js_printer.BufferWriter.init(allocator); - source_code_printer = js_printer.BufferPrinter.init(writer); - source_code_printer.ctx.append_null_byte = false; - - source_code_printer_loaded = true; + source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable; + source_code_printer.?.* = js_printer.BufferPrinter.init(writer); + source_code_printer.?.ctx.append_null_byte = false; } return VirtualMachine.vm; @@ -1989,8 +2051,7 @@ pub const VirtualMachine = struct { // } - threadlocal var source_code_printer: js_printer.BufferPrinter = undefined; - threadlocal var source_code_printer_loaded: bool = false; + threadlocal var source_code_printer: ?*js_printer.BufferPrinter = null; pub fn preflush(this: *VirtualMachine) void { // We flush on the next tick so that if there were any errors you can still see them @@ -2014,15 +2075,16 @@ pub const VirtualMachine = struct { log: *logger.Log, ) !ResolvedSource { std.debug.assert(VirtualMachine.vm_loaded); + var jsc_vm = vm; - if (vm.node_modules != null and strings.eqlComptime(_specifier, bun_file_import_path)) { + if (jsc_vm.node_modules != null and strings.eqlComptime(_specifier, bun_file_import_path)) { // We kind of need an abstraction around this. // Basically we should subclass JSC::SourceCode with: // - hash // - file descriptor for source input // - file path + file descriptor for bytecode caching // - separate bundles for server build vs browser build OR at least separate sections - const code = try vm.node_modules.?.readCodeAsStringSlow(vm.allocator); + const code = try jsc_vm.node_modules.?.readCodeAsStringSlow(jsc_vm.allocator); return ResolvedSource{ .allocator = null, @@ -2031,7 +2093,7 @@ pub const VirtualMachine = struct { .source_url = ZigString.init(bun_file_import_path[1..]), .hash = 0, // TODO }; - } else if (vm.node_modules == null and strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) { + } else if (jsc_vm.node_modules == null and strings.eqlComptime(_specifier, Runtime.Runtime.Imports.Name)) { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(Runtime.Runtime.sourceContent(false)), @@ -2043,17 +2105,17 @@ pub const VirtualMachine = struct { // so it consistently handles bundled imports // we can't take the shortcut of just directly importing the file, sadly. } else if (strings.eqlComptime(_specifier, main_file_name)) { - defer vm.transpiled_count += 1; + defer jsc_vm.transpiled_count += 1; - var bundler = &vm.bundler; - var old = vm.bundler.log; - vm.bundler.log = log; - vm.bundler.linker.log = log; - vm.bundler.resolver.log = log; + var bundler = &jsc_vm.bundler; + var old = jsc_vm.bundler.log; + jsc_vm.bundler.log = log; + jsc_vm.bundler.linker.log = log; + jsc_vm.bundler.resolver.log = log; defer { - vm.bundler.log = old; - vm.bundler.linker.log = old; - vm.bundler.resolver.log = old; + jsc_vm.bundler.log = old; + jsc_vm.bundler.linker.log = old; + jsc_vm.bundler.resolver.log = old; } var jsx = bundler.options.jsx; @@ -2066,30 +2128,33 @@ pub const VirtualMachine = struct { opts.features.react_fast_refresh = false; opts.filepath_hash_for_hmr = 0; opts.warn_about_unbundled_modules = false; - opts.macro_context = &vm.bundler.macro_context.?; - const main_ast = (bundler.resolver.caches.js.parse(vm.allocator, opts, bundler.options.define, bundler.log, &vm.entry_point.source) catch null) orelse { + opts.macro_context = &jsc_vm.bundler.macro_context.?; + const main_ast = (bundler.resolver.caches.js.parse(jsc_vm.allocator, opts, bundler.options.define, bundler.log, &jsc_vm.entry_point.source) catch null) orelse { return error.ParseError; }; - var parse_result = ParseResult{ .source = vm.entry_point.source, .ast = main_ast, .loader = .js, .input_fd = null }; + var parse_result = ParseResult{ .source = jsc_vm.entry_point.source, .ast = main_ast, .loader = .js, .input_fd = null }; var file_path = Fs.Path.init(bundler.fs.top_level_dir); file_path.name.dir = bundler.fs.top_level_dir; file_path.name.base = "bun:main"; try bundler.linker.link( file_path, &parse_result, - vm.origin, + jsc_vm.origin, .absolute_path, false, ); - - source_code_printer.ctx.reset(); - - var written = try vm.bundler.print( - parse_result, - @TypeOf(&source_code_printer), - &source_code_printer, - .esm_ascii, - ); + var printer = source_code_printer.?.*; + var written: usize = undefined; + printer.ctx.reset(); + { + defer source_code_printer.?.* = printer; + written = try jsc_vm.bundler.print( + parse_result, + @TypeOf(&printer), + &printer, + .esm_ascii, + ); + } if (written == 0) { return error.PrintingErrorWriteFailed; @@ -2097,7 +2162,7 @@ pub const VirtualMachine = struct { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(vm.allocator.dupe(u8, source_code_printer.ctx.written) catch unreachable), + .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable), .specifier = ZigString.init(std.mem.span(main_file_name)), .source_url = ZigString.init(std.mem.span(main_file_name)), .hash = 0, @@ -2105,7 +2170,7 @@ pub const VirtualMachine = struct { } else if (_specifier.len > js_ast.Macro.namespaceWithColon.len and strings.eqlComptimeIgnoreLen(_specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon)) { - if (vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(_specifier))) |entry| { + if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(_specifier))) |entry| { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(entry.source.contents), @@ -2137,20 +2202,20 @@ pub const VirtualMachine = struct { std.debug.assert(std.fs.path.isAbsolute(specifier)); // if this crashes, it means the resolver was skipped. const path = Fs.Path.init(specifier); - const loader = vm.bundler.options.loaders.get(path.name.ext) orelse .file; + const loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse .file; switch (loader) { .js, .jsx, .ts, .tsx, .json, .toml => { - vm.transpiled_count += 1; - vm.bundler.resetStore(); + jsc_vm.transpiled_count += 1; + jsc_vm.bundler.resetStore(); const hash = http.Watcher.getHash(path.text); - var allocator = if (vm.has_loaded) vm.arena.allocator() else vm.allocator; + var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; var fd: ?StoredFileDescriptorType = null; var package_json: ?*PackageJSON = null; - if (vm.watcher) |watcher| { + if (jsc_vm.watcher) |watcher| { if (watcher.indexOf(hash)) |index| { const _fd = watcher.watchlist.items(.fd)[index]; fd = if (_fd > 0) _fd else null; @@ -2158,24 +2223,24 @@ pub const VirtualMachine = struct { } } - var old = vm.bundler.log; - vm.bundler.log = log; - vm.bundler.linker.log = log; - vm.bundler.resolver.log = log; + var old = jsc_vm.bundler.log; + jsc_vm.bundler.log = log; + jsc_vm.bundler.linker.log = log; + jsc_vm.bundler.resolver.log = log; defer { - vm.bundler.log = old; - vm.bundler.linker.log = old; - vm.bundler.resolver.log = old; + jsc_vm.bundler.log = old; + jsc_vm.bundler.linker.log = old; + jsc_vm.bundler.resolver.log = old; } // this should be a cheap lookup because 24 bytes == 8 * 3 so it's read 3 machine words const is_node_override = specifier.len > "/bun-vfs/node_modules/".len and strings.eqlComptimeIgnoreLen(specifier[0.."/bun-vfs/node_modules/".len], "/bun-vfs/node_modules/"); - const macro_remappings = if (vm.macro_mode or !vm.has_any_macro_remappings or is_node_override) + const macro_remappings = if (jsc_vm.macro_mode or !jsc_vm.has_any_macro_remappings or is_node_override) MacroRemap{} else - vm.bundler.options.macro_remap; + jsc_vm.bundler.options.macro_remap; var fallback_source: logger.Source = undefined; @@ -2187,7 +2252,7 @@ pub const VirtualMachine = struct { .file_descriptor = fd, .file_hash = hash, .macro_remappings = macro_remappings, - .jsx = vm.bundler.options.jsx, + .jsx = jsc_vm.bundler.options.jsx, }; if (is_node_override) { @@ -2198,57 +2263,61 @@ pub const VirtualMachine = struct { } } - var parse_result = vm.bundler.parse( + var parse_result = jsc_vm.bundler.parse( parse_options, null, ) orelse { return error.ParseError; }; - const start_count = vm.bundler.linker.import_counter; + const start_count = jsc_vm.bundler.linker.import_counter; // We _must_ link because: // - node_modules bundle won't be properly - try vm.bundler.linker.link( + try jsc_vm.bundler.linker.link( path, &parse_result, - vm.origin, + jsc_vm.origin, .absolute_path, false, ); - if (!vm.macro_mode) - vm.resolved_count += vm.bundler.linker.import_counter - start_count; - vm.bundler.linker.import_counter = 0; + if (!jsc_vm.macro_mode) + jsc_vm.resolved_count += jsc_vm.bundler.linker.import_counter - start_count; + jsc_vm.bundler.linker.import_counter = 0; - source_code_printer.ctx.reset(); - - var written = try vm.bundler.print( - parse_result, - @TypeOf(&source_code_printer), - &source_code_printer, - .esm_ascii, - ); + var printer = source_code_printer.?.*; + var written: usize = undefined; + printer.ctx.reset(); + { + defer source_code_printer.?.* = printer; + written = try jsc_vm.bundler.print( + parse_result, + @TypeOf(&printer), + &printer, + .esm_ascii, + ); + } if (written == 0) { return error.PrintingErrorWriteFailed; } return ResolvedSource{ - .allocator = if (vm.has_loaded) &vm.allocator else null, - .source_code = ZigString.init(vm.allocator.dupe(u8, source_code_printer.ctx.written) catch unreachable), + .allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null, + .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable), .specifier = ZigString.init(specifier), .source_url = ZigString.init(path.text), .hash = 0, }; }, // .wasm => { - // vm.transpiled_count += 1; + // jsc_vm.transpiled_count += 1; // var fd: ?StoredFileDescriptorType = null; - // var allocator = if (vm.has_loaded) vm.arena.allocator() else vm.allocator; + // var allocator = if (jsc_vm.has_loaded) jsc_vm.arena.allocator() else jsc_vm.allocator; // const hash = http.Watcher.getHash(path.text); - // if (vm.watcher) |watcher| { + // if (jsc_vm.watcher) |watcher| { // if (watcher.indexOf(hash)) |index| { // const _fd = watcher.watchlist.items(.fd)[index]; // fd = if (_fd > 0) _fd else null; @@ -2263,10 +2332,10 @@ pub const VirtualMachine = struct { // .file_descriptor = fd, // .file_hash = hash, // .macro_remappings = MacroRemap{}, - // .jsx = vm.bundler.options.jsx, + // .jsx = jsc_vm.bundler.options.jsx, // }; - // var parse_result = vm.bundler.parse( + // var parse_result = jsc_vm.bundler.parse( // parse_options, // null, // ) orelse { @@ -2274,8 +2343,8 @@ pub const VirtualMachine = struct { // }; // return ResolvedSource{ - // .allocator = if (vm.has_loaded) &vm.allocator else null, - // .source_code = ZigString.init(vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), + // .allocator = if (jsc_vm.has_loaded) &jsc_vm.allocator else null, + // .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, parse_result.source.contents) catch unreachable), // .specifier = ZigString.init(specifier), // .source_url = ZigString.init(path.text), // .hash = 0, @@ -2285,7 +2354,7 @@ pub const VirtualMachine = struct { else => { return ResolvedSource{ .allocator = &vm.allocator, - .source_code = ZigString.init(try strings.quotedAlloc(VirtualMachine.vm.allocator, path.pretty)), + .source_code = ZigString.init(try strings.quotedAlloc(jsc_vm.allocator, path.pretty)), .specifier = ZigString.init(path.text), .source_url = ZigString.init(path.text), .hash = 0, @@ -2300,16 +2369,20 @@ pub const VirtualMachine = struct { fn _resolve(ret: *ResolveFunctionResult, _: *JSGlobalObject, specifier: string, source: string) !void { std.debug.assert(VirtualMachine.vm_loaded); + // macOS threadlocal vars are very slow + // we won't change threads in this function + // so we can copy it here + var jsc_vm = vm; - if (vm.node_modules == null and strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) { + if (jsc_vm.node_modules == null and strings.eqlComptime(std.fs.path.basename(specifier), Runtime.Runtime.Imports.alt_name)) { ret.path = Runtime.Runtime.Imports.Name; return; - } else if (vm.node_modules != null and strings.eqlComptime(specifier, bun_file_import_path)) { + } else if (jsc_vm.node_modules != null and strings.eqlComptime(specifier, bun_file_import_path)) { ret.path = bun_file_import_path; return; } else if (strings.eqlComptime(specifier, main_file_name)) { ret.result = null; - ret.path = vm.entry_point.source.path.text; + ret.path = jsc_vm.entry_point.source.path.text; return; } else if (specifier.len > js_ast.Macro.namespaceWithColon.len and strings.eqlComptimeIgnoreLen(specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon)) { ret.result = null; @@ -2332,25 +2405,25 @@ pub const VirtualMachine = struct { const is_special_source = strings.eqlComptime(source, main_file_name) or js_ast.Macro.isMacroPath(source); - const result = try vm.bundler.resolver.resolve( - if (!is_special_source) Fs.PathName.init(source).dirWithTrailingSlash() else VirtualMachine.vm.bundler.fs.top_level_dir, + const result = try jsc_vm.bundler.resolver.resolve( + if (!is_special_source) Fs.PathName.init(source).dirWithTrailingSlash() else jsc_vm.bundler.fs.top_level_dir, specifier, .stmt, ); - if (!vm.macro_mode) { - vm.has_any_macro_remappings = vm.has_any_macro_remappings or vm.bundler.options.macro_remap.count() > 0; + if (!jsc_vm.macro_mode) { + jsc_vm.has_any_macro_remappings = jsc_vm.has_any_macro_remappings or jsc_vm.bundler.options.macro_remap.count() > 0; } ret.result = result; const result_path = result.pathConst() orelse return error.ModuleNotFound; - vm.resolved_count += 1; + jsc_vm.resolved_count += 1; - if (vm.node_modules != null and !strings.eqlComptime(result_path.namespace, "node") and result.isLikelyNodeModule()) { - const node_modules_bundle = vm.node_modules.?; + if (jsc_vm.node_modules != null and !strings.eqlComptime(result_path.namespace, "node") and result.isLikelyNodeModule()) { + const node_modules_bundle = jsc_vm.node_modules.?; node_module_checker: { const package_json = result.package_json orelse brk: { - if (vm.bundler.resolver.packageJSONForResolvedNodeModule(&result)) |pkg| { + if (jsc_vm.bundler.resolver.packageJSONForResolvedNodeModule(&result)) |pkg| { break :brk pkg; } else { break :node_module_checker; @@ -2374,7 +2447,7 @@ pub const VirtualMachine = struct { std.debug.assert(strings.eql(node_modules_bundle.str(package.name), package_json.name)); } - const package_relative_path = vm.bundler.fs.relative( + const package_relative_path = jsc_vm.bundler.fs.relative( package_json.source.path.name.dirWithTrailingSlash(), result_path.text, ); |