diff options
-rw-r--r-- | src/bun.js/bindings/ModuleLoader.cpp | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/ScriptExecutionContext.cpp | 14 | ||||
-rw-r--r-- | src/js_ast.zig | 157 | ||||
-rw-r--r-- | src/js_parser.zig | 38 |
4 files changed, 160 insertions, 51 deletions
diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index 40e41b083..75981f5b3 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -457,7 +457,7 @@ static JSValue fetchSourceCode( } default: { auto provider = Zig::SourceProvider::create(res->result.value); - return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(provider))); + return rejectOrResolve(JSC::JSSourceCode::create(vm, JSC::SourceCode(WTFMove(provider)))); } } } diff --git a/src/bun.js/bindings/ScriptExecutionContext.cpp b/src/bun.js/bindings/ScriptExecutionContext.cpp index 08e8e11ef..e8cae5e33 100644 --- a/src/bun.js/bindings/ScriptExecutionContext.cpp +++ b/src/bun.js/bindings/ScriptExecutionContext.cpp @@ -10,7 +10,7 @@ extern "C" void Bun__startLoop(us_loop_t* loop); namespace WebCore { -static unsigned lastUniqueIdentifier = 0; +static std::atomic<unsigned> lastUniqueIdentifier = 0; static Lock allScriptExecutionContextsMapLock; static HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>& allScriptExecutionContextsMap() WTF_REQUIRES_LOCK(allScriptExecutionContextsMapLock) @@ -41,7 +41,7 @@ us_socket_context_t* ScriptExecutionContext::webSocketContextSSL() us_bun_socket_context_options_t opts; memset(&opts, 0, sizeof(us_bun_socket_context_options_t)); // adds root ca - opts.request_cert = true; + opts.request_cert = true; // but do not reject unauthorized opts.reject_unauthorized = false; this->m_ssl_client_websockets_ctx = us_create_bun_socket_context(1, loop, sizeof(size_t), opts); @@ -108,12 +108,12 @@ void ScriptExecutionContext::regenerateIdentifier() { Locker locker { allScriptExecutionContextsMapLock }; - ASSERT(allScriptExecutionContextsMap().contains(m_identifier)); - allScriptExecutionContextsMap().remove(m_identifier); + // ASSERT(allScriptExecutionContextsMap().contains(m_identifier)); + // allScriptExecutionContextsMap().remove(m_identifier); m_identifier = ++lastUniqueIdentifier; - ASSERT(!allScriptExecutionContextsMap().contains(m_identifier)); + // ASSERT(!allScriptExecutionContextsMap().contains(m_identifier)); allScriptExecutionContextsMap().add(m_identifier, this); } @@ -121,14 +121,14 @@ void ScriptExecutionContext::addToContextsMap() { Locker locker { allScriptExecutionContextsMapLock }; ASSERT(!allScriptExecutionContextsMap().contains(m_identifier)); - allScriptExecutionContextsMap().add(m_identifier, this); + // allScriptExecutionContextsMap().add(m_identifier, this); } void ScriptExecutionContext::removeFromContextsMap() { Locker locker { allScriptExecutionContextsMapLock }; ASSERT(allScriptExecutionContextsMap().contains(m_identifier)); - allScriptExecutionContextsMap().remove(m_identifier); + // allScriptExecutionContextsMap().remove(m_identifier); } } diff --git a/src/js_ast.zig b/src/js_ast.zig index 133ae9aa5..9bf76a37f 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -1435,21 +1435,16 @@ pub const E = struct { return ExprNodeList.init(out[0 .. out.len - remain.len]); } - pub fn toJS(this: @This(), ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef { - var stack = std.heap.stackFallback(32 * @sizeOf(ExprNodeList), JSC.getAllocator(ctx)); - var allocator = stack.get(); - var results = allocator.alloc(JSC.C.JSValueRef, this.items.len) catch { - return JSC.C.JSValueMakeUndefined(ctx); - }; - defer if (stack.fixed_buffer_allocator.end_index >= stack.fixed_buffer_allocator.buffer.len - 1) allocator.free(results); - - var i: usize = 0; + pub fn toJS(this: @This(), allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { const items = this.items.slice(); - while (i < results.len) : (i += 1) { - results[i] = items[i].toJS(ctx, exception); + var array = JSC.JSValue.createEmptyArray(globalObject, items.len); + array.protect(); + defer array.unprotect(); + for (items, 0..) |expr, j| { + array.putIndex(globalObject, @truncate(u32, j), try expr.data.toJS(allocator, globalObject)); } - return JSC.C.JSObjectMakeArray(ctx, results.len, results.ptr, exception); + return array; } }; @@ -1762,8 +1757,8 @@ pub const E = struct { return try std.json.stringify(self.value, opts, o); } - pub fn toJS(this: @This(), _: JSC.C.JSContextRef, _: JSC.C.ExceptionRef) JSC.C.JSValueRef { - return JSC.JSValue.jsNumber(this.value).asObjectRef(); + pub fn toJS(this: @This()) JSC.JSValue { + return JSC.JSValue.jsNumber(this.value); } }; @@ -1776,7 +1771,7 @@ pub const E = struct { return try std.json.stringify(self.value, opts, o); } - pub fn toJS(_: @This(), _: JSC.C.JSContextRef, _: JSC.C.ExceptionRef) JSC.C.JSValueRef { + pub fn toJS(_: @This()) JSC.JSValue { // TODO: return JSC.JSValue.jsNumber(0); } @@ -1840,6 +1835,22 @@ pub const E = struct { return if (asProperty(self, key)) |query| query.expr else @as(?Expr, null); } + pub fn toJS(this: *Object, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { + var obj = JSC.JSValue.createEmptyObject(globalObject, this.properties.len); + obj.protect(); + defer obj.unprotect(); + const props: []const G.Property = this.properties.slice(); + for (props) |prop| { + if (prop.kind != .normal or prop.class_static_block != null or prop.key == null or prop.key.?.data != .e_string or prop.value == null) { + return error.@"Cannot convert argument type to JS"; + } + var key = prop.key.?.data.e_string.toZigString(allocator); + obj.put(globalObject, &key, try prop.value.?.toJS(allocator, globalObject)); + } + + return obj; + } + pub fn put(self: *Object, allocator: std.mem.Allocator, key: string, expr: Expr) !void { if (asProperty(self, key)) |query| { self.properties.ptr[query.i].value = expr; @@ -2339,6 +2350,18 @@ pub const E = struct { } } + pub fn toJS(s: *String, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + return s.toZigString(allocator).toValueGC(globalObject); + } + + pub fn toZigString(s: *String, allocator: std.mem.Allocator) JSC.ZigString { + if (s.isUTF8()) { + return JSC.ZigString.fromUTF8(s.slice(allocator)); + } else { + return JSC.ZigString.init16(s.slice16()); + } + } + pub fn jsonStringify(s: *const String, options: anytype, writer: anytype) !void { var buf = [_]u8{0} ** 4096; var i: usize = 0; @@ -3090,8 +3113,8 @@ pub const Expr = struct { return false; } - pub fn toJS(this: Expr, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef { - return this.data.toJS(ctx, exception); + pub fn toJS(this: Expr, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { + return this.data.toJS(allocator, globalObject); } pub fn get(expr: *const Expr, name: string) ?Expr { @@ -5203,18 +5226,35 @@ pub const Expr = struct { return equality; } - pub fn toJS(this: Data, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.C.JSValueRef { + pub fn toJS(this: Data, allocator: std.mem.Allocator, globalObject: *JSC.JSGlobalObject) ToJSError!JSC.JSValue { return switch (this) { - .e_array => |e| e.toJS(ctx, exception), - .e_null => |e| e.toJS(ctx, exception), - .e_undefined => |e| e.toJS(ctx, exception), - .e_object => |e| e.toJS(ctx, exception), - .e_boolean => |e| e.toJS(ctx, exception), - .e_number => |e| e.toJS(ctx, exception), - .e_big_int => |e| e.toJS(ctx, exception), - .e_string => |e| e.toJS(ctx, exception), + .e_array => |e| e.toJS(allocator, globalObject), + .e_object => |e| e.toJS(allocator, globalObject), + .e_string => |e| e.toJS(allocator, globalObject), + .e_null => JSC.JSValue.null, + .e_undefined => JSC.JSValue.undefined, + .e_boolean => |boolean| if (boolean.value) + JSC.JSValue.true + else + JSC.JSValue.false, + .e_number => |e| e.toJS(), + // .e_big_int => |e| e.toJS(ctx, exception), + + .e_identifier, + .e_import_identifier, + .inline_identifier, + .e_private_identifier, + .e_commonjs_export_identifier, + => error.@"Cannot convert identifier to JS. Try a statically-known value", + + // brk: { + // // var node = try allocator.create(Macro.JSNode); + // // node.* = Macro.JSNode.initExpr(Expr{ .data = this, .loc = logger.Loc.Empty }); + // // break :brk JSC.JSValue.c(Macro.JSNode.Class.make(globalObject, node)); + // }, + else => { - return JSC.C.JSValueMakeUndefined(ctx); + return error.@"Cannot convert argument type to JS"; }, }; } @@ -9585,7 +9625,7 @@ pub const Macro = struct { threadlocal var args_buf: [3]js.JSObjectRef = undefined; threadlocal var expr_nodes_buf: [1]JSNode = undefined; threadlocal var exception_holder: Zig.ZigException.Holder = undefined; - pub const MacroError = error{MacroFailed}; + pub const MacroError = error{ MacroFailed, OutOfMemory } || ToJSError; pub fn NewRun(comptime Visitor: type) type { return struct { @@ -9609,6 +9649,7 @@ pub const Macro = struct { function_name: string, caller: Expr, args_count: usize, + args_ptr: [*]JSC.JSValue, source: *const logger.Source, id: i32, visitor: Visitor, @@ -9621,7 +9662,7 @@ pub const Macro = struct { macro_callback, null, args_count, - &args_buf, + @ptrCast([*]js.JSObjectRef, args_ptr), ); var runner = Run{ @@ -9936,13 +9977,47 @@ pub const Macro = struct { if (comptime Environment.isDebug) Output.prettyln("<r><d>[macro]<r> call <d><b>{s}<r>", .{function_name}); exception_holder = Zig.ZigException.Holder.init(); - expr_nodes_buf[0] = JSNode.initExpr(caller); - args_buf[0] = JSNode.Class.make( - macro.vm.global, - &expr_nodes_buf[0], - ); - args_buf[1] = if (javascript_object.isEmpty()) null else javascript_object.asObjectRef(); - args_buf[2] = null; + var js_args: []JSC.JSValue = &.{}; + defer { + for (js_args[0 .. js_args.len - @as(usize, @boolToInt(!javascript_object.isEmpty()))]) |arg| { + arg.unprotect(); + } + + allocator.free(js_args); + } + + var globalObject = JSC.VirtualMachine.get().global; + + switch (caller.data) { + .e_call => |call| { + const call_args: []Expr = call.args.slice(); + js_args = try allocator.alloc(JSC.JSValue, call_args.len + @as(usize, @boolToInt(!javascript_object.isEmpty()))); + + for (call_args, js_args[0..call_args.len]) |in, *out| { + const value = try in.toJS( + allocator, + globalObject, + ); + value.protect(); + out.* = value; + } + }, + .e_template => { + @panic("TODO: support template literals in macros"); + }, + else => { + @panic("Unexpected caller type"); + }, + } + + if (!javascript_object.isEmpty()) { + if (js_args.len == 0) { + js_args = try allocator.alloc(JSC.JSValue, 1); + } + + js_args[js_args.len - 1] = javascript_object; + } + const Run = NewRun(Visitor); const CallFunction = @TypeOf(Run.runAsync); @@ -9970,7 +10045,8 @@ pub const Macro = struct { allocator, function_name, caller, - 2 + @as(usize, @boolToInt(!javascript_object.isEmpty())), + js_args.len, + js_args.ptr, source, id, visitor, @@ -10301,3 +10377,10 @@ pub const GlobalStoreHandle = struct { // Stmt | 192 // STry | 384 // -- ESBuild bit sizes + +const ToJSError = error{ + @"Cannot convert argument type to JS", + @"Cannot convert identifier to JS. Try a statically-known value", + MacroError, + OutOfMemory, +}; diff --git a/src/js_parser.zig b/src/js_parser.zig index dea2f044f..13d4b06de 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -2300,7 +2300,11 @@ const ThenCatchChain = struct { has_catch: bool = false, }; -const ParsedPath = struct { loc: logger.Loc, text: string }; +const ParsedPath = struct { + loc: logger.Loc, + text: string, + is_macro: bool, +}; const StrictModeFeature = enum { with_statement, @@ -8058,7 +8062,7 @@ fn NewParser_( } fn processImportStatement(p: *P, stmt_: S.Import, path: ParsedPath, loc: logger.Loc, was_originally_bare_import: bool) anyerror!Stmt { - const is_macro = FeatureFlags.is_macro_enabled and js_ast.Macro.isMacroPath(path.text); + const is_macro = FeatureFlags.is_macro_enabled and (path.is_macro or js_ast.Macro.isMacroPath(path.text)); var stmt = stmt_; if (is_macro) { const id = p.addImportRecord(.stmt, path.loc, path.text); @@ -8884,6 +8888,10 @@ fn NewParser_( // path.assertions ); + if (path.is_macro) { + try p.log.addError(p.source, path.loc, "cannot use macro in export statement"); + } + if (comptime track_symbol_usage_during_parse_pass) { // In the scan pass, we need _some_ way of knowing *not* to mark as unused p.import_records.items[import_record_index].calls_runtime_re_export_fn = true; @@ -8919,6 +8927,10 @@ fn NewParser_( } } + if (parsedPath.is_macro) { + try p.log.addError(p.source, loc, "export from cannot be used with \"type\": \"macro\""); + } + const import_record_index = p.addImportRecord(.stmt, parsedPath.loc, parsedPath.text); const path_name = fs.PathName.init(parsedPath.text); const namespace_ref = p.storeNameInRef( @@ -10913,6 +10925,7 @@ fn NewParser_( var path = ParsedPath{ .loc = p.lexer.loc(), .text = p.lexer.string_literal_slice, + .is_macro = false, }; if (p.lexer.token == .t_no_substitution_template_literal) { @@ -10934,15 +10947,28 @@ fn NewParser_( try p.lexer.expect(.t_open_brace); while (p.lexer.token != .t_close_brace) { - // Parse the key - if (p.lexer.isIdentifierOrKeyword()) {} else if (p.lexer.token == .t_string_literal) {} else { - try p.lexer.expect(.t_identifier); - } + const is_type_flag = brk: { + // Parse the key + if (p.lexer.isIdentifierOrKeyword()) { + break :brk strings.eqlComptime(p.lexer.identifier, "type"); + } else if (p.lexer.token == .t_string_literal) { + break :brk p.lexer.string_literal_is_ascii and strings.eqlComptime(p.lexer.string_literal_slice, "type"); + } else { + try p.lexer.expect(.t_identifier); + } + + break :brk false; + }; try p.lexer.next(); try p.lexer.expect(.t_colon); try p.lexer.expect(.t_string_literal); + if (is_type_flag and + p.lexer.string_literal_is_ascii and strings.eqlComptime(p.lexer.string_literal_slice, "macro")) + { + path.is_macro = true; + } if (p.lexer.token != .t_comma) { break; |