diff options
Diffstat (limited to '')
-rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 13 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 6 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 130 |
3 files changed, 135 insertions, 14 deletions
diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index 407b452d2..407a03b96 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -512,7 +512,7 @@ bWTF__String JSC__JSString__value(JSC__JSString *arg0, JSC__JSGlobalObject *arg1 JSC__JSValue JSC__JSModuleLoader__callExportedFunction(JSC__JSGlobalObject *globalObject, ZigString specifier, ZigString functionName, JSC__JSValue *arguments, - unsigned char args_len, + unsigned char argumentsCount, ZigException *zig_exception) { JSC::VM &vm = globalObject->vm(); JSC::JSLockHolder lock(vm); @@ -531,22 +531,21 @@ JSC__JSValue JSC__JSModuleLoader__callExportedFunction(JSC__JSGlobalObject *glob if (JSC::JSModuleRecord *record = JSC::jsDynamicCast<JSC::JSModuleRecord *>(vm, entry->getDirect(vm, recordIdentifier))) { auto fn_impl = WTF::ExternalStringImpl::createStatic(functionName.ptr, functionName.len); - auto fn_ident = reinterpret_cast<WTF::UniquedStringImpl *>(specifier_impl.ptr()); - auto env = record->getModuleNamespace(globalObject); + auto fn_ident = reinterpret_cast<WTF::UniquedStringImpl *>(fn_impl.ptr()); + auto moduleNamespace = record->getModuleNamespace(globalObject); if (JSC::JSValue macroFunctionExport = - env->getIfPropertyExists(globalObject, JSC::PropertyName(fn_ident))) { + moduleNamespace->getIfPropertyExists(globalObject, JSC::PropertyName(fn_ident))) { if (JSC::JSObject *macroFunction = JSC::asObject(macroFunctionExport.asCell())) { - // auto functionNameImpl = - // WTF::ExternalStringImpl::createStatic(functionName.ptr, functionName.len); JSC::VMEntryScope entryScope(vm, globalObject); auto callData = JSC::getCallData(vm, macroFunction); if (callData.type == JSC::CallData::Type::None) return JSC::JSValue::encode({}); JSC::MarkedArgumentBuffer argList; - for (size_t i = 0; i < args_len; i++) argList.append(JSC::JSValue::decode(arguments[i])); + for (size_t i = 0; i < argumentsCount; i++) + argList.append(JSC::JSValue::decode(arguments[i])); NakedPtr<JSC::Exception> uncaughtException; JSC::JSValue reval = JSC::call(globalObject, macroFunction, callData, diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 96c1767b4..09fa04d3b 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -1445,11 +1445,11 @@ pub const JSValue = enum(i64) { } pub inline fn asRef(this: JSValue) C_API.JSValueRef { - return @intToPtr(C_API.JSValueRef, @intCast(usize, @enumToInt(this))); + return @intToPtr(C_API.JSValueRef, @bitCast(usize, @enumToInt(this))); } pub inline fn fromRef(this: C_API.JSValueRef) JSValue { - return @intToEnum(JSValue, @intCast(i64, @ptrToInt(this))); + return @intToEnum(JSValue, @bitCast(i64, @ptrToInt(this))); } pub inline fn asObjectRef(this: JSValue) C_API.JSObjectRef { @@ -1457,7 +1457,7 @@ pub const JSValue = enum(i64) { } pub inline fn asVoid(this: JSValue) *c_void { - return @intToPtr(*c_void, @intCast(usize, @enumToInt(this))); + return @intToPtr(*c_void, @bitCast(usize, @enumToInt(this))); } pub const Extern = [_][]const u8{ "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" }; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 9e6f5defd..e461f48d3 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -9,8 +9,10 @@ const Api = @import("../../api/schema.zig").Api; const options = @import("../../options.zig"); const Bundler = @import("../../bundler.zig").Bundler; const ServerEntryPoint = @import("../../bundler.zig").ServerEntryPoint; +const MacroEntryPoint = @import("../../bundler.zig").MacroEntryPoint; const js_printer = @import("../../js_printer.zig"); const js_parser = @import("../../js_parser.zig"); +const js_ast = @import("../../js_ast.zig"); const hash_map = @import("../../hash_map.zig"); const http = @import("../../http.zig"); const ImportKind = ast.ImportKind; @@ -158,6 +160,41 @@ pub const Bun = struct { return JSValue.createStringArray(VirtualMachine.vm.global, styles.ptr, styles.len).asRef(); } + pub fn registerMacro( + this: void, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + if (arguments.len != 2 or !js.JSValueIsNumber(ctx, arguments[0])) { + JSError(getAllocator(ctx), "Internal error registering macros: invalid args", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + // TODO: make this faster + const id = @truncate(i32, @floatToInt(i64, js.JSValueToNumber(ctx, arguments[0], exception))); + if (id == -1 or id == 0) { + JSError(getAllocator(ctx), "Internal error registering macros: invalid id", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + if (!js.JSValueIsObject(ctx, arguments[1]) or !js.JSObjectIsFunction(ctx, arguments[1])) { + JSError(getAllocator(ctx), "Macro must be a function. Received: {s}", .{@tagName(js.JSValueGetType(ctx, arguments[1]))}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + var get_or_put_result = VirtualMachine.vm.macros.getOrPut(id) catch unreachable; + if (get_or_put_result.found_existing) { + js.JSValueUnprotect(ctx, get_or_put_result.value_ptr.*); + } + + js.JSValueProtect(ctx, arguments[1]); + get_or_put_result.value_ptr.* = arguments[1]; + + return js.JSValueMakeUndefined(ctx); + } + pub fn getRouteFiles( this: void, ctx: js.JSContextRef, @@ -330,6 +367,13 @@ pub const Bun = struct { .@"return" = "string", }, }, + .registerMacro = .{ + .rfn = Bun.registerMacro, + .ts = d.ts{ + .name = "registerMacro", + .@"return" = "undefined", + }, + }, }, .{ .main = .{ @@ -367,6 +411,9 @@ pub const VirtualMachine = struct { allocator: *std.mem.Allocator, node_modules: ?*NodeModuleBundle = null, bundler: Bundler, + + macro_mode: bool = false, + watcher: ?*http.Watcher = null, console: *ZigConsoleClient, log: *logger.Log, @@ -375,6 +422,7 @@ pub const VirtualMachine = struct { process: js.JSObjectRef = null, blobs: *Blob.Group = undefined, flush_list: std.ArrayList(string), + macro_entry_points: std.AutoArrayHashMap(i32, *MacroEntryPoint), entry_point: ServerEntryPoint = undefined, arena: *std.heap.ArenaAllocator = undefined, @@ -383,9 +431,22 @@ pub const VirtualMachine = struct { transpiled_count: usize = 0, resolved_count: usize = 0, had_errors: bool = false, + macros: MacroMap, pub threadlocal var vm_loaded = false; pub threadlocal var vm: *VirtualMachine = undefined; + pub const MacroMap = std.AutoArrayHashMap(i32, js.JSObjectRef); + + pub fn enableMacroMode(this: *VirtualMachine) void { + this.bundler.options.platform = .bunMacro; + this.macro_mode = true; + } + + pub fn disableMacroMode(this: *VirtualMachine) void { + this.bundler.options.platform = .bun; + this.macro_mode = false; + } + pub fn init( allocator: *std.mem.Allocator, _args: Api.TransformOptions, @@ -420,6 +481,8 @@ pub const VirtualMachine = struct { .console = console, .node_modules = bundler.options.node_modules_bundle, .log = log, + .macros = MacroMap.init(allocator), + .macro_entry_points = @TypeOf(VirtualMachine.vm.macro_entry_points).init(allocator), .flush_list = std.ArrayList(string).init(allocator), .blobs = try Blob.Group.init(allocator), }; @@ -550,7 +613,7 @@ pub const VirtualMachine = struct { var parse_result = ParseResult{ .source = 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"; + file_path.name.base = main_file_name; try bundler.linker.link( file_path, &parse_result, @@ -579,6 +642,19 @@ pub const VirtualMachine = struct { .hash = 0, .bytecodecache_fd = 0, }; + } 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| { + return ResolvedSource{ + .allocator = null, + .source_code = ZigString.init(entry.source.contents), + .specifier = ZigString.init(_specifier), + .source_url = ZigString.init(_specifier), + .hash = 0, + .bytecodecache_fd = 0, + }; + } } const specifier = normalizeSpecifier(_specifier); @@ -691,10 +767,16 @@ pub const VirtualMachine = struct { ret.result = null; ret.path = 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; + ret.path = specifier; + return; } + const is_special_source = strings.eqlComptime(source, main_file_name) or js_ast.Macro.isMacroPath(source); + const result = try vm.bundler.resolver.resolve( - if (!strings.eqlComptime(source, main_file_name)) Fs.PathName.init(source).dirWithTrailingSlash() else VirtualMachine.vm.bundler.fs.top_level_dir, + if (!is_special_source) Fs.PathName.init(source).dirWithTrailingSlash() else VirtualMachine.vm.bundler.fs.top_level_dir, specifier, .stmt, ); @@ -970,6 +1052,46 @@ pub const VirtualMachine = struct { return promise; } + pub fn loadMacroEntryPoint(this: *VirtualMachine, entry_path: string, function_name: string, specifier: string, hash: i32) !*JSInternalPromise { + var entry_point_entry = try this.macro_entry_points.getOrPut(hash); + + if (!entry_point_entry.found_existing) { + var macro_entry_pointer: *MacroEntryPoint = this.allocator.create(MacroEntryPoint) catch unreachable; + entry_point_entry.value_ptr.* = macro_entry_pointer; + try macro_entry_pointer.generate(&this.bundler, Fs.PathName.init(entry_path), function_name, hash, specifier); + } + var entry_point = entry_point_entry.value_ptr.*; + + var promise: *JSInternalPromise = undefined; + // We first import the node_modules bundle. This prevents any potential TDZ issues. + // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick. + if (this.node_modules != null) { + promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(bun_file_import_path))); + + this.global.vm().drainMicrotasks(); + + while (promise.status(this.global.vm()) == JSPromise.Status.Pending) { + this.global.vm().drainMicrotasks(); + } + + if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) { + return promise; + } + + _ = promise.result(this.global.vm()); + } + + promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(entry_point.source.path.text)); + + this.global.vm().drainMicrotasks(); + + while (promise.status(this.global.vm()) == JSPromise.Status.Pending) { + this.global.vm().drainMicrotasks(); + } + + return promise; + } + // When the Error-like object is one of our own, it's best to rely on the object directly instead of serializing it to a ZigException. // This is for: // - BuildError @@ -1049,7 +1171,7 @@ pub const VirtualMachine = struct { var build_error = private_data_ptr.as(BuildError); if (!build_error.logged) { var writer = Output.errorWriter(); - build_error.msg.formatWriter(@TypeOf(writer), writer, allow_ansi_color) catch {}; + build_error.msg.writeFormat(writer, allow_ansi_color) catch {}; build_error.logged = true; } this.had_errors = this.had_errors or build_error.msg.kind == .err; @@ -1065,7 +1187,7 @@ pub const VirtualMachine = struct { var resolve_error = private_data_ptr.as(ResolveError); if (!resolve_error.logged) { var writer = Output.errorWriter(); - resolve_error.msg.formatWriter(@TypeOf(writer), writer, allow_ansi_color) catch {}; + resolve_error.msg.writeFormat(writer, allow_ansi_color) catch {}; resolve_error.logged = true; } |