diff options
Diffstat (limited to 'src/javascript/jsc/javascript.zig')
-rw-r--r-- | src/javascript/jsc/javascript.zig | 497 |
1 files changed, 302 insertions, 195 deletions
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 9f9b21044..7ff4070e5 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -149,8 +149,10 @@ pub const VirtualMachine = struct { existing_bundle: ?*NodeModuleBundle, _log: ?*logger.Log, ) !*VirtualMachine { - var group = js.JSContextGroupCreate(); - var ctx = js.JSGlobalContextCreateInGroup(group, null); + var group = js.JSContextGroupRetain(js.JSContextGroupCreate()); + + var ctx = js.JSGlobalContextRetain(js.JSGlobalContextCreateInGroup(group, null)); + var log: *logger.Log = undefined; if (_log) |__log| { log = __log; @@ -168,7 +170,7 @@ pub const VirtualMachine = struct { try configureTransformOptionsForSpeedy(allocator, _args), existing_bundle, ), - .node_module_list = undefined, + .node_module_list = try allocator.create(Module.NodeModuleList), .log = log, .group = group, .root = ctx, @@ -187,7 +189,7 @@ pub const VirtualMachine = struct { Properties.init(); if (vm.bundler.options.node_modules_bundle) |bundle| { vm.node_modules = bundle; - vm.node_module_list = try Module.NodeModuleList.init(vm, bundle); + try Module.NodeModuleList.create(vm, bundle, vm.node_module_list.?); } return vm; @@ -398,6 +400,8 @@ pub const Module = struct { vm: *VirtualMachine, require_func: js.JSObjectRef = null, + loaded: bool = false, + exports_function: js.JSValueRef = null, pub var module_class: js.JSClassRef = undefined; pub var module_global_class: js.JSClassRef = undefined; @@ -407,9 +411,10 @@ pub const Module = struct { pub const NodeModuleList = struct { tempbuf: []u8, property_names: [*]u8, + module_property_map: []u64, static_functions: [1]js.JSStaticFunction, property_getters: []js.JSObjectRef, - module_property_map: ModuleIDMap, + node_module_global_class: js.JSClassRef, node_module_global_class_def: js.JSClassDefinition, vm: *VirtualMachine, @@ -419,18 +424,31 @@ pub const Module = struct { require_cache: []?*Module, - pub fn loadBundledModuleById(node_module_list: *NodeModuleList, id: u32) !*Module { + exports_function_call: js.JSObjectRef = null, + + const RequireBundleClassName = "requireFromBundle"; + var require_bundle_class_def: js.JSClassDefinition = undefined; + var require_bundle_class_ref: js.JSClassRef = undefined; + var require_bundle_class_loaded = false; + + pub fn loadBundledModuleById(node_module_list: *NodeModuleList, id: u32, call_ctx: js.JSContextRef) !*Module { if (node_module_list.require_cache[id]) |mod| { return mod; } - var module = try Module.NodeModuleList.Instance.evalBundledModule( + var module = try node_module_list.vm.allocator.create(Module); + node_module_list.require_cache[id] = module; + errdefer node_module_list.vm.allocator.destroy(module); + + try Module.NodeModuleList.Instance.evalBundledModule( + module, node_module_list.vm.allocator, node_module_list.vm, node_module_list, id, + call_ctx, ); - node_module_list.require_cache[id] = module; + return module; } @@ -442,11 +460,13 @@ pub const Module = struct { threadlocal var source_code_buffer_loaded = false; pub fn evalBundledModule( + module: *Module, allocator: *std.mem.Allocator, vm: *VirtualMachine, node_module_list: *NodeModuleList, id: u32, - ) !*Module { + call_ctx: js.JSContextRef, + ) !void { const bundled_module = &vm.node_modules.?.bundle.modules[id]; const total_length = bundled_module.code.length + 1; if (!source_code_buffer_loaded) { @@ -461,32 +481,61 @@ pub const Module = struct { var node_module_file = std.fs.File{ .handle = vm.node_modules.?.fd }; const read = try node_module_file.pread(source_code_buffer.list.items, bundled_module.code.offset); - source_code_buffer.list.items[read] = 0; - var buf = source_code_buffer.list.items[0..read :0]; + var buf = source_code_buffer.list.items[0..read]; const bundled_package = &vm.node_modules.?.bundle.packages[bundled_module.package_id]; // We want linear because we expect it to virtually always be at 0 // However, out of caution we check. + var start_at: usize = std.mem.indexOfPosLinear(u8, buf, 0, "export var $") orelse return error.FailedCorruptNodeModuleMissingExport; start_at += "export var $".len; // export var $fooo = $$m("packageName", "id", (module, exports) => { // ^ - start_at = std.mem.indexOfPosLinear(u8, "\",", start_at, buf) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + start_at = std.mem.indexOfPosLinear( + u8, + buf, + start_at, + "\",", + ) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; start_at += 1; // export var $fooo = $$m("packageName", "id", (module, exports) => { // ^ - start_at = std.mem.indexOfPosLinear(u8, "\",", start_at, buf) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + start_at = std.mem.indexOfPosLinear( + u8, + buf, + start_at, + "\",", + ) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; start_at += 1; - // ((module, exports) => { - buf[start_at] = '('; - - var source_buf = source_code_buffer.list.items[start_at..read :0]; - var source_string = js.JSStringCreateWithUTF8CString(source_buf.ptr); - defer js.JSStringRelease(source_string); - var source_url_buf = try std.fmt.allocPrintZ( + start_at = std.mem.indexOfPosLinear( + u8, + buf, + start_at, + "=>", + ) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + start_at += 2; + // (module, exports) => { + // ^ + start_at = std.mem.indexOfPosLinear( + u8, + buf, + start_at, + "{", + ) orelse return error.FailedCorruptNodeModuleMissingModuleWrapper; + start_at += 1; + // (module, exports) => { + // + // ^ + var curr_buf = buf[start_at..]; + curr_buf = curr_buf[0 .. std.mem.lastIndexOfScalar(u8, curr_buf, ';') orelse return error.FailedCorruptNodeModuleMissingModuleWrapper]; + curr_buf = curr_buf[0 .. std.mem.lastIndexOfScalar(u8, curr_buf, ')') orelse return error.FailedCorruptNodeModuleMissingModuleWrapper]; + curr_buf = curr_buf[0 .. std.mem.lastIndexOfScalar(u8, curr_buf, '}') orelse return error.FailedCorruptNodeModuleMissingModuleWrapper]; + curr_buf.ptr[curr_buf.len] = 0; + var source_buf = curr_buf.ptr[0..curr_buf.len :0]; + var source_url_buf = try std.fmt.allocPrint( allocator, - "node_modules.jsb/{s}/{s}", + "{s}/{s}", .{ vm.node_modules.?.str(bundled_package.name), vm.node_modules.?.str(bundled_module.path), @@ -494,76 +543,19 @@ pub const Module = struct { ); errdefer allocator.free(source_url_buf); - var source_url = js.JSStringCreateWithUTF8CString(source_url_buf); - defer js.JSStringRelease(source_url); var exception: js.JSValueRef = null; - var return_value: js.JSObjectRef = null; - var module: *Module = undefined; - go: { - // Compile the wrapper function - var function = js.JSEvaluateScript( - node_module_list.bundle_ctx, - source_string, - null, - source_url, - 1, - &exception, - ); - if (exception != null) break :go; - if (!js.JSValueIsObject(node_module_list.bundle_ctx, function)) { - return error.ExpectedFunction; - } - - // Don't create the instance / module if the script has a syntax error - module = try allocator.create(Module); - module.* = Module{ - .path = Fs.Path.initWithPretty(source_url_buf, source_url_buf), - .ref = undefined, - .vm = vm, - }; - module.ref = js.JSObjectMake(node_module_list.bundle_ctx, Module.module_class, module); - var args = try allocator.alloc(js.JSValueRef, 2); - args[0] = module.ref; - args[1] = module.internalGetExports(); - - // Run the wrapper - _ = js.JSObjectCallAsFunction( - node_module_list.bundle_ctx, - function, - args[1], - 2, - args.ptr, - &exception, - ); - if (exception != null) { - allocator.destroy(module); - allocator.free(source_url_buf); - } - break :go; - } - - if (exception != null) { - var message = js.JSValueToStringCopy(node_module_list.bundle_ctx, exception.?, null); - defer js.JSStringRelease(message); - var message_str_size = js.JSStringGetMaximumUTF8CStringSize(message); - var message_str_buf = try allocator.alloc(u8, message_str_size); - defer allocator.free(message_str_buf); - var message_str_read = js.JSStringGetUTF8CString(message, message_str_buf.ptr, message_str_size); - defer Output.flush(); - vm.log.addErrorFmt(null, logger.Loc.Empty, allocator, "Error loading \"{s}/{s}\":\n{s}", .{ - vm.node_modules.?.str(bundled_package.name), - vm.node_modules.?.str(bundled_module.path), - message_str_buf[0..message_str_read], - }) catch {}; - Output.prettyErrorln("<r>{s}\n--<r><red>error<r> loading <cyan>\"{s}/{s}\"<r>--", .{ - message_str_buf[0..message_str_read], - vm.node_modules.?.str(bundled_package.name), - vm.node_modules.?.str(bundled_module.path), - }); - return error.FailedException; - } - - return module; + try Module.load( + module, + vm, + allocator, + vm.log, + source_buf, + Fs.Path.initWithPretty(source_url_buf, source_url_buf), + node_module_list.bundle_ctx, + call_ctx, + call_ctx, + &exception, + ); } }; @@ -597,20 +589,28 @@ pub const Module = struct { ), ); + std.mem.set(u8, this.tempbuf, 0); const size = js.JSStringGetUTF8CString(prop, this.tempbuf.ptr, this.tempbuf.len); - const key = std.hash.Wyhash.hash(0, this.tempbuf[0..size]); - const id = this.module_property_map.get(key) orelse return null; + const key = std.hash.Wyhash.hash(0, this.tempbuf); + const id = @intCast(u32, std.mem.indexOfScalar(u64, this.module_property_map, key) orelse return null); if (this.property_getters[id] == null) { - var require_bundled = this.vm.allocator.create(RequireBundledModule) catch unreachable; - require_bundled.* = RequireBundledModule{ .id = id, .list = this }; - this.property_getters[id] = To.JS.functionWithCallback( - RequireBundledModule, - require_bundled, - prop, - ctx, - requireBundledModule, - ); + if (!require_bundle_class_loaded) { + require_bundle_class_def = js.kJSClassDefinitionEmpty; + require_bundle_class_def.className = RequireBundleClassName[0.. :0]; + require_bundle_class_def.callAsFunction = To.JS.Callback(RequireBundledModule, requireBundledModule).rfn; + require_bundle_class_ref = js.JSClassRetain(js.JSClassCreate(&require_bundle_class_def)); + require_bundle_class_loaded = true; + } + + // TODO: remove this allocation by ptr casting + var require_from_bundle = this.vm.allocator.create(RequireBundledModule) catch unreachable; + require_from_bundle.* = RequireBundledModule{ + .list = this, + .id = id, + }; + this.property_getters[id] = js.JSObjectMake(this.bundle_ctx, require_bundle_class_ref, require_from_bundle); + js.JSValueProtect(this.bundle_ctx, this.property_getters[id]); } return this.property_getters[id]; @@ -628,8 +628,7 @@ pub const Module = struct { const bundle = &obj.list.vm.node_modules.?.bundle; const bundled_module = &bundle.modules[obj.id]; const bundled_pkg = &bundle.packages[bundled_module.package_id]; - - const result = loadBundledModuleById(obj.list, obj.id) catch |err| { + var module = loadBundledModuleById(obj.list, obj.id, obj.list.bundle_ctx) catch |err| { Output.prettyErrorln("<r><red>RequireError<r>: <b>{s}<r> in \"<cyan>{s}/{s}<r>\"", .{ @errorName(err), obj.list.vm.node_modules.?.str(bundled_pkg.name), @@ -648,74 +647,58 @@ pub const Module = struct { return js.JSValueMakeUndefined(ctx); }; - return result.internalGetExports(); + return module.internalGetExports(js.JSContextGetGlobalContext(ctx)); } - pub fn init(vm: *VirtualMachine, bundle: *const NodeModuleBundle) !*NodeModuleList { + pub fn create(vm: *VirtualMachine, bundle: *const NodeModuleBundle, node_module_list: *NodeModuleList) !void { var size: usize = 0; var longest_size: usize = 0; for (bundle.bundle.modules) |module, i| { - var hasher = std.hash.Wyhash.init(0); - hasher.update(bundle.str(module.path)); - hasher.update( - std.mem.asBytes( - &bundle.bundle.packages[module.package_id].hash, - ), - ); // Add one for null-terminated string offset const this_size = std.fmt.count( "${x}" ++ "\\x0", .{ - @truncate( - u32, - hasher.final(), - ), + module.id, }, ); size += this_size; longest_size = std.math.max(this_size, longest_size); } - var static_properties = try vm.allocator.alloc(js.JSStaticValue, bundle.bundle.modules.len); - var utf8 = try vm.allocator.alloc(u8, size + longest_size); - + var static_properties = try vm.allocator.alloc(js.JSStaticValue, bundle.bundle.modules.len + 1); + static_properties[static_properties.len - 1] = std.mem.zeroes(js.JSStaticValue); + var utf8 = try vm.allocator.alloc(u8, size + std.math.max(longest_size, 32)); + std.mem.set(u8, utf8, 0); var tempbuf = utf8[size..]; var names_buf = utf8[0..size]; - var module_property_map = ModuleIDMap.init(vm.allocator); - try module_property_map.ensureCapacity(@truncate(u32, bundle.bundle.modules.len)); + var module_property_map = try vm.allocator.alloc(u64, bundle.bundle.modules.len); for (bundle.bundle.modules) |module, i| { var hasher = std.hash.Wyhash.init(0); - hasher.update(bundle.str(module.path)); - hasher.update( - std.mem.asBytes( - &bundle.bundle.packages[module.package_id].hash, - ), - ); const hash = @truncate( u32, - hasher.final(), + module.id, ); // The variable name is the hash of the module path - var name = std.fmt.bufPrintZ(names_buf, "${x}", .{hash}) catch unreachable; + var name = std.fmt.bufPrint(names_buf, "${x}", .{hash}) catch unreachable; + std.mem.set(u8, tempbuf, 0); + std.mem.copy(u8, tempbuf, name); + name.ptr[name.len] = 0; // But we don't store that for the hash map. Instead, we store the hash of name. // This lets us avoid storing pointers to the name in the hash table, so if we free it later // or something it won't cause issues. - hasher = std.hash.Wyhash.init(0); - hasher.update(name[0..]); - var property_key = hasher.final(); + module_property_map[i] = std.hash.Wyhash.hash(0, tempbuf); static_properties[i] = js.JSStaticValue{ .name = name.ptr, .getProperty = getRequireFromBundleProperty, .setProperty = null, .attributes = .kJSPropertyAttributeReadOnly, }; - names_buf = names_buf[name.len..]; - module_property_map.putAssumeCapacityNoClobberWithHash(property_key, property_key, @truncate(u32, i)); + names_buf = names_buf[name.len + 1 ..]; } var node_module_global_class_def = js.kJSClassDefinitionEmpty; @@ -725,14 +708,13 @@ pub const Module = struct { var property_getters = try vm.allocator.alloc(js.JSObjectRef, bundle.bundle.modules.len); std.mem.set(js.JSObjectRef, property_getters, null); - var node_module_list = try vm.allocator.create(NodeModuleList); node_module_list.* = NodeModuleList{ .module_property_map = module_property_map, .node_module_global_class_def = node_module_global_class_def, .vm = vm, .tempbuf = tempbuf, - .property_names = names_buf.ptr, + .property_names = utf8.ptr, .bundle_ctx = undefined, .property_getters = property_getters, .node_module_global_class = undefined, @@ -748,10 +730,9 @@ pub const Module = struct { // }; // node_module_global_class_def.staticFunctions = &node_module_list.static_functions; node_module_list.node_module_global_class_def = node_module_global_class_def; - node_module_list.node_module_global_class = js.JSClassCreate(&node_module_list.node_module_global_class_def); - node_module_list.bundle_ctx = js.JSGlobalContextCreateInGroup(vm.group, node_module_list.node_module_global_class); - - return node_module_list; + node_module_list.node_module_global_class = js.JSClassRetain(js.JSClassCreate(&node_module_list.node_module_global_class_def)); + node_module_list.bundle_ctx = js.JSGlobalContextRetain(js.JSGlobalContextCreateInGroup(vm.group, node_module_list.node_module_global_class)); + _ = js.JSObjectSetPrivate(js.JSContextGetGlobalObject(node_module_list.bundle_ctx), node_module_list); } }; pub const node_module_global_class_name = "NodeModuleGlobal"; @@ -759,6 +740,21 @@ pub const Module = struct { threadlocal var require_buf: MutableString = undefined; threadlocal var require_buf_loaded: bool = false; + pub fn callExportsAsFunction( + this: *Module, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + if (js.JSObjectIsFunction(ctx, this.exports_function)) { + return js.JSObjectCallAsFunction(ctx, this.exports_function, this.ref, arguments.len, arguments.ptr, exception); + } + + return this.exports; + } + pub fn require( this: *Module, ctx: js.JSContextRef, @@ -784,18 +780,20 @@ pub const Module = struct { const len = js.JSStringGetLength(arguments[0]); - if (!require_buf_loaded) { - require_buf = MutableString.init(this.vm.allocator, len + 1) catch unreachable; - require_buf_loaded = true; - } else { - require_buf.reset(); - require_buf.growIfNeeded(len + 1) catch {}; - } + // if (!require_buf_loaded) { + // require_buf = MutableString.init(this.vm.allocator, len + 1) catch unreachable; + // require_buf_loaded = true; + // } else { + // require_buf.reset(); + // require_buf.growIfNeeded(len + 1) catch {}; + // } - require_buf.list.resize(this.vm.allocator, len + 1) catch unreachable; + // require_buf.list.resize(this.vm.allocator, len + 1) catch unreachable; - var end = js.JSStringGetUTF8CString(arguments[0], require_buf.list.items.ptr, require_buf.list.items.len); - var import_path = require_buf.list.items[0 .. end - 1]; + var require_buf_ = this.vm.allocator.alloc(u8, len + 1) catch unreachable; + var end = js.JSStringGetUTF8CString(arguments[0], require_buf_.ptr, require_buf_.len); + // var end = js.JSStringGetUTF8CString(arguments[0], require_buf.list.items.ptr, require_buf.list.items.len); + var import_path = require_buf_[0 .. end - 1]; var module = this; if (this.vm.bundler.linker.resolver.resolve(module.path.name.dirWithTrailingSlash(), import_path, .require)) |resolved| { @@ -805,7 +803,14 @@ pub const Module = struct { switch (load_result) { .Module => |new_module| { - return new_module.internalGetExports(); + if (isDebug) { + Output.prettyln( + "Input: {s}\nOutput: {s}", + .{ import_path, load_result.Module.path.text }, + ); + Output.flush(); + } + return new_module.internalGetExports(js.JSContextGetGlobalContext(ctx)); }, .Path => |path| { return js.JSStringCreateWithUTF8CString(path.text.ptr); @@ -831,6 +836,10 @@ pub const Module = struct { .get = getId, .ro = true, }, + .@"loaded" = .{ + .get = getLoaded, + .ro = true, + }, .@"exports" = .{ .get = getExports, .set = setExports, @@ -848,6 +857,8 @@ pub const Module = struct { pub fn boot(vm: *VirtualMachine) void { ExportsClass = std.mem.zeroes(js.JSClassDefinition); ExportsClass.className = ExportsClassName[0.. :0]; + ExportsClass.callAsFunction = To.JS.Callback(Module, callExportsAsFunction).rfn; + // ExportsClass.callAsConstructor = To.JS.Callback(Module, callExportsAsConstructor); exports_class_ref = js.JSClassRetain(js.JSClassCreate(&ExportsClass)); @@ -869,22 +880,26 @@ pub const Module = struct { threadlocal var source_code_printer_loaded: bool = false; var require_module_params: [3]js.JSStringRef = undefined; var require_module_params_loaded: bool = false; + threadlocal var module_wrapper_params: [2]js.JSStringRef = undefined; + threadlocal var module_wrapper_loaded = false; pub fn load( + module: *Module, vm: *VirtualMachine, allocator: *std.mem.Allocator, log: *logger.Log, source: [:0]u8, path: Fs.Path, + global_ctx: js.JSContextRef, call_ctx: js.JSContextRef, function_ctx: js.JSContextRef, exception: js.ExceptionRef, - ) !*Module { - var source_code_ref = js.JSStringRetain(js.JSStringCreateWithUTF8CString(source.ptr)); + ) !void { + var source_code_ref = js.JSStringCreateWithUTF8CString(source.ptr); defer js.JSStringRelease(source_code_ref); var source_url = try allocator.dupeZ(u8, path.text); defer allocator.free(source_url); - var source_url_ref = js.JSStringRetain(js.JSStringCreateWithUTF8CString(source_url.ptr)); + var source_url_ref = js.JSStringCreateWithUTF8CString(source_url.ptr); defer js.JSStringRelease(source_url_ref); if (isDebug) { @@ -892,43 +907,49 @@ pub const Module = struct { Output.flush(); } - var module = try allocator.create(Module); module.* = Module{ .path = path, .ref = undefined, .vm = vm, }; - module.ref = js.JSObjectMake(function_ctx, Module.module_class, module); + module.ref = js.JSObjectMake(global_ctx, Module.module_class, module); - js.JSValueProtect(function_ctx, module.ref); + js.JSValueProtect(global_ctx, module.ref); - // TODO: move these allocations to only occur once - var args = try allocator.alloc(js.JSValueRef, 2); - var params = try allocator.alloc(js.JSStringRef, 2); - params[0] = js.JSStringCreateWithUTF8CString(Properties.UTF8.module[0.. :0]); - params[1] = js.JSStringCreateWithUTF8CString(Properties.UTF8.exports[0.. :0]); - args[0] = module.ref; - args[1] = module.internalGetExports(); - js.JSValueProtect(function_ctx, args[1]); + if (!module_wrapper_loaded) { + module_wrapper_params[0] = js.JSStringCreateWithUTF8CString(Properties.UTF8.module[0.. :0]); + module_wrapper_params[1] = js.JSStringCreateWithUTF8CString(Properties.UTF8.exports[0.. :0]); + module_wrapper_loaded = true; + } - defer allocator.free(args); + var module_wrapper_args: [2]js.JSValueRef = undefined; + module_wrapper_args[0] = module.ref; + module_wrapper_args[1] = module.internalGetExports(global_ctx); + js.JSValueProtect(global_ctx, module_wrapper_args[1]); var except: js.JSValueRef = null; go: { var commonjs_wrapper = js.JSObjectMakeFunction( - function_ctx, + global_ctx, null, - @truncate(c_uint, params.len), - params.ptr, + @truncate(c_uint, module_wrapper_params.len), + &module_wrapper_params, source_code_ref, null, 1, &except, ); + js.JSValueProtect(global_ctx, commonjs_wrapper); if (except != null) { break :go; } - - _ = js.JSObjectCallAsFunction(call_ctx, commonjs_wrapper, null, 2, args.ptr, &except); + // var module = {exports: {}}; ((module, exports) => { + _ = js.JSObjectCallAsFunction(call_ctx, commonjs_wrapper, null, 2, &module_wrapper_args, &except); + // module.exports = exports; + // })(module, module.exports); + + // module.exports = module_wrapper_args[1]; + js.JSValueProtect(global_ctx, module.exports); + js.JSValueUnprotect(global_ctx, commonjs_wrapper); } if (except != null) { var message = js.JSValueToStringCopy(function_ctx, except.?, null); @@ -948,7 +969,8 @@ pub const Module = struct { }); return error.FailedException; } - return module; + + module.loaded = true; } pub fn loadFromResolveResult( @@ -963,7 +985,14 @@ pub const Module = struct { } const path = resolved.path_pair.primary; - const loader = vm.bundler.options.loaders.get(path.name.ext) orelse .file; + const loader: options.Loader = brk: { + if (resolved.is_external) { + break :brk options.Loader.file; + } + + break :brk vm.bundler.options.loaders.get(path.name.ext) orelse .file; + }; + switch (loader) { .js, .jsx, @@ -971,21 +1000,42 @@ pub const Module = struct { .tsx, .json, => { - if (resolved.package_json) |package_json| { + const package_json_ = resolved.package_json orelse brk: { + // package_json is sometimes null when we're loading as an absolute path + if (resolved.isLikelyNodeModule()) { + break :brk vm.bundler.resolver.packageJSONForResolvedNodeModule(&resolved); + } + break :brk null; + }; + + if (package_json_) |package_json| { if (package_json.hash > 0) { if (vm.node_modules) |node_modules| { - if (node_modules.getPackageIDByHash(package_json.hash)) |package_id| { - const package_relative_path = vm.bundler.fs.relative( - package_json.source.path.name.dirWithTrailingSlash(), - path.text, - ); - - if (node_modules.findModuleIDInPackage( - &node_modules.bundle.packages[package_id], - package_relative_path, - )) |id| { - var list = vm.node_module_list.?; - return LoadResult{ .Module = try list.loadBundledModuleById(id) }; + if (node_modules.getPackageIDByName(package_json.name)) |possible_package_ids| { + const package_id: ?u32 = brk: { + for (possible_package_ids) |pid| { + const pkg = node_modules.bundle.packages[pid]; + if (pkg.hash == package_json.hash) { + break :brk pid; + } + } + + break :brk null; + }; + + if (package_id) |pid| { + const package_relative_path = vm.bundler.fs.relative( + package_json.source.path.name.dirWithTrailingSlash(), + path.text, + ); + + if (node_modules.findModuleIDInPackage( + &node_modules.bundle.packages[pid], + package_relative_path, + )) |id| { + var list = vm.node_module_list.?; + return LoadResult{ .Module = try list.loadBundledModuleById(id + node_modules.bundle.packages[pid].modules_offset, ctx) }; + } } } } @@ -1044,18 +1094,23 @@ pub const Module = struct { if (written == 0) { return error.PrintingErrorWriteFailed; } + var module = try vm.allocator.create(Module); + errdefer vm.allocator.destroy(module); + try vm.require_cache.put(hash, module); - var module = try Module.load( + try Module.load( + module, vm, vm.allocator, vm.log, source_code_printer.ctx.sentinel, path, - ctx, vm.global.ctx, + ctx, + ctx, exception, ); - try vm.require_cache.put(hash, module); + return LoadResult{ .Module = module }; }, @@ -1119,6 +1174,16 @@ pub const Module = struct { } } + pub fn getLoaded( + this: *Module, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { + return js.JSValueMakeBoolean(ctx, this.loaded); + } + pub fn getId( this: *Module, ctx: js.JSContextRef, @@ -1140,12 +1205,12 @@ pub const Module = struct { prop: js.JSStringRef, exception: js.ExceptionRef, ) callconv(.C) js.JSValueRef { - return this.internalGetExports(); + return this.exports; } - pub fn internalGetExports(this: *Module) js.JSValueRef { + pub fn internalGetExports(this: *Module, globalContext: js.JSContextRef) js.JSValueRef { if (this.exports == null) { - this.exports = js.JSObjectMake(this.vm.global.ctx, exports_class_ref, this); + this.exports = js.JSObjectMake(globalContext, exports_class_ref, this); } return this.exports; @@ -1178,8 +1243,35 @@ pub const Module = struct { js.JSStringRelease(this.exports); } } + switch (js.JSValueGetType(ctx, value)) { + .kJSTypeObject => { + if (js.JSValueIsObjectOfClass(ctx, value, exports_class_ref)) { + var other = @ptrCast( + *Module, + @alignCast( + @alignOf( + *Module, + ), + js.JSObjectGetPrivate(value).?, + ), + ); + + if (other != this) { + this.exports = other.exports; + } + + return true; + } else { + if (js.JSObjectIsFunction(ctx, value)) { + this.exports_function = value; + } + } + }, + else => {}, + } this.exports = value; + return true; } @@ -1264,9 +1356,15 @@ pub const GlobalObject = struct { std.debug.assert(js.JSObjectSetPrivate(js.JSContextGetGlobalObject(global.ctx), private)); global.ref = js.JSContextGetGlobalObject(global.ctx); + + if (!printer_buf_loaded) { + printer_buf_loaded = true; + printer_buf = try MutableString.init(global.vm.allocator, 0); + } } - threadlocal var printer_buf: [4092]u8 = undefined; + threadlocal var printer_buf: MutableString = undefined; + threadlocal var printer_buf_loaded: bool = false; fn valuePrinter(comptime ValueType: js.JSType, ctx: js.JSContextRef, arg: js.JSValueRef, writer: anytype) !void { switch (ValueType) { .kJSTypeUndefined => { @@ -1289,8 +1387,17 @@ pub const GlobalObject = struct { ); }, .kJSTypeString => { - const used = js.JSStringGetUTF8CString(arg, (&printer_buf), printer_buf.len); - try writer.writeAll(printer_buf[0..used]); + printer_buf.reset(); + var string_ref = js.JSValueToStringCopy(ctx, arg, null); + const len = js.JSStringGetMaximumUTF8CStringSize(string_ref) + 1; + + printer_buf.growIfNeeded(len) catch {}; + printer_buf.inflate(len) catch {}; + var slice = printer_buf.toOwnedSliceLeaky(); + + defer js.JSStringRelease(string_ref); + const used = js.JSStringGetUTF8CString(string_ref, slice.ptr, slice.len); + try writer.writeAll(slice[0..used]); }, .kJSTypeObject => { // TODO: |