diff options
author | 2021-06-30 02:38:23 -0700 | |
---|---|---|
committer | 2021-06-30 02:38:23 -0700 | |
commit | cb2ee39bfa27c1a1c223bbb0c135a03a9d3a4d85 (patch) | |
tree | 20352ce9bc2029d1e8ce490e408f0a37e300b4b8 | |
parent | 3f197d1ce0c197864ad4c7c7b8238af4370275b4 (diff) | |
download | bun-cb2ee39bfa27c1a1c223bbb0c135a03a9d3a4d85.tar.gz bun-cb2ee39bfa27c1a1c223bbb0c135a03a9d3a4d85.tar.zst bun-cb2ee39bfa27c1a1c223bbb0c135a03a9d3a4d85.zip |
latest
-rw-r--r-- | src/javascript/jsc/base.zig | 467 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 439 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 399 | ||||
-rw-r--r-- | src/resolver/resolver.zig | 17 | ||||
-rw-r--r-- | src/test/fixtures/console.log.js | 4 | ||||
-rw-r--r-- | src/test/fixtures/escape-chars.mjs (renamed from src/test/fixtures/escape-chars.js) | 0 | ||||
-rw-r--r-- | src/test/fixtures/export-check.mjs (renamed from src/test/fixtures/export-check.ts) | 2 | ||||
-rw-r--r-- | src/test/fixtures/quoted-escape.js | 2 |
8 files changed, 885 insertions, 445 deletions
diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig new file mode 100644 index 000000000..e9bc819ff --- /dev/null +++ b/src/javascript/jsc/base.zig @@ -0,0 +1,467 @@ +pub const js = @import("./JavaScriptCore.zig"); +const std = @import("std"); +pub usingnamespace @import("../../global.zig"); +const javascript = @import("./javascript.zig"); +pub const ExceptionValueRef = [*c]js.JSValueRef; +pub const JSValueRef = js.JSValueRef; + +pub const To = struct { + pub const JS = struct { + pub inline fn str(ref: anytype, val: anytype) js.JSStringRef { + return js.JSStringCreateWithUTF8CString(val[0.. :0]); + } + + pub fn functionWithCallback( + comptime ZigContextType: type, + zig: *ZigContextType, + name: js.JSStringRef, + ctx: js.JSContextRef, + comptime callback: fn ( + obj: *ZigContextType, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef, + ) js.JSObjectRef { + var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback).rfn); + _ = js.JSObjectSetPrivate( + function, + @ptrCast(*c_void, @alignCast(@alignOf(*c_void), zig)), + ); + return function; + } + + pub fn Callback( + comptime ZigContextType: type, + comptime ctxfn: fn ( + obj: *ZigContextType, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef, + ) type { + return struct { + pub fn rfn( + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + argumentCount: usize, + arguments: [*c]const js.JSValueRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { + var object_ptr_ = js.JSObjectGetPrivate(function); + if (object_ptr_ == null) { + object_ptr_ = js.JSObjectGetPrivate(thisObject); + } + + if (object_ptr_ == null) { + return js.JSValueMakeUndefined(ctx); + } + + var object_ptr = object_ptr_.?; + + return ctxfn( + @ptrCast(*ZigContextType, @alignCast(@alignOf(*ZigContextType), object_ptr)), + ctx, + function, + thisObject, + if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, + exception, + ); + } + }; + } + }; + + pub const Ref = struct { + pub inline fn str(ref: anytype) js.JSStringRef { + return @as(js.JSStringRef, ref); + } + }; + + pub const Zig = struct { + pub inline fn str(ref: anytype, buf: anytype) string { + return buf[0..js.JSStringGetUTF8CString(Ref.str(ref), buf.ptr, buf.len)]; + } + }; +}; + +pub const Properties = struct { + pub const UTF8 = struct { + pub const module = "module"; + pub const globalThis = "globalThis"; + pub const exports = "exports"; + pub const log = "log"; + pub const debug = "debug"; + pub const name = "name"; + pub const info = "info"; + pub const error_ = "error"; + pub const warn = "warn"; + pub const console = "console"; + pub const require = "require"; + pub const description = "description"; + pub const initialize_bundled_module = "$$m"; + pub const load_module_function = "$lOaDuRcOdE$"; + pub const window = "window"; + }; + + pub const UTF16 = struct { + pub const module: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.module); + pub const globalThis: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.globalThis); + pub const exports: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.exports); + pub const log: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.log); + pub const debug: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.debug); + pub const info: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.info); + pub const error_: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.error_); + pub const warn: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.warn); + pub const console: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.console); + pub const require: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.require); + pub const description: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.description); + pub const name: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.name); + pub const initialize_bundled_module = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.initialize_bundled_module); + pub const load_module_function: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.load_module_function); + pub const window: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.window); + }; + + pub const Refs = struct { + pub var module: js.JSStringRef = null; + pub var globalThis: js.JSStringRef = null; + pub var exports: js.JSStringRef = null; + pub var log: js.JSStringRef = null; + pub var debug: js.JSStringRef = null; + pub var info: js.JSStringRef = null; + pub var error_: js.JSStringRef = null; + pub var warn: js.JSStringRef = null; + pub var console: js.JSStringRef = null; + pub var require: js.JSStringRef = null; + pub var description: js.JSStringRef = null; + pub var name: js.JSStringRef = null; + pub var initialize_bundled_module: js.JSStringRef = null; + pub var load_module_function: js.JSStringRef = null; + pub var window: js.JSStringRef = null; + }; + + pub fn init() void { + inline for (std.meta.fieldNames(UTF8)) |name| { + @field(Refs, name) = js.JSStringRetain( + js.JSStringCreateWithCharactersNoCopy( + @field(StringStore.UTF16, name).ptr, + @field(StringStore.UTF16, name).len - 1, + ), + ); + + if (isDebug) { + std.debug.assert( + js.JSStringIsEqualToUTF8CString(@field(Refs, name), @field(UTF8, name)[0.. :0]), + ); + } + } + } +}; + +pub fn NewClass( + comptime ZigType: type, + comptime name: string, + comptime staticFunctions: anytype, + comptime properties: anytype, + comptime read_only: bool, + comptime singleton: bool, +) type { + return struct { + const ClassDefinitionCreator = @This(); + const function_names = std.meta.fieldNames(@TypeOf(staticFunctions)); + const names_buf = brk: { + var total_len: usize = 0; + for (function_names) |field, i| { + total_len += std.unicode.utf8ToUtf16LeStringLiteral(field).len; + } + var offset: usize = 0; + var names_buf_ = std.mem.zeroes([total_len]u16); + for (function_names) |field, i| { + var name_ = std.unicode.utf8ToUtf16LeStringLiteral(field); + std.mem.copy(u16, names_buf_[offset .. name_.len + offset], name_[0..]); + offset += name_.len; + } + break :brk names_buf_; + }; + const function_name_literals: [function_names.len][]const js.JSChar = brk: { + var names = std.mem.zeroes([function_names.len][]const js.JSChar); + var len: usize = 0; + for (function_names) |field, i| { + const end = len + std.unicode.utf8ToUtf16LeStringLiteral(field).len; + names[i] = names_buf[len..end]; + len = end; + } + break :brk names; + }; + var function_name_refs: [function_names.len]js.JSStringRef = undefined; + var class_name_str = name[0.. :0].ptr; + + const class_name_literal = std.unicode.utf8ToUtf16LeStringLiteral(name); + var static_functions: [function_name_refs.len + 1]js.JSStaticFunction = undefined; + var instance_functions: [function_names.len]js.JSObjectRef = undefined; + const property_names = std.meta.fieldNames(@TypeOf(properties)); + var property_name_refs: [property_names.len]js.JSStringRef = undefined; + const property_name_literals: [property_names.len][]const js.JSChar = brk: { + var list = std.mem.zeroes([property_names.len][]const js.JSChar); + for (property_names) |prop_name, i| { + list[i] = std.unicode.utf8ToUtf16LeStringLiteral(prop_name); + } + break :brk list; + }; + var static_properties: [property_names.len]js.JSStaticValue = undefined; + + pub fn getPropertyCallback( + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { + var instance_pointer_ = js.JSObjectGetPrivate(obj); + if (instance_pointer_ == null) return null; + var instance_pointer = instance_pointer_.?; + var ptr = @ptrCast( + *ZigType, + @alignCast( + @alignOf( + *ZigType, + ), + instance_pointer, + ), + ); + + if (singleton) { + inline for (function_names) |propname, i| { + if (js.JSStringIsEqual(prop, function_name_refs[i])) { + return instance_functions[i]; + } + } + if (comptime std.meta.trait.hasFn("onMissingProperty")(ZigType)) { + return ptr.onMissingProperty(ctx, obj, prop, exception); + } + } else { + inline for (property_names) |propname, i| { + if (js.JSStringIsEqual(prop, property_name_refs[i])) { + return @field( + properties, + propname, + )(ptr, ctx, obj, exception); + } + } + + if (comptime std.meta.trait.hasFn("onMissingProperty")(ZigType)) { + return ptr.onMissingProperty(ctx, obj, prop, exception); + } + } + + return js.JSValueMakeUndefined(ctx); + } + + fn StaticProperty(comptime id: usize) type { + return struct { + pub fn getter( + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSValueRef { + var instance_pointer_ = js.JSObjectGetPrivate(obj); + if (instance_pointer_ == null) return null; + var this: *ZigType = @ptrCast( + *ZigType, + @alignCast( + @alignOf( + *ZigType, + ), + instance_pointer_.?, + ), + ); + + var exc: js.ExceptionRef = null; + + switch (comptime @typeInfo(@TypeOf(@field( + properties, + property_names[id], + )))) { + .Fn => { + return @field( + properties, + property_names[id], + )( + this, + ctx, + obj, + exception, + ); + }, + .Struct => { + return @field( + @field( + properties, + property_names[id], + ), + "get", + )( + this, + ctx, + obj, + prop, + exception, + ); + }, + else => unreachable, + } + } + + pub fn setter( + ctx: js.JSContextRef, + obj: js.JSObjectRef, + prop: js.JSStringRef, + value: js.JSValueRef, + exception: js.ExceptionRef, + ) callconv(.C) bool { + var instance_pointer_ = js.JSObjectGetPrivate(obj); + if (instance_pointer_ == null) return false; + var this: *ZigType = @ptrCast( + *ZigType, + @alignCast( + @alignOf( + *ZigType, + ), + instance_pointer_.?, + ), + ); + + var exc: js.ExceptionRef = null; + + switch (comptime @typeInfo(@TypeOf(@field( + properties, + property_names[id], + )))) { + .Struct => { + return @field( + @field( + properties, + property_names[id], + ), + "set", + )( + this, + ctx, + obj, + prop, + value, + exception, + ); + }, + else => unreachable, + } + } + }; + } + + pub fn define() js.JSClassDefinition { + var def = js.kJSClassDefinitionEmpty; + + if (static_functions.len > 0) { + std.mem.set(js.JSStaticFunction, &static_functions, std.mem.zeroes(js.JSStaticFunction)); + var count: usize = 0; + inline for (function_name_literals) |function_name, i| { + if (comptime strings.eqlComptime(function_names[i], "constructor")) { + def.callAsConstructor = @field(staticFunctions, function_names[i]); + } else { + count += 1; + var callback = To.JS.Callback(ZigType, @field(staticFunctions, function_names[i])).rfn; + static_functions[count] = js.JSStaticFunction{ + .name = (function_names[i][0.. :0]).ptr, + .callAsFunction = callback, + .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, + }; + } + + // if (singleton) { + // var function = js.JSObjectMakeFunctionWithCallback(ctx, function_name_refs[i], callback); + // instance_functions[i] = function; + // } + } + + def.staticFunctions = static_functions[0..count].ptr; + } + + if (property_names.len > 0) { + inline for (comptime property_name_literals) |prop_name, i| { + property_name_refs[i] = js.JSStringCreateWithCharactersNoCopy( + prop_name.ptr, + prop_name.len, + ); + static_properties[i] = std.mem.zeroes(js.JSStaticValue); + static_properties[i].getProperty = StaticProperty(i).getter; + + const field = comptime @field(properties, property_names[i]); + const hasSetter = std.meta.trait.hasField("set"); + if (comptime hasSetter(@TypeOf(field))) { + static_properties[i].setProperty = StaticProperty(i).setter; + } + static_properties[i].name = property_names[i][0.. :0]; + } + + def.staticValues = (&static_properties); + } + + def.className = class_name_str; + // def.getProperty = getPropertyCallback; + + def.finalize + + return def; + } + }; +} + +threadlocal var error_args: [1]js.JSValueRef = undefined; +pub fn JSError( + allocator: *std.mem.Allocator, + comptime fmt: string, + args: anytype, + ctx: js.JSContextRef, + exception: ExceptionValueRef, +) void { + if (comptime std.meta.fields(@TypeOf(args)).len == 0) { + var message = js.JSStringCreateWithUTF8CString(fmt[0.. :0]); + defer js.JSStringRelease(message); + error_args[0] = js.JSValueMakeString(ctx, message); + exception.* = js.JSObjectMakeError(ctx, 1, &error_args, null); + } else { + var buf = std.fmt.allocPrintZ(allocator, fmt, args) catch unreachable; + var message = js.JSStringCreateWithUTF8CString(buf); + defer js.JSStringRelease(message); + defer allocator.free(buf); + error_args[0] = js.JSValueMakeString(ctx, message); + exception.* = js.JSObjectMakeError(ctx, 1, &error_args, null); + } +} + +pub fn getAllocator(ctx: js.JSContextRef) *std.mem.Allocator { + var global_obj = js.JSContextGetGlobalObject(ctx); + var priv = js.JSObjectGetPrivate(global_obj).?; + var global = @ptrCast(*javascript.GlobalObject, @alignCast(@alignOf(*javascript.GlobalObject), priv)); + + return global.vm.allocator; +} + +pub const JSStringList = std.ArrayList(js.JSStringRef); + +pub const ArrayBuffer = struct { + ptr: [*]u8 = undefined, + offset: u32, + // for the array type, + len: u32, + + byte_len: u32, + + typed_array_type: js.JSTypedArrayType, +}; diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 338e05490..52dcfff1c 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -1,11 +1,9 @@ -const js = @import("./JavaScriptCore.zig"); const std = @import("std"); -usingnamespace @import("../../global.zig"); + const Fs = @import("../../fs.zig"); const resolver = @import("../../resolver/resolver.zig"); const ast = @import("../../import_record.zig"); const NodeModuleBundle = @import("../../node_module_bundle.zig").NodeModuleBundle; -const WTFString = @import("../../wtf_string_mutable.zig").WTFStringMutable; const logger = @import("../../logger.zig"); const Api = @import("../../api/schema.zig").Api; const options = @import("../../options.zig"); @@ -13,35 +11,9 @@ const Bundler = @import("../../bundler.zig").ServeBundler; const js_printer = @import("../../js_printer.zig"); const hash_map = @import("../../hash_map.zig"); const http = @import("../../http.zig"); -usingnamespace @import("./node_env_buf_map.zig"); -pub const ExportJavaScript = union(Tag) { - Module: *Module, - String: *String, - GlobalObject: *GlobalObject, - - pub const Tag = enum { - Module, - String, - GlobalObject, - }; -}; - -pub const ResolveFunctionType = fn (ctx: anytype, source_dir: string, import_path: string, import_kind: ast.ImportKind) anyerror!resolver.Result; -pub const TranspileFunctionType = fn (ctx: anytype, resolve_result: resolver.Result) anyerror![:0]const u8; -pub const ExceptionValueRef = [*c]js.JSValueRef; -pub const JSValueRef = js.JSValueRef; -const JSStringMapContext = struct { - pub fn hash(self: @This(), s: js.JSStringRef) u64 { - return hashString(s); - } - pub fn eql(self: @This(), a: js.JSStringRef, b: js.JSStringRef) bool { - return eqlString(a, b); - } -}; -pub fn JSStringMap(comptime V: type) type { - return std.HashMap(js.JSStringRef, V, JSStringMapContext, 60); -} +usingnamespace @import("./node_env_buf_map.zig"); +usingnamespace @import("./base.zig"); const DefaultSpeedyDefines = struct { pub const Keys = struct { @@ -219,160 +191,7 @@ pub const VirtualMachine = struct { } }; -pub const To = struct { - pub const JS = struct { - pub inline fn str(ref: anytype, val: anytype) js.JSStringRef { - return js.JSStringCreateWithUTF8CString(val[0.. :0]); - } - - pub fn functionWithCallback( - comptime ZigContextType: type, - zig: *ZigContextType, - name: js.JSStringRef, - ctx: js.JSContextRef, - comptime callback: fn ( - obj: *ZigContextType, - ctx: js.JSContextRef, - function: js.JSObjectRef, - thisObject: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef, - ) js.JSObjectRef { - var function = js.JSObjectMakeFunctionWithCallback(ctx, name, Callback(ZigContextType, callback).rfn); - _ = js.JSObjectSetPrivate( - function, - @ptrCast(*c_void, @alignCast(@alignOf(*c_void), zig)), - ); - return function; - } - - pub fn Callback( - comptime ZigContextType: type, - comptime ctxfn: fn ( - obj: *ZigContextType, - ctx: js.JSContextRef, - function: js.JSObjectRef, - thisObject: js.JSObjectRef, - arguments: []const js.JSValueRef, - exception: js.ExceptionRef, - ) js.JSValueRef, - ) type { - return struct { - pub fn rfn( - ctx: js.JSContextRef, - function: js.JSObjectRef, - thisObject: js.JSObjectRef, - argumentCount: usize, - arguments: [*c]const js.JSValueRef, - exception: js.ExceptionRef, - ) callconv(.C) js.JSValueRef { - var object_ptr_ = js.JSObjectGetPrivate(function); - if (object_ptr_ == null) { - object_ptr_ = js.JSObjectGetPrivate(thisObject); - } - - if (object_ptr_ == null) { - return js.JSValueMakeUndefined(ctx); - } - - var object_ptr = object_ptr_.?; - - return ctxfn( - @ptrCast(*ZigContextType, @alignCast(@alignOf(*ZigContextType), object_ptr)), - ctx, - function, - thisObject, - if (arguments) |args| args[0..argumentCount] else &[_]js.JSValueRef{}, - exception, - ); - } - }; - } - }; - - pub const Ref = struct { - pub inline fn str(ref: anytype) js.JSStringRef { - return @as(js.JSStringRef, ref); - } - }; - - pub const Zig = struct { - pub inline fn str(ref: anytype, buf: anytype) string { - return buf[0..js.JSStringGetUTF8CString(Ref.str(ref), buf.ptr, buf.len)]; - } - }; -}; - -pub const Properties = struct { - pub const UTF8 = struct { - pub const module = "module"; - pub const globalThis = "globalThis"; - pub const exports = "exports"; - pub const log = "log"; - pub const debug = "debug"; - pub const name = "name"; - pub const info = "info"; - pub const error_ = "error"; - pub const warn = "warn"; - pub const console = "console"; - pub const require = "require"; - pub const description = "description"; - pub const initialize_bundled_module = "$$m"; - pub const load_module_function = "$lOaDuRcOdE$"; - }; - - pub const UTF16 = struct { - pub const module: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.module); - pub const globalThis: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.globalThis); - pub const exports: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.exports); - pub const log: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.log); - pub const debug: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.debug); - pub const info: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.info); - pub const error_: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.error_); - pub const warn: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.warn); - pub const console: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.console); - pub const require: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.require); - pub const description: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.description); - pub const name: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.name); - pub const initialize_bundled_module = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.initialize_bundled_module); - pub const load_module_function: []c_ushort = std.unicode.utf8ToUtf16LeStringLiteral(UTF8.load_module_function); - }; - - pub const Refs = struct { - pub var module: js.JSStringRef = null; - pub var globalThis: js.JSStringRef = null; - pub var exports: js.JSStringRef = null; - pub var log: js.JSStringRef = null; - pub var debug: js.JSStringRef = null; - pub var info: js.JSStringRef = null; - pub var error_: js.JSStringRef = null; - pub var warn: js.JSStringRef = null; - pub var console: js.JSStringRef = null; - pub var require: js.JSStringRef = null; - pub var description: js.JSStringRef = null; - pub var name: js.JSStringRef = null; - pub var initialize_bundled_module: js.JSStringRef = null; - pub var load_module_function: js.JSStringRef = null; - }; - pub fn init() void { - inline for (std.meta.fieldNames(UTF8)) |name| { - @field(Refs, name) = js.JSStringRetain( - js.JSStringCreateWithCharactersNoCopy( - @field(StringStore.UTF16, name).ptr, - @field(StringStore.UTF16, name).len - 1, - ), - ); - - if (isDebug) { - std.debug.assert( - js.JSStringIsEqualToUTF8CString(@field(Refs, name), @field(UTF8, name)[0.. :0]), - ); - } - } - } -}; pub const Object = struct { ref: js.jsObjectRef, @@ -1535,255 +1354,3 @@ pub const GlobalObject = struct { // js.JSObjectMakeFunctionWithCallback(ctx: JSContextRef, name: JSStringRef, callAsFunction: JSObjectCallAsFunctionCallback) } }; - -pub fn NewClass( - comptime ZigType: type, - comptime name: string, - comptime staticFunctions: anytype, - comptime properties: anytype, - comptime read_only: bool, - comptime singleton: bool, -) type { - return struct { - const ClassDefinitionCreator = @This(); - const function_names = std.meta.fieldNames(@TypeOf(staticFunctions)); - const names_buf = brk: { - var total_len: usize = 0; - for (function_names) |field, i| { - total_len += std.unicode.utf8ToUtf16LeStringLiteral(field).len; - } - var offset: usize = 0; - var names_buf_ = std.mem.zeroes([total_len]u16); - for (function_names) |field, i| { - var name_ = std.unicode.utf8ToUtf16LeStringLiteral(field); - std.mem.copy(u16, names_buf_[offset .. name_.len + offset], name_[0..]); - offset += name_.len; - } - break :brk names_buf_; - }; - const function_name_literals: [function_names.len][]const js.JSChar = brk: { - var names = std.mem.zeroes([function_names.len][]const js.JSChar); - var len: usize = 0; - for (function_names) |field, i| { - const end = len + std.unicode.utf8ToUtf16LeStringLiteral(field).len; - names[i] = names_buf[len..end]; - len = end; - } - break :brk names; - }; - var function_name_refs: [function_names.len]js.JSStringRef = undefined; - var class_name_str = name[0.. :0].ptr; - - const class_name_literal = std.unicode.utf8ToUtf16LeStringLiteral(name); - var static_functions: [function_name_refs.len + 1]js.JSStaticFunction = undefined; - var instance_functions: [function_names.len]js.JSObjectRef = undefined; - const property_names = std.meta.fieldNames(@TypeOf(properties)); - var property_name_refs: [property_names.len]js.JSStringRef = undefined; - const property_name_literals: [property_names.len][]const js.JSChar = brk: { - var list = std.mem.zeroes([property_names.len][]const js.JSChar); - for (property_names) |prop_name, i| { - list[i] = std.unicode.utf8ToUtf16LeStringLiteral(prop_name); - } - break :brk list; - }; - var static_properties: [property_names.len]js.JSStaticValue = undefined; - - pub fn getPropertyCallback( - ctx: js.JSContextRef, - obj: js.JSObjectRef, - prop: js.JSStringRef, - exception: js.ExceptionRef, - ) callconv(.C) js.JSValueRef { - var instance_pointer_ = js.JSObjectGetPrivate(obj); - if (instance_pointer_ == null) return null; - var instance_pointer = instance_pointer_.?; - var ptr = @ptrCast( - *ZigType, - @alignCast( - @alignOf( - *ZigType, - ), - instance_pointer, - ), - ); - - if (singleton) { - inline for (function_names) |propname, i| { - if (js.JSStringIsEqual(prop, function_name_refs[i])) { - return instance_functions[i]; - } - } - if (comptime std.meta.trait.hasFn("onMissingProperty")(ZigType)) { - return ptr.onMissingProperty(ctx, obj, prop, exception); - } - } else { - inline for (property_names) |propname, i| { - if (js.JSStringIsEqual(prop, property_name_refs[i])) { - return @field( - properties, - propname, - )(ptr, ctx, obj, exception); - } - } - - if (comptime std.meta.trait.hasFn("onMissingProperty")(ZigType)) { - return ptr.onMissingProperty(ctx, obj, prop, exception); - } - } - - return js.JSValueMakeUndefined(ctx); - } - - fn StaticProperty(comptime id: usize) type { - return struct { - pub fn getter( - ctx: js.JSContextRef, - obj: js.JSObjectRef, - prop: js.JSStringRef, - exception: js.ExceptionRef, - ) callconv(.C) js.JSValueRef { - var instance_pointer_ = js.JSObjectGetPrivate(obj); - if (instance_pointer_ == null) return null; - var this: *ZigType = @ptrCast( - *ZigType, - @alignCast( - @alignOf( - *ZigType, - ), - instance_pointer_.?, - ), - ); - - var exc: js.ExceptionRef = null; - - switch (comptime @typeInfo(@TypeOf(@field( - properties, - property_names[id], - )))) { - .Fn => { - return @field( - properties, - property_names[id], - )( - this, - ctx, - obj, - exception, - ); - }, - .Struct => { - return @field( - @field( - properties, - property_names[id], - ), - "get", - )( - this, - ctx, - obj, - prop, - exception, - ); - }, - else => unreachable, - } - } - - pub fn setter( - ctx: js.JSContextRef, - obj: js.JSObjectRef, - prop: js.JSStringRef, - value: js.JSValueRef, - exception: js.ExceptionRef, - ) callconv(.C) bool { - var instance_pointer_ = js.JSObjectGetPrivate(obj); - if (instance_pointer_ == null) return false; - var this: *ZigType = @ptrCast( - *ZigType, - @alignCast( - @alignOf( - *ZigType, - ), - instance_pointer_.?, - ), - ); - - var exc: js.ExceptionRef = null; - - switch (comptime @typeInfo(@TypeOf(@field( - properties, - property_names[id], - )))) { - .Struct => { - return @field( - @field( - properties, - property_names[id], - ), - "set", - )( - this, - ctx, - obj, - prop, - value, - exception, - ); - }, - else => unreachable, - } - } - }; - } - - pub fn define() js.JSClassDefinition { - var def = js.kJSClassDefinitionEmpty; - - if (static_functions.len > 0) { - std.mem.set(js.JSStaticFunction, &static_functions, std.mem.zeroes(js.JSStaticFunction)); - - inline for (function_name_literals) |function_name, i| { - var callback = To.JS.Callback(ZigType, @field(staticFunctions, function_names[i])).rfn; - - static_functions[i] = js.JSStaticFunction{ - .name = (function_names[i][0.. :0]).ptr, - .callAsFunction = callback, - .attributes = comptime if (read_only) js.JSPropertyAttributes.kJSPropertyAttributeReadOnly else js.JSPropertyAttributes.kJSPropertyAttributeNone, - }; - // if (singleton) { - // var function = js.JSObjectMakeFunctionWithCallback(ctx, function_name_refs[i], callback); - // instance_functions[i] = function; - // } - } - - def.staticFunctions = &static_functions; - } - - if (property_names.len > 0) { - inline for (comptime property_name_literals) |prop_name, i| { - property_name_refs[i] = js.JSStringCreateWithCharactersNoCopy( - prop_name.ptr, - prop_name.len, - ); - static_properties[i] = std.mem.zeroes(js.JSStaticValue); - static_properties[i].getProperty = StaticProperty(i).getter; - - const field = comptime @field(properties, property_names[i]); - const hasSetter = std.meta.trait.hasField("set"); - if (comptime hasSetter(@TypeOf(field))) { - static_properties[i].setProperty = StaticProperty(i).setter; - } - static_properties[i].name = property_names[i][0.. :0]; - } - - def.staticValues = (&static_properties); - } - - def.className = class_name_str; - // def.getProperty = getPropertyCallback; - - return def; - } - }; -} diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig new file mode 100644 index 000000000..580714da8 --- /dev/null +++ b/src/javascript/jsc/webcore/response.zig @@ -0,0 +1,399 @@ +usingnamespace @import("../base.zig"); +const std = @import("std"); +const Api = @import("../../../api/schema.zig").Api; +const http = @import("../../../http.zig"); + +pub const Response = struct { + pub const Class = NewClass( + Response, + "Response", + .{ + .@"constructor" = constructor, + }, + .{ + // .@"url" = .{ + // .@"get" = getURL, + // .ro = true, + // }, + .@"ok" = .{ + .@"get" = getOK, + .ro = true, + }, + .@"status" = .{ + .@"get" = getStatus, + .ro = true, + }, + }, + false, + false, + ); + + pub var class_definition: js.JSClassDefinition = undefined; + pub var class_ref: js.JSClassRef = undefined; + pub var loaded = false; + + pub fn load() void { + if (!loaded) { + class_definition = Class.define(); + class_ref = js.JSClassRetain(js.JSClassCreate(&class_definition)); + loaded = true; + } + } + allocator: *std.mem.Allocator, + body: Body, + + pub const Props = struct {}; + + pub fn getOK( + this: *Response, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + // https://developer.mozilla.org/en-US/docs/Web/API/Response/ok + return js.JSValueMakeBoolean(ctx, this.body.init.status_code >= 200 and this.body.init.status_code <= 299); + } + + pub fn getStatus( + this: *Response, + ctx: js.JSContextRef, + thisObject: js.JSValueRef, + prop: js.JSStringRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + // https://developer.mozilla.org/en-US/docs/Web/API/Response/status + return js.JSValueMakeNumber(ctx, @intToFloat(f64, this.body.init.status_code)); + } + + pub fn finalize( + this: *Response, + ctx: js.JSContextRef, + ) void { + this.body.deinit(this.allocator); + this.allocator.destroy(this); + } + + pub fn constructor( + ctx: js.JSContextRef, + function: js.JSObjectRef, + arguments_len: usize, + arguments_ptr: [*c]const js.JSValueRef, + exception: js.ExceptionRef, + ) callconv(.C) js.JSObjectRef { + const arguments = arguments_ptr[0..arguments_len]; + + const body = brk: { + switch (arguments.len) { + 0 => { + break :brk Body.@"404"(ctx); + }, + 1 => { + break :brk Body.extract(ctx, arguments[0], exception); + }, + else => { + if (js.JSValueGetType(ctx, arguments[1]) == js.JSType.kJSTypeObject) { + break :brk Body.extractWithInit(ctx, arguments[0], arguments[1], exception); + } else { + break :brk Body.extract(ctx, arguments[0], exception); + } + }, + } + unreachable; + }; + + if (exception != null) { + return null; + } + + var allocator = getAllocator(ctx); + var response = allocator.create(Response) catch return null; + + response.* = Response{ + .body = body, + .allocator = allocator, + }; + return js.JSObjectMake(ctx, class_ref, response); + } +}; + +pub const Headers = struct { + pub const Kv = struct { + key: Api.StringPointer, + value: Api.StringPointer, + }; + pub const Entries = std.MultiArrayList(Kv); + entries: Entries, + buf: std.ArrayListUnmanaged(u8), + allocator: *std.mem.Allocator, + used: usize = 0, + + fn appendString(this: *Headers, str: js.JSStringRef, comptime needs_lowercase: bool) Api.StringPointer { + const ptr = Api.StringPointer{ .offset = this.used, .length = js.JSStringGetLength(str) }; + std.debug.assert(ptr.length > 0); + std.debug.assert(this.buf.items.len >= ptr.offset + ptr.length); + var slice = this.buf.items[ptr.offset][0..ptr.length]; + ptr.length = js.JSStringGetUTF8CString(this, slice.ptr, slice.len); + if (needs_lowercase) { + for (slice) |c, i| { + slice[i] = std.ascii.toLower(c); + } + } + + this.used += ptr.len; + return ptr; + } + + fn appendNumber(this: *Headers, num: f64) Api.StringPointer { + const ptr = Api.StringPointer{ .offset = this.used, .length = std.fmt.count("{d}", num) }; + std.debug.assert(this.buf.items.len >= ptr.offset + ptr.length); + var slice = this.buf.items[ptr.offset][0..ptr.length]; + ptr.length = std.fmt.bufPrint(slice, "{d}", num) catch 0; + this.used += ptr.len; + return ptr; + } + + pub fn append(this: *Headers, ctx: js.JSContextRef, key: js.JSStringRef, comptime value_type: js.JSType, value: js.JSValueRef) !void { + this.entries.append(this.allocator, Kv{ + .key = this.appendString(key, true), + .value = switch (comptime value_type) { + js.JSType.kJSTypeNumber => this.appendNumber(js.JSValueToNumber(ctx, value, null)), + js.JSType.kJSTypeString => this.appendString(value, false), + }, + }); + } +}; + +// https://developer.mozilla.org/en-US/docs/Web/API/Body +pub const Body = struct { + init: Init, + value: Value, + + pub fn deinit(this: *Body, allocator: *std.mem.Allocator) void { + if (this.init.headers) |headers| { + headers.buf.deinit(headers.allocator); + headers.entries.deinit(headers.allocator); + } + + switch (this.value) { + .ArrayBuffer => {}, + .String => |str| { + allocator.free(str); + }, + .Empty => {}, + } + } + + pub const Init = struct { + headers: ?Headers, + status_code: u16, + + pub fn init(allocator: *std.mem.Allocator, ctx: js.JSContextRef, init_ref: js.JSValueRef) !?Init { + var result = Init{ .headers = null, .status_code = 0 }; + var array = js.JSObjectCopyPropertyNames(ctx, init_ref); + defer js.JSPropertyNameArrayRelease(array); + const count = js.JSPropertyNameArrayGetCount(array); + var i: usize = 0; + upper: while (i < count) : (i += 1) { + var property_name_ref = js.JSPropertyNameArrayGetNameAtIndex(array, i); + switch (js.JSStringGetLength(property_name_ref)) { + "headers".len => { + if (js.JSStringIsEqualToUTF8CString(property_name_ref, "headers")) { + // only support headers as an object for now. + if (js.JSObjectGetProperty(ctx, init_ref, property_name_ref, null)) |header_prop| { + switch (js.JSValueGetType(ctx, header_prop)) { + js.JSType.kJSTypeObject => { + const header_keys = js.JSObjectCopyPropertyNames(ctx, header_prop); + defer js.JSPropertyNameArrayRelease(header_keys); + const total_header_count = js.JSPropertyNameArrayGetCount(array); + if (total_header_count == 0) continue :upper; + + // 2 passes through the headers + + // Pass #1: find the "real" count. + // The number of things which are strings or numbers. + // Anything else should be ignored. + // We could throw a TypeError, but ignoring silently is more JavaScript-like imo + var real_header_count: usize = 0; + var estimated_buffer_len: usize = 0; + var j: usize = 0; + while (j < total_header_count) : (j += 1) { + var key_ref = js.JSPropertyNameArrayGetNameAtIndex(j); + var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); + + switch (js.JSValueGetType(ctx, value_ref)) { + js.JSType.kJSTypeNumber => { + const key_len = js.JSStringGetLength(key_ref); + if (key_len > 0) { + real_header_count += 1; + estimated_buffer_len += key_len; + estimated_buffer_len += std.fmt.count("{d}", .{js.JSValueToNumber(ctx, value_ref, null)}); + } + }, + js.JSType.kJSTypeString => { + const value_len = js.JSStringGetLength(value_ref); + if (key_len > 0 and value_len > 0) { + real_header_count += 1; + estimated_buffer_len += key_len + value_len; + } + }, + else => {}, + } + } + + if (real_header_count == 0 or estimated_buffer_len == 0) continue :upper; + + j = 0; + var headers = Headers{ + .buf = try std.ArrayList(u8).initCapacity(allocator, estimated_buffer_len), + .entries = std.mem.zeroes(Headers.Entries), + }; + errdefer headers.deinit(); + try headers.entries.ensureTotalCapacity(allocator, real_header_count); + + while (j < total_header_count) : (j += 1) { + var key_ref = js.JSPropertyNameArrayGetNameAtIndex(j); + var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); + + switch (js.JSValueGetType(ctx, value_ref)) { + js.JSType.kJSTypeNumber => { + if (js.JSStringGetLength(key_ref) == 0) continue; + try headers.append(ctx, key_ref, .kJSTypeNumber, value_ref); + }, + js.JSType.kJSTypeString => { + if (js.JSStringGetLength(value_ref) == 0 or js.JSStringGetLength(key_ref) == 0) continue; + try headers.append(ctx, key_ref, .kJSTypeString, value_ref); + }, + else => {}, + } + } + result.headers = headers; + }, + else => {}, + } + } + } + }, + "statusCode".len => { + if (js.JSStringIsEqualToUTF8CString(property_name_ref, "statusCode")) { + var value_ref = js.JSObjectGetProperty(ctx, header_prop, key_ref, null); + var exception: js.JSValueRef = null; + const number = js.JSValueToNumber(ctx, value_ref, &exception); + if (exception != null or !std.math.isFinite(number)) continue; + result.status_code = @truncate(u16, @floatToInt(u64, number)); + } + }, + else => {}, + } + } + + if (result.headers == null and result.status_code < 200) return null; + return result; + } + }; + pub const Value = union(Tag) { + ArrayBuffer: ArrayBuffer, + String: string, + Empty: u0, + pub const Tag = enum { + ArrayBuffer, + String, + Empty, + }; + }; + + pub fn @"404"(ctx: js.JSContextRef) Body { + return Body{ .init = Init{ + .headers = null, + .status_code = 404, + }, .value = .{ .Empty = 0 } }; + } + + pub fn extract(ctx: js.JSContextRef, body_ref: js.JSObjectRef, exception: ExceptionValueRef) Body { + return extractBody(ctx, body_ref, false, null); + } + + pub fn extractWithInit(ctx: js.JSContextRef, body_ref: js.JSObjectRef, init_ref: js.JSValueRef, exception: ExceptionValueRef) Body { + return extractBody(ctx, body_ref, true, init_ref); + } + + // https://github.com/WebKit/webkit/blob/main/Source/WebCore/Modules/fetch/FetchBody.cpp#L45 + inline fn extractBody( + ctx: js.JSContextRef, + body_ref: js.JSObjectRef, + comptime has_init: bool, + init_ref: js.JSValueRef, + exception: ExceptionValueRef, + ) Body { + var body = Body{ .init = Init{ .headers = null, .status_code = 200 }, .value = .{ .Empty = 0 } }; + + switch (js.JSValueGetType(ctx, body_ref)) { + js.kJSTypeString => { + if (exception == null) { + var allocator = getAllocator(ctx); + + if (has_init) { + body.init = Init.init(allocator, ctx, init_ref.?) catch unreachable; + } + const len = js.JSStringGetLength(body_ref); + if (len == 0) { + body.value = .{ .String = "" }; + return body; + } + + var str = try allocator.alloc(u8, len); + + body.value = Value{ .String = str[0..js.JSStringGetUTF8CString(body_ref, str.ptr, len)] }; + return body; + } + }, + js.kJSTypeObject => { + const typed_array = js.JSValueGetTypedArrayType(ctx, body_ref, exception); + switch (typed_array) { + js.JSTypedArrayType.kJSTypedArrayTypeNone => {}, + else => { + const buffer = ArrayBuffer{ + .ptr = js.JSObjectGetTypedArrayBytesPtr(ctx, body_ref, exception), + .offset = js.JSObjectGetTypedArrayByteOffset(ctx, body_ref, exception), + .len = js.JSObjectGetTypedArrayLength(ctx, body_ref, exception), + .byte_len = js.JSObjectGetTypedArrayLength(ctx, body_ref, exception), + .typed_array_type = typed_array, + }; + if (exception == null) { + if (has_init) { + body.init = Init.init(allocator, ctx, init_ref.?) catch unreachable; + } + body.value = Value{ .ArrayBuffer = buffer }; + return body; + } + }, + } + }, + else => {}, + } + + if (exception == null) { + JSError(getAllocator(allocator), "Body must be a string or a TypedArray (for now)", .{}, ctx, exception); + } + + return null; + } +}; + +pub const FetchEvent = struct { + started_waiting_at: u64 = 0, + response: ?*Response = null, + request_context: *http.RequestContext, + + pub const Class = NewClass( + FetchEvent, + "FetchEvent", + .{ .@"respondWith" = respondWith, .@"waitUntil" = waitUntil }, + .{ + .@"client" = getClient, + .@"request" = getRequest, + }, + true, + false, + ); +}; diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index 41a39b273..d27c3b0a6 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -729,8 +729,6 @@ pub fn NewResolver(cache_files: bool) type { if (remapped.len == 0) { path.is_disabled = true; } else if (r.resolveWithoutRemapping(dir_info, remapped, kind)) |remapped_result| { - result.is_from_node_modules = remapped_result.is_node_module; - switch (iter.index) { 0 => { result.path_pair.primary = remapped_result.path_pair.primary; @@ -893,10 +891,17 @@ pub fn NewResolver(cache_files: bool) type { } pub fn parsePackageJSON(r: *ThisResolver, file: string, dirname_fd: StoredFileDescriptorType) !?*PackageJSON { - const pkg = PackageJSON.parse(ThisResolver, r, file, dirname_fd, !cache_files) orelse return null; - var _pkg = try r.allocator.create(PackageJSON); - _pkg.* = pkg; - return _pkg; + if (!cache_files or r.opts.node_modules_bundle != null) { + const pkg = PackageJSON.parse(ThisResolver, r, file, dirname_fd, true) orelse return null; + var _pkg = try r.allocator.create(PackageJSON); + _pkg.* = pkg; + return _pkg; + } else { + const pkg = PackageJSON.parse(ThisResolver, r, file, dirname_fd, false) orelse return null; + var _pkg = try r.allocator.create(PackageJSON); + _pkg.* = pkg; + return _pkg; + } } fn dirInfoCached(r: *ThisResolver, path: string) !?*DirInfo { diff --git a/src/test/fixtures/console.log.js b/src/test/fixtures/console.log.js index 353895b01..cb95413ed 100644 --- a/src/test/fixtures/console.log.js +++ b/src/test/fixtures/console.log.js @@ -1,3 +1,3 @@ -import { isJavaScriptCore } from "./export-check"; +import React from "react"; -console.log("Is this JavaScriptCore?", isJavaScriptCore); +console.log("Is this JavaScriptCore?", JSON.stringify(<div>hello</div>)); diff --git a/src/test/fixtures/escape-chars.js b/src/test/fixtures/escape-chars.mjs index cff9b60a2..cff9b60a2 100644 --- a/src/test/fixtures/escape-chars.js +++ b/src/test/fixtures/escape-chars.mjs diff --git a/src/test/fixtures/export-check.ts b/src/test/fixtures/export-check.mjs index 6e32acd50..6613308d9 100644 --- a/src/test/fixtures/export-check.ts +++ b/src/test/fixtures/export-check.mjs @@ -1,2 +1,2 @@ -export const isJavaScriptCore: boolean = +export const isJavaScriptCore = !("process" in globalThis) && !("location" in globalThis); diff --git a/src/test/fixtures/quoted-escape.js b/src/test/fixtures/quoted-escape.js new file mode 100644 index 000000000..17716fa12 --- /dev/null +++ b/src/test/fixtures/quoted-escape.js @@ -0,0 +1,2 @@ +// bug: doesn't properly choose best escape quote +export default "foo" + '=""'; |