diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | integration/bunjs-only-snippets/globals.test.js | 38 | ||||
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 11 | ||||
-rw-r--r-- | src/javascript/jsc/api/html_rewriter.zig | 3 | ||||
-rw-r--r-- | src/javascript/jsc/api/router.zig | 22 | ||||
-rw-r--r-- | src/javascript/jsc/api/transpiler.zig | 5 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 386 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/ZigGlobalObject.cpp | 44 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 6 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.zig | 17 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/exports.zig | 35 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers-cpp.h | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.h | 4 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/headers.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/root.h | 3 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 41 | ||||
-rw-r--r-- | src/javascript/jsc/node/types.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/encoding.zig | 98 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 60 |
19 files changed, 496 insertions, 284 deletions
@@ -979,6 +979,7 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp $(CLANG_FLAGS) \ $(MACOS_MIN_FLAG) \ -O3 \ + -g \ -fno-exceptions \ -ffunction-sections -fdata-sections -g \ -ferror-limit=1000 diff --git a/integration/bunjs-only-snippets/globals.test.js b/integration/bunjs-only-snippets/globals.test.js new file mode 100644 index 000000000..831bbd93f --- /dev/null +++ b/integration/bunjs-only-snippets/globals.test.js @@ -0,0 +1,38 @@ +import { it, describe, expect } from "bun:test"; + +it("extendable", () => { + const classes = [ + Blob, + TextDecoder, + TextEncoder, + Request, + Response, + Headers, + HTMLRewriter, + Bun.Transpiler, + ]; + // None of these should error + for (let Class of classes) { + var Foo = class extends Class {}; + var bar = new Foo(); + expect(bar instanceof Class).toBe(true); + expect(Class.prototype instanceof Class).toBe(true); + } + expect(true).toBe(true); +}); + +it("name", () => { + const classes = [ + ["Blob", Blob], + ["TextDecoder", TextDecoder], + ["TextEncoder", TextEncoder], + ["Request", Request], + ["Response", Response], + ["Headers", Headers], + ["HTMLRewriter", HTMLRewriter], + ["Transpiler", Bun.Transpiler], + ]; + for (let [name, Class] of classes) { + expect(Class.name).toBe(name); + } +}); diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index baa751b9c..f2917b1f5 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -820,6 +820,7 @@ pub fn getPublicPathJS( var stream = std.io.fixedBufferStream(&public_path_temp_str); var writer = stream.writer(); getPublicPath(to, VirtualMachine.vm.origin, @TypeOf(&writer), &writer); + return ZigString.init(stream.buffer[0..stream.pos]).toValueGC(ctx.ptr()).asObjectRef(); } @@ -921,14 +922,17 @@ pub const Class = NewClass( .name = "registerMacro", .@"return" = "undefined", }, + .enumerable = false, }, .fs = .{ .rfn = Bun.createNodeFS, .ts = d.ts{}, + .enumerable = false, }, .jest = .{ .rfn = @import("../test/jest.zig").Jest.call, .ts = d.ts{}, + .enumerable = false, }, .gc = .{ .rfn = Bun.runGC, @@ -995,11 +999,14 @@ pub const Class = NewClass( pub fn getTranspilerConstructor( _: void, ctx: js.JSContextRef, - _: js.JSValueRef, + this: js.JSValueRef, _: js.JSStringRef, _: js.ExceptionRef, ) js.JSValueRef { - return js.JSObjectMake(ctx, Transpiler.TranspilerConstructor.get().?[0], null); + var value = Transpiler.Constructor.constructor(ctx); + JSC.JSValue.fromRef(this).put(ctx.ptr(), &ZigString.init("Transpiler"), JSC.JSValue.fromRef(value)); + + return value; } pub fn getTOMLObject( diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig index 4acb30ef6..c62a6503f 100644 --- a/src/javascript/jsc/api/html_rewriter.zig +++ b/src/javascript/jsc/api/html_rewriter.zig @@ -61,11 +61,12 @@ pub const HTMLRewriter = struct { builder: *LOLHTML.HTMLRewriter.Builder, context: LOLHTMLContext, + pub const Constructor = JSC.NewConstructor(HTMLRewriter, .{ .constructor = constructor }, .{}); + pub const Class = NewClass( HTMLRewriter, .{ .name = "HTMLRewriter" }, .{ - .constructor = constructor, .finalize = finalize, .on = .{ .rfn = wrap(HTMLRewriter, "on"), diff --git a/src/javascript/jsc/api/router.zig b/src/javascript/jsc/api/router.zig index da0731efa..8bf1ab7e0 100644 --- a/src/javascript/jsc/api/router.zig +++ b/src/javascript/jsc/api/router.zig @@ -65,19 +65,19 @@ pub fn match( return null; } - var arg: JSC.JSValue = undefined; + const arg: JSC.JSValue = brk: { + if (FetchEvent.Class.isLoaded()) { + if (JSValue.as(JSValue.fromRef(arguments[0]), FetchEvent)) |fetch_event| { + if (fetch_event.request_context != null) { + return matchFetchEvent(ctx, fetch_event, exception); + } - if (FetchEvent.Class.loaded and js.JSValueIsObjectOfClass(ctx, arguments[0], FetchEvent.Class.get().*)) { - var fetch_event = To.Zig.ptr(FetchEvent, arguments[0]); - if (fetch_event.request_context != null) { - return matchFetchEvent(ctx, fetch_event, exception); + // When disconencted, we still have a copy of the request data in here + break :brk JSC.JSValue.fromRef(fetch_event.getRequest(ctx, null, null, null)); + } } - - // When disconencted, we still have a copy of the request data in here - arg = JSC.JSValue.fromRef(fetch_event.getRequest(ctx, null, null, null)); - } else { - arg = JSC.JSValue.fromRef(arguments[0]); - } + break :brk JSC.JSValue.fromRef(arguments[0]); + }; var router = JavaScript.VirtualMachine.vm.bundler.router orelse { JSError(getAllocator(ctx), "Bun.match needs a framework configured with routes", .{}, ctx, exception); diff --git a/src/javascript/jsc/api/transpiler.zig b/src/javascript/jsc/api/transpiler.zig index 3987bc427..e7c78ac78 100644 --- a/src/javascript/jsc/api/transpiler.zig +++ b/src/javascript/jsc/api/transpiler.zig @@ -69,9 +69,8 @@ pub const Class = NewClass( .{}, ); -pub const TranspilerConstructor = NewClass( - void, - .{ .name = "Transpiler" }, +pub const Constructor = JSC.NewConstructor( + @This(), .{ .constructor = .{ .rfn = constructor }, }, diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 1ccc023ce..9dddba9ee 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -433,6 +433,7 @@ pub const Properties = struct { const hasSetter = std.meta.trait.hasField("set"); const hasReadOnly = std.meta.trait.hasField("ro"); const hasFinalize = std.meta.trait.hasField("finalize"); +const hasEnumerable = std.meta.trait.hasField("enumerable"); const hasTypeScriptField = std.meta.trait.hasField("ts"); fn hasTypeScript(comptime Type: type) bool { @@ -833,29 +834,59 @@ pub const d = struct { // This should only exist at compile-time. pub const ClassOptions = struct { - name: string, + name: stringZ, read_only: bool = false, + hidden: []const string = &[_]string{}, + no_inheritance: bool = false, singleton: bool = false, ts: d.ts.decl = d.ts.decl{ .empty = 0 }, }; -// work around a comptime bug +pub fn NewConstructor( + comptime InstanceType: type, + comptime staticFunctions: anytype, + comptime properties: anytype, +) type { + return struct { + pub usingnamespace NewClassWithInstanceType(void, InstanceType.Class.class_options, staticFunctions, properties, InstanceType); + const name_string = &ZigString.init(InstanceType.Class.class_options.name); + pub fn constructor(ctx: js.JSContextRef) callconv(.C) js.JSObjectRef { + return JSValue.makeWithNameAndPrototype( + ctx.ptr(), + @This().get().*, + InstanceType.Class.get().*, + name_string, + ).asObjectRef(); + } + }; +} const _to_json: stringZ = "toJSON"; + pub fn NewClass( comptime ZigType: type, comptime options: ClassOptions, comptime staticFunctions: anytype, comptime properties: anytype, ) type { - const read_only = options.read_only; - const singleton = options.singleton; - _ = read_only; + return NewClassWithInstanceType(ZigType, options, staticFunctions, properties, void); +} +pub fn NewClassWithInstanceType( + comptime ZigType: type, + comptime options: ClassOptions, + comptime staticFunctions: anytype, + comptime properties: anytype, + comptime InstanceType: type, +) type { return struct { - const name = options.name; + const read_only = options.read_only; + const singleton = options.singleton; + pub const name = options.name; + pub const class_options = options; pub const isJavaScriptCoreClass = true; + pub const Zig = ZigType; const ClassDefinitionCreator = @This(); const function_names = std.meta.fieldNames(@TypeOf(staticFunctions)); const function_name_literals = function_names; @@ -863,46 +894,22 @@ pub fn NewClass( var function_name_refs_set = false; var class_name_str = name[0.. :0].ptr; - var static_functions = brk: { - var funcs: [function_name_refs.len + 1]js.JSStaticFunction = undefined; - std.mem.set( - js.JSStaticFunction, - &funcs, - js.JSStaticFunction{ - .name = @intToPtr([*c]const u8, 0), - .callAsFunction = null, - .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, - }, - ); - break :brk funcs; - }; const property_names = std.meta.fieldNames(@TypeOf(properties)); var property_name_refs: [property_names.len]js.JSStringRef = undefined; var property_name_refs_set: bool = false; const property_name_literals = property_names; - pub threadlocal var ref: js.JSClassRef = null; - pub threadlocal var loaded = false; - pub var defined: bool = false; - pub var definition: js.JSClassDefinition = .{ - .version = 0, - .attributes = js.JSClassAttributes.kJSClassAttributeNone, - .className = name[0.. :0].ptr, - .parentClass = null, - .staticValues = null, - .staticFunctions = null, - .initialize = null, - .finalize = null, - .hasProperty = null, - .getProperty = null, - .setProperty = null, - .deleteProperty = null, - .getPropertyNames = null, - .callAsFunction = null, - .callAsConstructor = null, - .hasInstance = null, - .convertToType = null, + const LazyClassRef = struct { + ref: js.JSClassRef = null, + loaded: bool = false, }; + + threadlocal var lazy_ref: LazyClassRef = LazyClassRef{}; + + pub inline fn isLoaded() bool { + return lazy_ref.loaded; + } + const ConstructorWrapper = struct { pub fn rfn( ctx: js.JSContextRef, @@ -912,7 +919,7 @@ pub fn NewClass( arguments: [*c]const js.JSValueRef, exception: js.ExceptionRef, ) callconv(.C) js.JSValueRef { - return definition.callAsConstructor.?(ctx, function, argumentCount, arguments, exception); + return class_definition_ptr.callAsConstructor.?(ctx, function, argumentCount, arguments, exception); } }; @@ -934,24 +941,36 @@ pub fn NewClass( } pub const Constructor = ConstructorWrapper.rfn; + const class_definition_ptr = &complete_definition; pub fn get() callconv(.C) [*c]js.JSClassRef { - if (!defined) { - definition = define(); - defined = true; + var lazy = lazy_ref; + + if (!lazy.loaded) { + lazy = .{ + .ref = js.JSClassCreate(class_definition_ptr), + .loaded = true, + }; + lazy_ref = lazy; } - if (!loaded) { - loaded = true; - ref = js.JSClassCreate(&definition); - } - - _ = js.JSClassRetain(ref); + _ = js.JSClassRetain(lazy.ref); - return &ref; + return &lazy.ref; } pub fn customHasInstance(ctx: js.JSContextRef, _: js.JSObjectRef, value: js.JSValueRef, _: js.ExceptionRef) callconv(.C) bool { + if (InstanceType != void) { + var current = value; + while (current != null) { + if (js.JSValueIsObjectOfClass(ctx, current, InstanceType.Class.get().*)) { + return true; + } + current = js.JSObjectGetPrototype(ctx, current); + } + return false; + } + return js.JSValueIsObjectOfClass(ctx, value, get().*); } @@ -1105,6 +1124,10 @@ pub fn NewClass( }; } + pub inline fn getDefinition() js.JSClassDefinition { + return class_definition_ptr.*; + } + // This should only be run at comptime pub fn typescriptModuleDeclaration() d.ts.module { comptime var class = options.ts.module; @@ -1113,7 +1136,7 @@ pub fn NewClass( class.read_only = options.read_only; } - if (static_functions.len > 0) { + if (function_name_literals.len > 0) { var count: usize = 0; inline for (function_name_literals) |_, i| { const func = @field(staticFunctions, function_names[i]); @@ -1332,7 +1355,7 @@ pub fn NewClass( class.read_only = options.read_only; } - if (static_functions.len > 0) { + if (function_name_literals.len > 0) { var count: usize = 0; inline for (function_name_literals) |_, i| { const func = @field(staticFunctions, function_names[i]); @@ -1442,7 +1465,7 @@ pub fn NewClass( return comptime class; } - var static_properties = brk: { + const static_properties = brk: { var props: [property_names.len + 1]js.JSStaticValue = undefined; std.mem.set( js.JSStaticValue, @@ -1454,14 +1477,34 @@ pub fn NewClass( .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, }, ); + for (property_name_literals) |_, i| { + props[i] = brk2: { + var static_prop = JSC.C.JSStaticValue{ + .name = property_names[i][0.. :0].ptr, + .getProperty = null, + .setProperty = null, + .attributes = @intToEnum(js.JSPropertyAttributes, 0), + }; + static_prop.getProperty = StaticProperty(i).getter; + + const field = @field(properties, property_names[i]); + + if (hasSetter(@TypeOf(field))) { + static_prop.setProperty = StaticProperty(i).setter; + } + break :brk2 static_prop; + }; + } break :brk props; }; - pub fn define() js.JSClassDefinition { - var def = js.JSClassDefinition{ + // this madness is a workaround for stage1 compiler bugs + fn generateDef(comptime ReturnType: type) ReturnType { + var count = 0; + var def: js.JSClassDefinition = js.JSClassDefinition{ .version = 0, .attributes = js.JSClassAttributes.kJSClassAttributeNone, - .className = name.ptr[0..name.len :0], + .className = "", .parentClass = null, .staticValues = null, .staticFunctions = null, @@ -1478,117 +1521,146 @@ pub fn NewClass( .convertToType = null, }; - // These workaround stage1 compiler bugs - var JSStaticValue_empty = std.mem.zeroes(js.JSStaticValue); - var count: usize = 0; + var __static_functions: [function_name_literals.len + 1]js.JSStaticFunction = undefined; + + for (function_name_literals) |function_name_literal, i| { + const is_read_only = options.read_only; + + _ = i; + switch (@typeInfo(@TypeOf(@field(staticFunctions, function_name_literal)))) { + .Struct => { + if (strings.eqlComptime(function_name_literal, "constructor")) { + def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn; + } else if (strings.eqlComptime(function_name_literal, "finalize")) { + def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn; + } else if (strings.eqlComptime(function_name_literal, "call")) { + def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call.rfn).rfn; + } else if (strings.eqlComptime(function_name_literal, "callAsFunction")) { + const ctxfn = @field(staticFunctions, function_name_literal).rfn; + const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; + + const PointerType = std.meta.Child(Func.args[0].arg_type.?); + + def.callAsFunction = if (Func.calling_convention == .C) ctxfn else To.JS.Callback( + PointerType, + ctxfn, + ).rfn; + } else if (strings.eqlComptime(function_name_literal, "hasProperty")) { + def.hasProperty = @field(staticFunctions, "hasProperty").rfn; + } else if (strings.eqlComptime(function_name_literal, "getProperty")) { + def.getProperty = @field(staticFunctions, "getProperty").rfn; + } else if (strings.eqlComptime(function_name_literal, "setProperty")) { + def.setProperty = @field(staticFunctions, "setProperty").rfn; + } else if (strings.eqlComptime(function_name_literal, "deleteProperty")) { + def.deleteProperty = @field(staticFunctions, "deleteProperty").rfn; + } else if (strings.eqlComptime(function_name_literal, "getPropertyNames")) { + def.getPropertyNames = @field(staticFunctions, "getPropertyNames").rfn; + } else if (strings.eqlComptime(function_name_literal, "convertToType")) { + def.convertToType = @field(staticFunctions, "convertToType").rfn; + } else { + const CtxField = @field(staticFunctions, function_name_literals[i]); + if (!@hasField(@TypeOf(CtxField), "rfn")) { + @compileError("Expected " ++ options.name ++ "." ++ function_name_literal ++ " to have .rfn"); + } + const ctxfn = CtxField.rfn; + const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; - if (comptime static_functions.len > 0) { - inline for (function_name_literals) |function_name_literal, i| { - _ = i; - switch (comptime @typeInfo(@TypeOf(@field(staticFunctions, function_name_literal)))) { - .Struct => { - if (comptime strings.eqlComptime(function_name_literal, "constructor")) { - def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor.rfn).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "finalize")) { - def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize.rfn).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "call")) { - def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call.rfn).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "callAsFunction")) { - const ctxfn = @field(staticFunctions, function_name_literal).rfn; - const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; - - const PointerType = std.meta.Child(Func.args[0].arg_type.?); - - def.callAsFunction = if (Func.calling_convention == .C) ctxfn else To.JS.Callback( + var attributes: c_uint = @enumToInt(js.JSPropertyAttributes.kJSPropertyAttributeNone); + + if (is_read_only or hasReadOnly(@TypeOf(CtxField))) { + attributes |= @enumToInt(js.JSPropertyAttributes.kJSPropertyAttributeReadOnly); + } + + if (hasEnumerable(@TypeOf(CtxField)) and !CtxField.enumerable) { + attributes |= @enumToInt(js.JSPropertyAttributes.kJSPropertyAttributeDontEnum); + } + + var PointerType = void; + + if (Func.args[0].arg_type.? != void) { + PointerType = std.meta.Child(Func.args[0].arg_type.?); + } + + __static_functions[count] = js.JSStaticFunction{ + .name = @ptrCast([*c]const u8, function_names[i].ptr), + .callAsFunction = if (Func.calling_convention == .C) ctxfn else To.JS.Callback( PointerType, ctxfn, - ).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "hasProperty")) { - def.hasProperty = @field(staticFunctions, "hasProperty").rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "getProperty")) { - def.getProperty = @field(staticFunctions, "getProperty").rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "setProperty")) { - def.setProperty = @field(staticFunctions, "setProperty").rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "deleteProperty")) { - def.deleteProperty = @field(staticFunctions, "deleteProperty").rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "getPropertyNames")) { - def.getPropertyNames = @field(staticFunctions, "getPropertyNames").rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "convertToType")) { - def.convertToType = @field(staticFunctions, "convertToType").rfn; - } else { - const CtxField = comptime @field(staticFunctions, function_name_literal); - if (comptime !@hasField(@TypeOf(CtxField), "rfn")) { - @compileError("Expected " ++ options.name ++ "." ++ function_name_literal ++ " to have .rfn"); - } - const ctxfn = CtxField.rfn; - const Func: std.builtin.TypeInfo.Fn = @typeInfo(@TypeOf(ctxfn)).Fn; + ).rfn, + .attributes = @intToEnum(js.JSPropertyAttributes, attributes), + }; - const PointerType = if (Func.args[0].arg_type.? == void) void else std.meta.Child(Func.args[0].arg_type.?); + count += 1; + } + }, + .Fn => { + if (strings.eqlComptime(function_name_literal, "constructor")) { + def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor).rfn; + } else if (strings.eqlComptime(function_name_literal, "finalize")) { + def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn; + } else if (strings.eqlComptime(function_name_literal, "call")) { + def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call).rfn; + } else if (strings.eqlComptime(function_name_literal, "getPropertyNames")) { + def.getPropertyNames = To.JS.Callback(ZigType, staticFunctions.getPropertyNames).rfn; + } else if (strings.eqlComptime(function_name_literal, "hasInstance")) { + def.hasInstance = staticFunctions.hasInstance; + } else { + const attributes: js.JSPropertyAttributes = brk: { + var base = @enumToInt(js.JSPropertyAttributes.kJSPropertyAttributeNone); - static_functions[count] = js.JSStaticFunction{ - .name = (function_names[i][0.. :0]).ptr, - .callAsFunction = if (Func.calling_convention == .C) ctxfn else To.JS.Callback( - PointerType, - ctxfn, - ).rfn, - .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, - }; + if (is_read_only) + base |= @enumToInt(js.JSPropertyAttributes.kJSPropertyAttributeReadOnly); - count += 1; - } - }, - .Fn => { - if (comptime strings.eqlComptime(function_name_literal, "constructor")) { - def.callAsConstructor = To.JS.Constructor(staticFunctions.constructor).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "finalize")) { - def.finalize = To.JS.Finalize(ZigType, staticFunctions.finalize).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "call")) { - def.callAsFunction = To.JS.Callback(ZigType, staticFunctions.call).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "getPropertyNames")) { - def.getPropertyNames = To.JS.Callback(ZigType, staticFunctions.getPropertyNames).rfn; - } else if (comptime strings.eqlComptime(function_name_literal, "hasInstance")) { - def.hasInstance = staticFunctions.hasInstance; - } else { - static_functions[count] = js.JSStaticFunction{ - .name = (function_names[i][0.. :0]).ptr, - .callAsFunction = To.JS.Callback( - ZigType, - @field(staticFunctions, function_name_literal), - ).rfn, - .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, - }; - - count += 1; - } - }, - else => {}, - } + break :brk @intToEnum(js.JSPropertyAttributes, base); + }; - // if (singleton) { - // var function = js.JSObjectMakeFunctionWithCallback(ctx, function_name_refs[i], callback); - // instance_functions[i] = function; - // } - } + __static_functions[count] = js.JSStaticFunction{ + .name = @ptrCast([*c]const u8, function_names[i].ptr), + .callAsFunction = To.JS.Callback( + ZigType, + @field(staticFunctions, function_name_literal), + ).rfn, + .attributes = attributes, + }; - def.staticFunctions = static_functions[0..count].ptr; + count += 1; + } + }, + else => {}, + } } - if (comptime property_names.len > 0) { - inline for (property_name_literals) |_, i| { - static_properties[i] = JSStaticValue_empty; - static_properties[i].getProperty = StaticProperty(i).getter; + if (ReturnType == JSC.C.JSClassDefinition) { + return def; + } else { + while (count < __static_functions.len) : (count += 1) { + __static_functions[count] = js.JSStaticFunction{ + .name = "", + .callAsFunction = null, + .attributes = js.JSPropertyAttributes.kJSPropertyAttributeNone, + }; + } + + return __static_functions; + } + } - const field = comptime @field(properties, property_names[i]); + const base_def_ = generateDef(JSC.C.JSClassDefinition); + const static_functions__ = generateDef([function_name_literals.len + 1]js.JSStaticFunction); + const static_functions_ptr = &static_functions__; + const static_values_ptr = &static_properties; - if (comptime hasSetter(@TypeOf(field))) { - static_properties[i].setProperty = StaticProperty(i).setter; - } - static_properties[i].name = property_names[i][0.. :0].ptr; - } - def.staticValues = &static_properties; + const complete_definition = brk: { + var def = base_def_; + def.staticFunctions = static_functions_ptr; + if (options.no_inheritance) { + def.attributes = JSC.C.JSClassAttributes.kJSClassAttributeNoAutomaticPrototype; + } + if (property_names.len > 0) { + def.staticValues = static_values_ptr; } - def.className = class_name_str; + def.className = options.name; // def.getProperty = getPropertyCallback; if (def.callAsConstructor == null) { @@ -1605,8 +1677,8 @@ pub fn NewClass( if (!singleton and def.hasInstance == null) def.hasInstance = customHasInstance; - return def; - } + break :brk def; + }; }; } @@ -1615,7 +1687,6 @@ const ZigString = JSC.ZigString; pub const PathString = bun.PathString; -threadlocal var error_args: [1]js.JSValueRef = undefined; pub fn JSError( _: std.mem.Allocator, comptime fmt: string, @@ -1623,6 +1694,7 @@ pub fn JSError( ctx: js.JSContextRef, exception: ExceptionValueRef, ) void { + var error_args: [1]js.JSValueRef = undefined; @setCold(true); if (comptime std.meta.fields(@TypeOf(args)).len == 0) { diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp index 6dca86aa6..4c6a0f9c2 100644 --- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp +++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp @@ -199,6 +199,7 @@ const JSC::ClassInfo GlobalObject::s_info = { "GlobalObject", &Base::s_info, nul CREATE_METHOD_TABLE(GlobalObject) }; extern "C" JSClassRef* Zig__getAPIGlobals(size_t* count); +extern "C" const JSC__JSValue* Zig__getAPIConstructors(size_t* count, JSC__JSGlobalObject*); static JSGlobalObject* deriveShadowRealmGlobalObject(JSGlobalObject* globalObject) { @@ -216,6 +217,22 @@ static JSGlobalObject* deriveShadowRealmGlobalObject(JSGlobalObject* globalObjec return shadow; } +extern "C" JSC__JSValue JSC__JSValue__makeWithNameAndPrototype(JSC__JSGlobalObject* globalObject, void* arg1, void* arg2, const ZigString* visibleInterfaceName) +{ + auto& vm = globalObject->vm(); + JSClassRef jsClass = reinterpret_cast<JSClassRef>(arg1); + JSClassRef protoClass = reinterpret_cast<JSClassRef>(arg2); + JSObjectRef objectRef = JSObjectMake(reinterpret_cast<JSContextRef>(globalObject), jsClass, nullptr); + JSC::JSObject* object = JSC::JSValue::decode(reinterpret_cast<JSC__JSValue>(objectRef)).getObject(); + JSString* nameString = JSC::jsNontrivialString(vm, Zig::toString(*visibleInterfaceName)); + object->putDirect(vm, vm.propertyNames->name, nameString, JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum); + object->putDirect(vm, vm.propertyNames->toStringTagSymbol, + nameString, JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::ReadOnly); + object->putDirect(vm, vm.propertyNames->prototype, JSC::JSValue::decode(reinterpret_cast<JSC__JSValue>(JSObjectMake(reinterpret_cast<JSContextRef>(globalObject), protoClass, nullptr))), JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete); + + return JSC::JSValue::encode(JSC::JSValue(object)); +} + const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, @@ -562,16 +579,29 @@ static JSC_DEFINE_HOST_FUNCTION(functionReportError, // and any other objects available globally. void GlobalObject::installAPIGlobals(JSClassRef* globals, int count) { - WTF::Vector<GlobalPropertyInfo> extraStaticGlobals; - extraStaticGlobals.reserveCapacity((size_t)count + 3 + 9); + auto clientData = Bun::clientData(vm()); + size_t constructor_count = 0; + JSC__JSValue const* constructors = Zig__getAPIConstructors(&constructor_count, this); + WTF::Vector<GlobalPropertyInfo> extraStaticGlobals; + extraStaticGlobals.reserveCapacity((size_t)count + constructor_count + 3 + 9); int i = 0; - for (; i < count - 1; i++) { - auto jsClass = globals[i]; + for (; i < constructor_count; i++) { + auto* object = JSC::jsDynamicCast<JSC::JSCallbackObject<JSNonFinalObject>*>(vm(), JSC::JSValue::decode(constructors[i]).asCell()->getObject()); + if (JSObject* prototype = object->classRef()->prototype(this)) + object->setPrototypeDirect(vm(), prototype); + + extraStaticGlobals.uncheckedAppend( + GlobalPropertyInfo { JSC::Identifier::fromString(vm(), object->get(this, vm().propertyNames->name).toWTFString(this)), + JSC::JSValue(object), JSC::PropertyAttribute::DontDelete | 0 }); + } + int j = 0; + for (; j < count - 1; j++) { + auto jsClass = globals[j]; JSC::JSCallbackObject<JSNonFinalObject>* object = JSC::JSCallbackObject<JSNonFinalObject>::create(this, this->callbackObjectStructure(), jsClass, nullptr); - if (JSObject* prototype = jsClass->prototype(this)) + if (JSObject* prototype = object->classRef()->prototype(this)) object->setPrototypeDirect(vm(), prototype); extraStaticGlobals.uncheckedAppend( @@ -581,7 +611,7 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count) // The last one must be "process.env" // Runtime-support is for if they change - dot_env_class_ref = globals[i]; + dot_env_class_ref = globals[j]; // // The last one must be "process.env" // // Runtime-support is for if they change @@ -653,8 +683,6 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count) "reportError", functionReportError), JSC::PropertyAttribute::DontDelete | 0 }); - auto clientData = Bun::clientData(vm()); - this->addStaticGlobals(extraStaticGlobals.data(), extraStaticGlobals.size()); putDirectCustomAccessor( vm(), clientData->builtinNames().processPublicName(), diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index 181fe98be..aaff3102c 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -1413,6 +1413,12 @@ bool JSC__JSValue__isBoolean(JSC__JSValue JSValue0) return JSC::JSValue::decode(JSValue0).isBoolean(); } +void JSC__JSValue__put(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, const ZigString* arg2, JSC__JSValue JSValue3) +{ + JSC::JSObject* object = JSC::JSValue::decode(JSValue0).asCell()->getObject(); + object->putDirect(arg1->vm(), Zig::toIdentifier(*arg2, arg1), JSC::JSValue::decode(JSValue3)); +} + bool JSC__JSValue__isClass(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1) { JSC::JSValue value = JSC::JSValue::decode(JSValue0); diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig index 438c64967..8bb36a307 100644 --- a/src/javascript/jsc/bindings/bindings.zig +++ b/src/javascript/jsc/bindings/bindings.zig @@ -1864,6 +1864,10 @@ pub const JSValue = enum(u64) { return cppFn("putRecord", .{ value, global, key, values, values_len }); } + pub fn put(value: JSValue, global: *JSGlobalObject, key: *const ZigString, result: JSC.JSValue) void { + return cppFn("put", .{ value, global, key, result }); + } + pub fn as(value: JSValue, comptime ZigType: type) ?*ZigType { if (value.isUndefinedOrNull()) return null; @@ -1871,6 +1875,13 @@ pub const JSValue = enum(u64) { return JSC.GetJSPrivateData(ZigType, value.asObjectRef()); } + pub fn asCheckLoaded(value: JSValue, comptime ZigType: type) ?*ZigType { + if (!ZigType.Class.isLoaded() or value.isUndefinedOrNull()) + return null; + + return JSC.GetJSPrivateData(ZigType, value.asObjectRef()); + } + /// Create an object with exactly two properties pub fn createObject2(global: *JSGlobalObject, key1: *const ZigString, key2: *const ZigString, value1: JSValue, value2: JSValue) JSValue { return cppFn("createObject2", .{ global, key1, key2, value1, value2 }); @@ -1880,6 +1891,10 @@ pub const JSValue = enum(u64) { return cppFn("getErrorsProperty", .{ this, globalObject }); } + pub fn makeWithNameAndPrototype(globalObject: *JSGlobalObject, class: ?*anyopaque, instance: ?*anyopaque, name_: *const ZigString) JSValue { + return cppFn("makeWithNameAndPrototype", .{ globalObject, class, instance, name_ }); + } + pub fn jsNumberWithType(comptime Number: type, number: Number) JSValue { return switch (comptime Number) { JSValue => number, @@ -2291,7 +2306,7 @@ pub const JSValue = enum(u64) { return @intToPtr(*anyopaque, @enumToInt(this)); } - pub const Extern = [_][]const u8{ "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "getReadableStreamState", "getWritableStreamState", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "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" }; + pub const Extern = [_][]const u8{ "put", "makeWithNameAndPrototype", "parseJSON", "symbolKeyFor", "symbolFor", "getSymbolDescription", "createInternalPromise", "asInternalPromise", "asArrayBuffer_", "getReadableStreamState", "getWritableStreamState", "fromEntries", "createTypeError", "createRangeError", "createObject2", "getIfPropertyExistsImpl", "jsType", "jsonStringify", "kind_", "isTerminationException", "isSameValue", "getLengthOfArray", "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "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" }; }; extern "c" fn Microtask__run(*Microtask, *JSGlobalObject) void; diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig index 7a4c1eb0a..10c3674ac 100644 --- a/src/javascript/jsc/bindings/exports.zig +++ b/src/javascript/jsc/bindings/exports.zig @@ -259,6 +259,12 @@ export fn Zig__getAPIGlobals(count: *usize) [*]JSC.C.JSClassRef { return globals.ptr; } +export fn Zig__getAPIConstructors(count: *usize, ctx: *JSGlobalObject) [*]const JSValue { + var globals = JSC.VirtualMachine.getAPIConstructors(ctx); + count.* = globals.len; + return globals.ptr; +} + pub const JSErrorCode = enum(u8) { Error = 0, EvalError = 1, @@ -1195,6 +1201,16 @@ pub const ZigConsoleClient = struct { }; pub fn get(value: JSValue, globalThis: *JSGlobalObject) Result { + switch (@enumToInt(value)) { + 0, 0xa => return Result{ + .tag = .Undefined, + }, + 0x2 => return Result{ + .tag = .Null, + }, + else => {}, + } + if (value.isInt32()) { return .{ .tag = .Integer, @@ -1203,14 +1219,6 @@ pub const ZigConsoleClient = struct { return .{ .tag = .Double, }; - } else if (value.isUndefined()) { - return .{ - .tag = .Undefined, - }; - } else if (value.isNull()) { - return .{ - .tag = .Null, - }; } else if (value.isBoolean()) { return .{ .tag = .Boolean, @@ -1259,7 +1267,7 @@ pub const ZigConsoleClient = struct { }; } return .{ - .tag = .Class, + .tag = .Object, .cell = js_type, }; } @@ -1878,7 +1886,9 @@ pub const ZigConsoleClient = struct { while (i < count_) : (i += 1) { var property_name_ref = CAPI.JSPropertyNameArrayGetNameAtIndex(array, i); - var prop = CAPI.JSStringGetCharacters8Ptr(property_name_ref)[0..CAPI.JSStringGetLength(property_name_ref)]; + const prop_len = CAPI.JSStringGetLength(property_name_ref); + if (prop_len == 0) continue; + var prop = CAPI.JSStringGetCharacters8Ptr(property_name_ref)[0..prop_len]; if (strings.eqlComptime(prop, "children")) { CAPI.JSStringRelease(property_name_ref); continue; @@ -2054,7 +2064,9 @@ pub const ZigConsoleClient = struct { while (i < count_) : (i += 1) { var property_name_ref = CAPI.JSPropertyNameArrayGetNameAtIndex(array, i); defer CAPI.JSStringRelease(property_name_ref); - var prop = CAPI.JSStringGetCharacters8Ptr(property_name_ref)[0..CAPI.JSStringGetLength(property_name_ref)]; + const len = CAPI.JSStringGetLength(property_name_ref); + if (len == 0) continue; + var prop = CAPI.JSStringGetCharacters8Ptr(property_name_ref)[0..len]; var property_value = CAPI.JSObjectGetProperty(this.globalThis.ref(), object, property_name_ref, null); const tag = Tag.get(JSValue.fromRef(property_value), this.globalThis); @@ -2481,6 +2493,7 @@ comptime { _ = Process.getTitle; _ = Process.setTitle; _ = Zig__getAPIGlobals; + _ = Zig__getAPIConstructors; std.testing.refAllDecls(NodeReadableStream); std.testing.refAllDecls(Bun.Timer); std.testing.refAllDecls(NodeWritableStream); diff --git a/src/javascript/jsc/bindings/headers-cpp.h b/src/javascript/jsc/bindings/headers-cpp.h index 3c71807ab..ba8175a74 100644 --- a/src/javascript/jsc/bindings/headers-cpp.h +++ b/src/javascript/jsc/bindings/headers-cpp.h @@ -1,4 +1,4 @@ -//-- AUTOGENERATED FILE -- 1647445628 +//-- AUTOGENERATED FILE -- 1647600299 // clang-format off #pragma once diff --git a/src/javascript/jsc/bindings/headers.h b/src/javascript/jsc/bindings/headers.h index ba6fee8dc..1d36062da 100644 --- a/src/javascript/jsc/bindings/headers.h +++ b/src/javascript/jsc/bindings/headers.h @@ -1,5 +1,5 @@ // clang-format: off -//-- AUTOGENERATED FILE -- 1647445628 +//-- AUTOGENERATED FILE -- 1647600299 #pragma once #include <stddef.h> @@ -488,7 +488,9 @@ CPP_DECL void JSC__JSValue__jsonStringify(JSC__JSValue JSValue0, JSC__JSGlobalOb CPP_DECL JSC__JSValue JSC__JSValue__jsTDZValue(); CPP_DECL unsigned char JSC__JSValue__jsType(JSC__JSValue JSValue0); CPP_DECL JSC__JSValue JSC__JSValue__jsUndefined(); +CPP_DECL JSC__JSValue JSC__JSValue__makeWithNameAndPrototype(JSC__JSGlobalObject* arg0, void* arg1, void* arg2, const ZigString* arg3); CPP_DECL JSC__JSValue JSC__JSValue__parseJSON(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1); +CPP_DECL void JSC__JSValue__put(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, const ZigString* arg2, JSC__JSValue JSValue3); CPP_DECL void JSC__JSValue__putRecord(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, size_t arg4); CPP_DECL JSC__JSValue JSC__JSValue__symbolFor(JSC__JSGlobalObject* arg0, ZigString* arg1); CPP_DECL bool JSC__JSValue__symbolKeyFor(JSC__JSValue JSValue0, JSC__JSGlobalObject* arg1, ZigString* arg2); diff --git a/src/javascript/jsc/bindings/headers.zig b/src/javascript/jsc/bindings/headers.zig index c1ebc75f3..f9d09821a 100644 --- a/src/javascript/jsc/bindings/headers.zig +++ b/src/javascript/jsc/bindings/headers.zig @@ -336,7 +336,9 @@ pub extern fn JSC__JSValue__jsonStringify(JSValue0: JSC__JSValue, arg1: [*c]JSC_ pub extern fn JSC__JSValue__jsTDZValue(...) JSC__JSValue; pub extern fn JSC__JSValue__jsType(JSValue0: JSC__JSValue) u8; pub extern fn JSC__JSValue__jsUndefined(...) JSC__JSValue; +pub extern fn JSC__JSValue__makeWithNameAndPrototype(arg0: [*c]JSC__JSGlobalObject, arg1: ?*anyopaque, arg2: ?*anyopaque, arg3: [*c]const ZigString) JSC__JSValue; pub extern fn JSC__JSValue__parseJSON(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject) JSC__JSValue; +pub extern fn JSC__JSValue__put(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]const ZigString, JSValue3: JSC__JSValue) void; pub extern fn JSC__JSValue__putRecord(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString, arg3: [*c]ZigString, arg4: usize) void; pub extern fn JSC__JSValue__symbolFor(arg0: [*c]JSC__JSGlobalObject, arg1: [*c]ZigString) JSC__JSValue; pub extern fn JSC__JSValue__symbolKeyFor(JSValue0: JSC__JSValue, arg1: [*c]JSC__JSGlobalObject, arg2: [*c]ZigString) bool; diff --git a/src/javascript/jsc/bindings/root.h b/src/javascript/jsc/bindings/root.h index 7bbd8ec5f..4577ab0fd 100644 --- a/src/javascript/jsc/bindings/root.h +++ b/src/javascript/jsc/bindings/root.h @@ -66,4 +66,5 @@ #include <CoreFoundation/CoreFoundation.h> #endif -#include <JavaScriptCore/Heap.h>
\ No newline at end of file +#include <JavaScriptCore/Heap.h> +#include <wtf/PlatformCallingConventions.h>
\ No newline at end of file diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 88132aa77..709201a68 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -84,10 +84,18 @@ const Config = @import("./config.zig"); const URL = @import("../../url.zig").URL; const Transpiler = @import("./api/transpiler.zig"); const Bun = JSC.API.Bun; + +pub const GlobalConstructors = [_]type{ + WebCore.Blob.Constructor, + WebCore.TextDecoder.Constructor, + WebCore.TextEncoder.Constructor, + Request.Constructor, + Response.Constructor, + Headers.Constructor, + JSC.Cloudflare.HTMLRewriter.Constructor, +}; + pub const GlobalClasses = [_]type{ - Request.Class, - Response.Class, - Headers.Class, EventListenerMixin.addEventListener(VirtualMachine), BuildError.Class, ResolveError.Class, @@ -99,12 +107,6 @@ pub const GlobalClasses = [_]type{ WebCore.Crypto.Class, WebCore.Crypto.Prototype, - WebCore.TextEncoder.Constructor.Class, - WebCore.TextDecoder.Constructor.Class, - - JSC.Cloudflare.HTMLRewriter.Class, - WebCore.Blob.Class, - // The last item in this array becomes "process.env" Bun.EnvironmentVariables.Class, }; @@ -432,6 +434,7 @@ pub const SavedSourceMap = struct { pub const VirtualMachine = struct { global: *JSGlobalObject, allocator: std.mem.Allocator, + has_loaded_constructors: bool = false, node_modules: ?*NodeModuleBundle = null, bundler: Bundler, watcher: ?*http.Watcher = null, @@ -463,6 +466,7 @@ pub const VirtualMachine = struct { is_from_devserver: bool = false, has_enabled_macro_mode: bool = false, argv: []const []const u8 = &[_][]const u8{"bun"}, + global_api_constructors: [GlobalConstructors.len]JSC.JSValue = undefined, origin_timer: std.time.Timer = undefined, active_tasks: usize = 0, @@ -655,6 +659,25 @@ pub const VirtualMachine = struct { return classes; } + pub fn getAPIConstructors(globalObject: *JSGlobalObject) []const JSC.JSValue { + if (is_bindgen) + return &[_]JSC.JSValue{}; + if (!VirtualMachine.vm.has_loaded_constructors) { + VirtualMachine.vm.global = globalObject; + VirtualMachine.vm.has_loaded_constructors = true; + } + + inline for (GlobalConstructors) |Class, i| { + var ref = Class.constructor(globalObject.ref()).?; + JSC.C.JSValueProtect(globalObject.ref(), ref); + JSC.VirtualMachine.vm.global_api_constructors[i] = JSC.JSValue.fromRef( + ref, + ); + } + + return &JSC.VirtualMachine.vm.global_api_constructors; + } + pub fn init( allocator: std.mem.Allocator, _args: Api.TransformOptions, diff --git a/src/javascript/jsc/node/types.zig b/src/javascript/jsc/node/types.zig index a66efdf04..dfb1b63ed 100644 --- a/src/javascript/jsc/node/types.zig +++ b/src/javascript/jsc/node/types.zig @@ -691,7 +691,7 @@ pub const Date = enum(u64) { } }; -fn StatsLike(comptime name: string, comptime T: type) type { +fn StatsLike(comptime name: [:0]const u8, comptime T: type) type { return struct { const This = @This(); diff --git a/src/javascript/jsc/webcore/encoding.zig b/src/javascript/jsc/webcore/encoding.zig index f95e8589d..260d794ec 100644 --- a/src/javascript/jsc/webcore/encoding.zig +++ b/src/javascript/jsc/webcore/encoding.zig @@ -44,6 +44,14 @@ pub const TextEncoder = struct { filler: u32 = 0, var text_encoder: TextEncoder = TextEncoder{}; + pub const Constructor = JSC.NewConstructor( + TextEncoder, + .{ + .constructor = .{ .rfn = constructor }, + }, + .{}, + ); + pub const Class = NewClass( TextEncoder, .{ @@ -154,27 +162,14 @@ pub const TextEncoder = struct { return JSC.JSValue.createObject2(ctx.ptr(), &read_key, &written_key, JSValue.jsNumber(result.read), JSValue.jsNumber(result.written)).asObjectRef(); } - pub const Constructor = struct { - pub const Class = NewClass( - void, - .{ - .name = "TextEncoder", - }, - .{ - .constructor = constructor, - }, - .{}, - ); - - pub fn constructor( - ctx: js.JSContextRef, - _: js.JSObjectRef, - _: []const js.JSValueRef, - _: js.ExceptionRef, - ) js.JSObjectRef { - return TextEncoder.Class.make(ctx, &text_encoder); - } - }; + pub fn constructor( + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + _: js.ExceptionRef, + ) js.JSObjectRef { + return TextEncoder.Class.make(ctx, &text_encoder); + } }; /// https://encoding.spec.whatwg.org/encodings.json @@ -626,44 +621,35 @@ pub const TextDecoder = struct { } } - pub const Constructor = struct { - pub const Class = NewClass( - void, - .{ - .name = "TextDecoder", - }, - .{ - .constructor = constructor, - }, - .{}, - ); + pub const Constructor = JSC.NewConstructor(TextDecoder, .{ + .constructor = .{ .rfn = constructor }, + }, .{}); - pub fn constructor( - ctx: js.JSContextRef, - _: js.JSObjectRef, - args_: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSObjectRef { - var arguments: []const JSC.JSValue = @ptrCast([*]const JSC.JSValue, args_.ptr)[0..args_.len]; - var encoding = EncodingLabel.@"UTF-8"; - if (arguments.len > 0) { - if (!arguments[0].isString()) { - JSC.throwInvalidArguments("TextDecoder(encoding) label is invalid", .{}, ctx, exception); - return null; - } - - var str = arguments[0].toSlice(ctx.ptr(), default_allocator); - defer if (str.allocated) str.deinit(); - encoding = EncodingLabel.which(str.slice()) orelse { - JSC.throwInvalidArguments("Unsupported encoding label \"{s}\"", .{str.slice()}, ctx, exception); - return null; - }; + pub fn constructor( + ctx: js.JSContextRef, + _: js.JSObjectRef, + args_: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSObjectRef { + var arguments: []const JSC.JSValue = @ptrCast([*]const JSC.JSValue, args_.ptr)[0..args_.len]; + var encoding = EncodingLabel.@"UTF-8"; + if (arguments.len > 0) { + if (!arguments[0].isString()) { + JSC.throwInvalidArguments("TextDecoder(encoding) label is invalid", .{}, ctx, exception); + return null; } - var decoder = getAllocator(ctx).create(TextDecoder) catch unreachable; - decoder.* = TextDecoder{ .encoding = encoding }; - return TextDecoder.Class.make(ctx, decoder); + + var str = arguments[0].toSlice(ctx.ptr(), default_allocator); + defer if (str.allocated) str.deinit(); + encoding = EncodingLabel.which(str.slice()) orelse { + JSC.throwInvalidArguments("Unsupported encoding label \"{s}\"", .{str.slice()}, ctx, exception); + return null; + }; } - }; + var decoder = getAllocator(ctx).create(TextDecoder) catch unreachable; + decoder.* = TextDecoder{ .encoding = encoding }; + return TextDecoder.Class.make(ctx, decoder); + } }; test "Vec" {} diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 52dce05ef..0fa5507c7 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -42,11 +42,17 @@ const JSPrinter = @import("../../../js_printer.zig"); const picohttp = @import("picohttp"); const StringJoiner = @import("../../../string_joiner.zig"); pub const Response = struct { + pub const Constructor = JSC.NewConstructor( + Response, + .{ + .@"constructor" = constructor, + }, + .{}, + ); pub const Class = NewClass( Response, .{ .name = "Response" }, .{ - .@"constructor" = constructor, .@"finalize" = finalize, .@"text" = .{ .rfn = Response.getText, @@ -707,8 +713,7 @@ pub const Fetch = struct { } } } - } else if (Request.Class.loaded and first_arg.as(Request) != null) { - var request = first_arg.as(Request).?; + } else if (first_arg.asCheckLoaded(Request)) |request| { url = ZigURL.parse(request.url.dupe(getAllocator(ctx)) catch unreachable); method = request.method; if (request.headers) |head| { @@ -1052,6 +1057,16 @@ pub const Headers = struct { this.deref(); } }; + pub const Constructor = JSC.NewConstructor( + Headers, + .{ + .@"constructor" = .{ + .rfn = JS.constructor, + .ts = d.ts{}, + }, + }, + .{}, + ); pub const Class = NewClass( RefCountedHeaders, .{ @@ -1086,10 +1101,7 @@ pub const Headers = struct { .rfn = JS.values, .ts = d.ts{}, }, - .@"constructor" = .{ - .rfn = JS.constructor, - .ts = d.ts{}, - }, + .@"finalize" = .{ .rfn = JS.finalize, }, @@ -1484,11 +1496,18 @@ pub const Blob = struct { } }; + pub const Constructor = JSC.NewConstructor( + Blob, + .{ + .constructor = .{ .rfn = constructor }, + }, + .{}, + ); + pub const Class = NewClass( Blob, .{ .name = "Blob" }, .{ - .constructor = constructor, .finalize = finalize, .text = .{ .rfn = getText, @@ -2530,13 +2549,21 @@ pub const Request = struct { } } + pub const Constructor = JSC.NewConstructor( + Request, + .{ + .constructor = .{ .rfn = constructor }, + }, + .{}, + ); + pub const Class = NewClass( Request, .{ .name = "Request", .read_only = true, }, - .{ .finalize = finalize, .constructor = constructor, .text = .{ + .{ .finalize = finalize, .text = .{ .rfn = Request.getText, }, .json = .{ .rfn = Request.getJSON, @@ -2977,7 +3004,7 @@ pub const FetchEvent = struct { var globalThis = ctx.ptr(); // A Response or a Promise that resolves to a Response. Otherwise, a network error is returned to Fetch. - if (arguments.len == 0 or !Response.Class.loaded or !js.JSValueIsObject(ctx, arguments[0])) { + if (arguments.len == 0 or !Response.Class.isLoaded() or !js.JSValueIsObject(ctx, arguments[0])) { JSError(getAllocator(ctx), "event.respondWith() must be a Response or a Promise<Response>.", .{}, ctx, exception); request_context.sendInternalError(error.respondWithWasEmpty) catch {}; return js.JSValueMakeUndefined(ctx); @@ -2985,7 +3012,7 @@ pub const FetchEvent = struct { var arg = arguments[0]; - if (!js.JSValueIsObjectOfClass(ctx, arg, Response.Class.ref)) { + if (JSValue.fromRef(arg).as(Response) == null) { this.pending_promise = this.pending_promise orelse JSInternalPromise.resolvedPromise(globalThis, JSValue.fromRef(arguments[0])); } @@ -3010,19 +3037,10 @@ pub const FetchEvent = struct { arg = promise.result(ctx.ptr().vm()).asRef(); } - if (!js.JSValueIsObjectOfClass(ctx, arg, Response.Class.ref)) { - this.rejected = true; - this.pending_promise = null; - JSError(getAllocator(ctx), "event.respondWith() must be a Response or a Promise<Response>.", .{}, ctx, exception); - this.onPromiseRejectionHandler.?(this.onPromiseRejectionCtx, error.RespondWithInvalidType, this, JSValue.fromRef(exception.*)); - - return js.JSValueMakeUndefined(ctx); - } - var response: *Response = GetJSPrivateData(Response, arg) orelse { this.rejected = true; this.pending_promise = null; - JSError(getAllocator(ctx), "event.respondWith()'s Response object was invalid. This may be an internal error.", .{}, ctx, exception); + JSError(getAllocator(ctx), "event.respondWith() expects Response or Promise<Response>", .{}, ctx, exception); this.onPromiseRejectionHandler.?(this.onPromiseRejectionCtx, error.RespondWithInvalidTypeInternal, this, JSValue.fromRef(exception.*)); return js.JSValueMakeUndefined(ctx); }; |