aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/js_ast.zig622
-rw-r--r--src/js_lexer.zig3
-rw-r--r--src/js_parser/js_parser.zig186
-rw-r--r--src/json_parser.zig43
4 files changed, 499 insertions, 355 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 88bf444f0..c457984e1 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -5324,7 +5324,8 @@ pub const Macro = struct {
return Expr{ .loc = this.loc, .data = .{ .e_spread = value } };
},
.e_string => |value| {
- return Expr{ .loc = this.loc, .data = .{ .e_string = value } };
+ this.data.e_string.* = value.clone(JavaScript.VirtualMachine.vm.allocator) catch unreachable;
+ return Expr{ .loc = this.loc, .data = .{ .e_string = this.data.e_string } };
},
.e_template_part => |value| {
return Expr{ .loc = this.loc, .data = .{ .e_template_part = value } };
@@ -7480,16 +7481,6 @@ pub const Macro = struct {
}
pub const Runner = struct {
- caller: Expr,
- function_name: string,
- macro: *const Macro,
- allocator: std.mem.Allocator,
- id: i32,
- log: *logger.Log,
- source: *const logger.Source,
- is_top_level: bool = true,
- visited: VisitMap = VisitMap{},
-
const VisitMap = std.AutoHashMapUnmanaged(JSC.JSValue, Expr);
threadlocal var args_buf: [2]js.JSObjectRef = undefined;
@@ -7497,267 +7488,374 @@ pub const Macro = struct {
threadlocal var exception_holder: Zig.ZigException.Holder = undefined;
pub const MacroError = error{MacroFailed};
- fn coerce(
- this: *Runner,
- value: JSC.JSValue,
- global: *JSC.JSGlobalObject,
- comptime Visitor: type,
- visitor: Visitor,
- ) MacroError!Expr {
- if (value.isUndefined()) {
- if (this.is_top_level) {
- return this.caller;
+ pub fn NewRun(comptime Visitor: type) type {
+ return struct {
+ const Run = @This();
+ caller: Expr,
+ function_name: string,
+ macro: *const Macro,
+ global: *JSC.JSGlobalObject,
+ allocator: std.mem.Allocator,
+ id: i32,
+ log: *logger.Log,
+ source: *const logger.Source,
+ visited: VisitMap = VisitMap{},
+ visitor: Visitor,
+ is_top_level: bool = false,
+
+ pub fn runAsync(
+ macro: Macro,
+ log: *logger.Log,
+ allocator: std.mem.Allocator,
+ function_name: string,
+ caller: Expr,
+ args: []Expr,
+ source: *const logger.Source,
+ id: i32,
+ visitor: Visitor,
+ ) callconv(.Async) MacroError!Expr {
+ var macro_callback = macro.vm.macros.get(id) orelse return caller;
+
+ var result = js.JSObjectCallAsFunctionReturnValueHoldingAPILock(macro.vm.global.ref(), macro_callback, null, args.len + 1, &args_buf);
+
+ var runner = Run{
+ .caller = caller,
+ .function_name = function_name,
+ .macro = &macro,
+ .allocator = allocator,
+ .global = macro.vm.global,
+ .id = id,
+ .log = log,
+ .source = source,
+ .visited = VisitMap{},
+ .visitor = visitor,
+ };
+
+ defer runner.visited.deinit(allocator);
+
+ return try runner.run(
+ result,
+ );
}
- return Expr.init(E.Undefined, E.Undefined{}, this.caller.loc);
- } else if (value.isNull()) {
- return Expr.init(E.Null, E.Null{}, this.caller.loc);
- }
+ pub fn run(
+ this: *Run,
+ value: JSC.JSValue,
+ ) MacroError!Expr {
+ return try switch (JSC.ZigConsoleClient.Formatter.Tag.get(value, this.global).tag) {
+ .Error => this.coerce(value, .Error),
+ .Undefined => this.coerce(value, .Undefined),
+ .Null => this.coerce(value, .Null),
+ .Private => this.coerce(value, .Private),
+ .Boolean => this.coerce(value, .Boolean),
+ .Array => this.coerce(value, .Array),
+ .Object => this.coerce(value, .Object),
+ .JSON => this.coerce(value, .JSON),
+ .Integer => this.coerce(value, .Integer),
+ .Double => this.coerce(value, .Double),
+ .String => this.coerce(value, .String),
+ .Promise => this.coerce(value, .Promise),
+ else => brk: {
+ this.log.addErrorFmt(
+ this.source,
+ this.caller.loc,
+ this.allocator,
+ "cannot coerce {s} to Bun's AST. Please return a valid macro using the JSX syntax",
+ .{@tagName(value.jsType())},
+ ) catch unreachable;
+ break :brk error.MacroFailed;
+ },
+ };
+ }
- this.is_top_level = false;
+ pub fn coerce(
+ this: *Run,
+ value: JSC.JSValue,
+ comptime tag: JSC.ZigConsoleClient.Formatter.Tag,
+ ) MacroError!Expr {
+ switch (comptime tag) {
+ .Error => {
+ this.macro.vm.defaultErrorHandler(value, null);
+ return this.caller;
+ },
+ .Undefined => if (this.is_top_level)
+ return this.caller
+ else
+ return Expr.init(E.Undefined, E.Undefined{}, this.caller.loc),
+ .Null => return Expr.init(E.Null, E.Null{}, this.caller.loc),
+ .Private => {
+ this.is_top_level = false;
+ var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
+ if (_entry.found_existing) {
+ return _entry.value_ptr.*;
+ }
- if (value.isError() or value.isAggregateError(global) or value.isException(global.vm())) {
- this.macro.vm.defaultErrorHandler(value, null);
- return error.MacroFailed;
- }
+ if (JSCBase.GetJSPrivateData(JSNode, value.asObjectRef())) |node| {
+ _entry.value_ptr.* = node.toExpr();
+ node.visited = true;
+ node.updateSymbolsMap(Visitor, this.visitor);
+ return _entry.value_ptr.*;
+ }
- const console_tag = JSC.ZigConsoleClient.Formatter.Tag.get(value, global);
- switch (console_tag.tag) {
- .Error, .Undefined, .Null => unreachable,
- .Private => {
- var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
- if (_entry.found_existing) {
- return _entry.value_ptr.*;
- }
+ if (JSCBase.GetJSPrivateData(JSC.BuildError, value.asObjectRef()) != null) {
+ this.macro.vm.defaultErrorHandler(value, null);
+ return error.MacroFailed;
+ }
- if (JSCBase.GetJSPrivateData(JSNode, value.asObjectRef())) |node| {
- _entry.value_ptr.* = node.toExpr();
- node.visited = true;
- node.updateSymbolsMap(Visitor, visitor);
- return _entry.value_ptr.*;
- }
+ if (JSCBase.GetJSPrivateData(JSC.ResolveError, value.asObjectRef()) != null) {
+ this.macro.vm.defaultErrorHandler(value, null);
+ return error.MacroFailed;
+ }
- if (JSCBase.GetJSPrivateData(JSC.BuildError, value.asObjectRef()) != null) {
- this.macro.vm.defaultErrorHandler(value, null);
- return error.MacroFailed;
- }
+ // alright this is insane
+ if (JSCBase.GetJSPrivateData(JSC.WebCore.Response, value.asObjectRef())) |response| {
+ switch (response.body.value) {
+ .Unconsumed => {
+ if (response.body.len > 0) {
+ var mime_type = HTTP.MimeType.other;
+ if (response.body.init.headers) |headers| {
+ if (headers.getHeaderIndex("content-type")) |content_type| {
+ mime_type = HTTP.MimeType.init(headers.asStr(headers.entries.get(content_type).value));
+ }
+ }
- if (JSCBase.GetJSPrivateData(JSC.ResolveError, value.asObjectRef()) != null) {
- this.macro.vm.defaultErrorHandler(value, null);
- return error.MacroFailed;
- }
+ if (response.body.ptr) |_ptr| {
+ var zig_string = JSC.ZigString.init(_ptr[0..response.body.len]);
+
+ if (mime_type.category == .json) {
+ var source = logger.Source.initPathString("fetch.json", zig_string.slice());
+ var out_expr = JSONParser.ParseJSONForMacro(&source, this.log, this.allocator) catch {
+ return error.MacroFailed;
+ };
+ switch (out_expr.data) {
+ .e_object => {
+ out_expr.data.e_object.was_originally_macro = true;
+ },
+ .e_array => {
+ out_expr.data.e_array.was_originally_macro = true;
+ },
+ else => {},
+ }
+
+ return out_expr;
+ }
- // alright this is insane
- if (JSCBase.GetJSPrivateData(JSC.WebCore.Response, value.asObjectRef())) |response| {
- switch (response.body.value) {
- .Unconsumed => {
- if (response.body.len > 0) {
- var mime_type = HTTP.MimeType.other;
- if (response.body.init.headers) |headers| {
- if (headers.getHeaderIndex("content-type")) |content_type| {
- mime_type = HTTP.MimeType.init(headers.asStr(headers.entries.get(content_type).value));
- }
- }
+ if (mime_type.category.isTextLike()) {
+ zig_string.detectEncoding();
+ const utf8 = if (zig_string.is16Bit())
+ zig_string.toSlice(this.allocator).slice()
+ else
+ zig_string.slice();
- if (response.body.ptr) |_ptr| {
- var zig_string = JSC.ZigString.init(_ptr[0..response.body.len]);
-
- if (mime_type.category == .json) {
- var source = logger.Source.initPathString("fetch.json", zig_string.slice());
- var out_expr = JSONParser.ParseJSON(&source, this.log, this.allocator) catch {
- return error.MacroFailed;
- };
- switch (out_expr.data) {
- .e_object => {
- out_expr.data.e_object.was_originally_macro = true;
- },
- .e_array => {
- out_expr.data.e_array.was_originally_macro = true;
- },
- else => {},
- }
+ return Expr.init(E.String, E.String{ .utf8 = utf8 }, this.caller.loc);
+ }
- return out_expr;
+ return Expr.init(E.String, E.String{ .utf8 = zig_string.toBase64DataURL(this.allocator) catch unreachable }, this.caller.loc);
+ }
}
- if (mime_type.category.isTextLike()) {
- zig_string.detectEncoding();
- const utf8 = if (zig_string.is16Bit())
- zig_string.toSlice(this.allocator).slice()
- else
- zig_string.slice();
-
- return Expr.init(E.String, E.String{ .utf8 = utf8 }, this.caller.loc);
+ return Expr.init(E.String, E.String.empty, this.caller.loc);
+ },
+ .Empty => {
+ return Expr.init(E.String, E.String.empty, this.caller.loc);
+ },
+ .String => |str| {
+ var zig_string = JSC.ZigString.init(str);
+
+ zig_string.detectEncoding();
+ if (zig_string.is16Bit()) {
+ var slice = zig_string.toSlice(this.allocator);
+ if (response.body.ptr_allocator) |allocator| response.body.deinit(allocator);
+ return Expr.init(E.String, E.String{ .utf8 = slice.slice() }, this.caller.loc);
}
- return Expr.init(E.String, E.String{ .utf8 = zig_string.toBase64DataURL(this.allocator) catch unreachable }, this.caller.loc);
- }
+ return Expr.init(E.String, E.String{ .utf8 = zig_string.slice() }, this.caller.loc);
+ },
+ .ArrayBuffer => |buffer| {
+ return Expr.init(
+ E.String,
+ E.String{ .utf8 = JSC.ZigString.init(buffer.slice()).toBase64DataURL(this.allocator) catch unreachable },
+ this.caller.loc,
+ );
+ },
}
+ }
+ },
- return Expr.init(E.String, E.String.empty, this.caller.loc);
- },
- .Empty => {
- return Expr.init(E.String, E.String.empty, this.caller.loc);
- },
- .String => |str| {
- var zig_string = JSC.ZigString.init(str);
-
- zig_string.detectEncoding();
- if (zig_string.is16Bit()) {
- var slice = zig_string.toSlice(this.allocator);
- if (response.body.ptr_allocator) |allocator| response.body.deinit(allocator);
- return Expr.init(E.String, E.String{ .utf8 = slice.slice() }, this.caller.loc);
+ .Boolean => {
+ return Expr{ .data = .{ .e_boolean = .{ .value = value.toBoolean() } }, .loc = this.caller.loc };
+ },
+ JSC.ZigConsoleClient.Formatter.Tag.Array => {
+ this.is_top_level = false;
+
+ var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
+ if (_entry.found_existing) {
+ switch (_entry.value_ptr.*.data) {
+ .e_object, .e_array => {
+ this.log.addErrorFmt(this.source, this.caller.loc, this.allocator, "converting circular structure to Bun AST is not implemented yet", .{}) catch unreachable;
+ return error.MacroFailed;
+ },
+ else => {},
}
+ return _entry.value_ptr.*;
+ }
- return Expr.init(E.String, E.String{ .utf8 = zig_string.slice() }, this.caller.loc);
- },
- .ArrayBuffer => |buffer| {
- return Expr.init(
- E.String,
- E.String{ .utf8 = JSC.ZigString.init(buffer.slice()).toBase64DataURL(this.allocator) catch unreachable },
+ var iter = JSC.JSArrayIterator.init(value, this.global);
+ if (iter.len == 0) {
+ const result = Expr.init(
+ E.Array,
+ E.Array{
+ .items = ExprNodeList.init(&[_]Expr{}),
+ .was_originally_macro = true,
+ },
this.caller.loc,
);
- },
- }
- }
- },
-
- .Boolean => {
- return Expr{ .data = .{ .e_boolean = .{ .value = value.toBoolean() } }, .loc = this.caller.loc };
- },
- JSC.ZigConsoleClient.Formatter.Tag.Array => {
- var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
- if (_entry.found_existing) {
- return _entry.value_ptr.*;
- }
+ _entry.value_ptr.* = result;
+ return result;
+ }
+ var array = this.allocator.alloc(Expr, iter.len) catch unreachable;
+ var out = Expr.init(
+ E.Array,
+ E.Array{
+ .items = ExprNodeList.init(array[0..0]),
+ .was_originally_macro = true,
+ },
+ this.caller.loc,
+ );
+ _entry.value_ptr.* = out;
+
+ errdefer this.allocator.free(array);
+ var i: usize = 0;
+ while (iter.next()) |item| {
+ array[i] = try this.run(item);
+ if (array[i].isMissing())
+ continue;
+ i += 1;
+ }
+ out.data.e_array.items = ExprNodeList.init(array);
+ _entry.value_ptr.* = out;
+ return out;
+ },
+ // TODO: optimize this
+ JSC.ZigConsoleClient.Formatter.Tag.Object => {
+ this.is_top_level = false;
+ var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
+ if (_entry.found_existing) {
+ switch (_entry.value_ptr.*.data) {
+ .e_object, .e_array => {
+ this.log.addErrorFmt(this.source, this.caller.loc, this.allocator, "converting circular structure to Bun AST is not implemented yet", .{}) catch unreachable;
+ return error.MacroFailed;
+ },
+ else => {},
+ }
+ return _entry.value_ptr.*;
+ }
- var iter = JSC.JSArrayIterator.init(value, global);
- if (iter.len == 0) {
- return Expr.init(
- E.Array,
- E.Array{
- .items = ExprNodeList.init(&[_]Expr{}),
- .was_originally_macro = true,
- },
- this.caller.loc,
- );
- }
- var array = this.allocator.alloc(Expr, iter.len) catch unreachable;
- errdefer this.allocator.free(array);
- var i: usize = 0;
- while (iter.next()) |item| {
- array[i] = try this.coerce(item, global, Visitor, visitor);
- if (array[i].isMissing())
- continue;
- i += 1;
- }
+ var object = value.asObjectRef();
+ var array = JSC.C.JSObjectCopyPropertyNames(this.global.ref(), object);
+ defer JSC.C.JSPropertyNameArrayRelease(array);
+ const count_ = JSC.C.JSPropertyNameArrayGetCount(array);
+ var properties = this.allocator.alloc(G.Property, count_) catch unreachable;
+ errdefer this.allocator.free(properties);
+ var out = Expr.init(
+ E.Object,
+ E.Object{
+ .properties = BabyList(G.Property).init(properties),
+ .was_originally_macro = true,
+ },
+ this.caller.loc,
+ );
+ _entry.value_ptr.* = out;
+
+ var i: usize = 0;
+ while (i < count_) : (i += 1) {
+ var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(array, i);
+ defer JSC.C.JSStringRelease(property_name_ref);
+ properties[i] = G.Property{
+ .key = Expr.init(E.String, E.String{ .utf8 = this.allocator.dupe(
+ u8,
+ JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)],
+ ) catch unreachable }, this.caller.loc),
+ .value = try this.run(
+ JSC.JSValue.fromRef(JSC.C.JSObjectGetProperty(this.global.ref(), object, property_name_ref, null)),
+ ),
+ };
+ }
+ out.data.e_object.properties = BabyList(G.Property).init(properties[0..i]);
+ _entry.value_ptr.* = out;
+ return out;
+ },
- const out = Expr.init(
- E.Array,
- E.Array{
- .items = ExprNodeList.init(array[0..i]),
- .was_originally_macro = true,
+ .JSON => {
+ this.is_top_level = false;
+ // if (console_tag.cell == .JSDate) {
+ // // in the code for printing dates, it never exceeds this amount
+ // var iso_string_buf = this.allocator.alloc(u8, 36) catch unreachable;
+ // var str = JSC.ZigString.init("");
+ // value.jsonStringify(this.global, 0, &str);
+ // var out_buf: []const u8 = std.fmt.bufPrint(iso_string_buf, "{}", .{str}) catch "";
+ // if (out_buf.len > 2) {
+ // // trim the quotes
+ // out_buf = out_buf[1 .. out_buf.len - 1];
+ // }
+ // return Expr.init(E.New, E.New{.target = Expr.init(E.Dot{.target = E}) })
+ // }
},
- this.caller.loc,
- );
- _entry.value_ptr.* = out;
- return out;
- },
- // TODO: optimize this
- JSC.ZigConsoleClient.Formatter.Tag.Object => {
- var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
- if (_entry.found_existing) {
- return _entry.value_ptr.*;
- }
- var object = value.asObjectRef();
- var array = JSC.C.JSObjectCopyPropertyNames(global.ref(), object);
- defer JSC.C.JSPropertyNameArrayRelease(array);
- const count_ = JSC.C.JSPropertyNameArrayGetCount(array);
- var properties = this.allocator.alloc(G.Property, count_) catch unreachable;
- errdefer this.allocator.free(properties);
- var i: usize = 0;
- while (i < count_) : (i += 1) {
- var property_name_ref = JSC.C.JSPropertyNameArrayGetNameAtIndex(array, i);
- defer JSC.C.JSStringRelease(property_name_ref);
- properties[i] = G.Property{
- .key = Expr.init(E.String, E.String{ .utf8 = this.allocator.dupe(
- u8,
- JSC.C.JSStringGetCharacters8Ptr(property_name_ref)[0..JSC.C.JSStringGetLength(property_name_ref)],
- ) catch unreachable }, this.caller.loc),
- .value = try this.coerce(
- JSC.JSValue.fromRef(JSC.C.JSObjectGetProperty(global.ref(), object, property_name_ref, null)),
- global,
- Visitor,
- visitor,
- ),
- };
- }
- const out = Expr.init(
- E.Object,
- E.Object{
- .properties = BabyList(G.Property).init(properties[0..i]),
- .was_originally_macro = true,
+ .Integer => {
+ return Expr.init(E.Number, E.Number{ .value = @intToFloat(f64, value.toInt32()) }, this.caller.loc);
},
- this.caller.loc,
- );
- _entry.value_ptr.* = out;
- return out;
- },
-
- .JSON => {
- // if (console_tag.cell == .JSDate) {
- // // in the code for printing dates, it never exceeds this amount
- // var iso_string_buf = this.allocator.alloc(u8, 36) catch unreachable;
- // var str = JSC.ZigString.init("");
- // value.jsonStringify(global, 0, &str);
- // var out_buf: []const u8 = std.fmt.bufPrint(iso_string_buf, "{}", .{str}) catch "";
- // if (out_buf.len > 2) {
- // // trim the quotes
- // out_buf = out_buf[1 .. out_buf.len - 1];
- // }
- // return Expr.init(E.New, E.New{.target = Expr.init(E.Dot{.target = E}) })
- // }
- },
-
- .Integer => {
- return Expr.init(E.Number, E.Number{ .value = @intToFloat(f64, value.toInt32()) }, this.caller.loc);
- },
- .Double => {
- return Expr.init(E.Number, E.Number{ .value = value.asNumber() }, this.caller.loc);
- },
- .String => {
- var zig_str = value.getZigString(global);
- zig_str.detectEncoding();
- var sliced = zig_str.toSlice(this.allocator);
- return Expr.init(E.String, E.String{ .utf8 = sliced.slice() }, this.caller.loc);
- },
- .Promise => {
- var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
- if (_entry.found_existing) {
- return _entry.value_ptr.*;
- }
+ .Double => {
+ return Expr.init(E.Number, E.Number{ .value = value.asNumber() }, this.caller.loc);
+ },
+ .String => {
+ var zig_str = value.getZigString(this.global);
+ zig_str.detectEncoding();
+ var sliced = zig_str.toSlice(this.allocator);
+ return Expr.init(E.String, E.String{ .utf8 = sliced.slice() }, this.caller.loc);
+ },
+ .Promise => {
+ var _entry = this.visited.getOrPut(this.allocator, value) catch unreachable;
+ if (_entry.found_existing) {
+ return _entry.value_ptr.*;
+ }
- var promise = JSC.JSPromise.resolvedPromise(global, value);
- while (promise.status(global.vm()) == .Pending) {
- this.macro.vm.tick();
- }
+ var promise = JSC.JSPromise.resolvedPromise(this.global, value);
+ while (promise.status(this.global.vm()) == .Pending) {
+ this.macro.vm.tick();
+ }
- const result = try this.coerce(promise.result(global.vm()), global, Visitor, visitor);
- _entry.value_ptr.* = result;
- return result;
- },
- else => {},
- }
+ const rejected = promise.status(this.global.vm()) == .Rejected;
+ const promise_result = promise.result(this.global.vm());
+
+ if (promise_result.isUndefined() and this.is_top_level) {
+ this.is_top_level = false;
+ return this.caller;
+ }
+
+ if (rejected or promise_result.isError() or promise_result.isAggregateError(this.global) or promise_result.isException(this.global.vm())) {
+ this.macro.vm.defaultErrorHandler(promise_result, null);
+ return error.MacroFailed;
+ }
+ this.is_top_level = false;
+ const result = try this.run(promise_result);
+
+ _entry.value_ptr.* = result;
+ return result;
+ },
+ else => {},
+ }
- this.log.addErrorFmt(
- this.source,
- this.caller.loc,
- this.allocator,
- "cannot coerce {s} to Bun's AST. Please return a valid macro using the JSX syntax",
- .{@tagName(console_tag.cell)},
- ) catch unreachable;
- return error.MacroFailed;
+ this.log.addErrorFmt(
+ this.source,
+ this.caller.loc,
+ this.allocator,
+ "cannot coerce {s} to Bun's AST. Please return a valid macro using the JSX syntax",
+ .{@tagName(value.jsType())},
+ ) catch unreachable;
+ return error.MacroFailed;
+ }
+ };
}
pub fn run(
@@ -7780,29 +7878,29 @@ pub const Macro = struct {
macro.vm.global.ref(),
&expr_nodes_buf[0],
);
-
args_buf[1] = null;
+ const Run = NewRun(Visitor);
- var macro_callback = macro.vm.macros.get(id) orelse return caller;
- var result = js.JSObjectCallAsFunctionReturnValueHoldingAPILock(macro.vm.global.ref(), macro_callback, null, args.len + 1, &args_buf);
-
- var runner = Runner{
- .caller = caller,
- .function_name = function_name,
- .macro = &macro,
- .allocator = allocator,
- .id = id,
- .log = log,
- .source = source,
- };
- defer runner.visited.deinit(allocator);
+ // Give it >= 256 KB stack space
+ // Cast to usize to ensure we get an 8 byte aligned pointer
+ const PooledFrame = ObjectPool([@maximum(@sizeOf(@Frame(Run.runAsync)), 256_000) / @sizeOf(usize)]usize, null, true, 1);
+ var pooled_frame = PooledFrame.get(default_allocator);
+ defer pooled_frame.release();
+
+ var result: MacroError!Expr = error.MacroFailed;
- return try runner.coerce(
- result,
- macro.vm.global,
- Visitor,
+ _ = nosuspend @asyncCall(std.mem.asBytes(&pooled_frame.data), &result, Run.runAsync, .{
+ macro,
+ log,
+ allocator,
+ function_name,
+ caller,
+ args,
+ source,
+ id,
visitor,
- );
+ });
+ return result;
}
};
};
diff --git a/src/js_lexer.zig b/src/js_lexer.zig
index 45c8dd982..878751eea 100644
--- a/src/js_lexer.zig
+++ b/src/js_lexer.zig
@@ -51,6 +51,9 @@ pub const JSONOptions = struct {
ignore_trailing_escape_sequences: bool = false,
json_warn_duplicate_keys: bool = true,
+
+ /// mark as originally for a macro to enable inlining
+ was_originally_macro: bool = false,
};
pub fn NewLexer(comptime json_options: JSONOptions) type {
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index e5add04bf..1c5e63b06 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -2958,6 +2958,8 @@ pub fn NewParser(
pub const parser_features = js_parser_features;
const P = @This();
pub const jsx_transform_type: JSXTransformType = js_parser_jsx;
+ const allow_macros = FeatureFlags.is_macro_enabled and jsx_transform_type != .macro;
+ const MacroCallCountType = if (allow_macros) u32 else u0;
macro: MacroState = undefined,
allocator: Allocator,
options: Parser.Options,
@@ -2991,7 +2993,7 @@ pub fn NewParser(
promise_ref: ?Ref = null,
scopes_in_order_visitor_index: usize = 0,
has_classic_runtime_warned: bool = false,
- has_called_macro: bool = false,
+ macro_call_count: MacroCallCountType = 0,
/// Used for transforming export default -> module.exports
has_export_default: bool = false,
@@ -6178,7 +6180,6 @@ pub fn NewParser(
try p.lexer.expectContextualKeyword("from");
_ = try p.parsePath();
try p.lexer.expectOrInsertSemicolon();
- Output.debug("Imported type-only import", .{});
return p.s(S.TypeScript{}, loc);
},
else => {},
@@ -6241,7 +6242,7 @@ pub fn NewParser(
}
}
- const macro_remap = if ((comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) and !is_macro)
+ const macro_remap = if ((comptime allow_macros) and !is_macro)
p.options.macro_context.getRemap(path.text)
else
null;
@@ -11099,7 +11100,7 @@ pub fn NewParser(
if (func.name) |name| {
if (name.ref) |name_ref| {
p.recordDeclaredSymbol(name_ref) catch unreachable;
- const symbol_name = p.symbols.items[name_ref.inner_index].original_name;
+ const symbol_name = p.loadNameFromRef(name_ref);
if (isEvalOrArguments(symbol_name)) {
p.markStrictModeFeature(.eval_or_arguments, js_lexer.rangeOfIdentifier(p.source, name.loc), symbol_name) catch unreachable;
}
@@ -11495,12 +11496,12 @@ pub fn NewParser(
if (e_.tag) |tag| {
e_.tag = p.visitExpr(tag);
- if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
+ if (comptime allow_macros) {
if (e_.tag.?.data == .e_import_identifier) {
const ref = e_.tag.?.data.e_import_identifier.ref;
if (p.macro.refs.get(ref)) |import_record_id| {
const name = p.symbols.items[ref.inner_index].original_name;
- p.has_called_macro = true;
+ p.macro_call_count += 1;
const record = &p.import_records.items[import_record_id];
// We must visit it to convert inline_identifiers and record usage
const macro_result = (p.options.macro_context.call(
@@ -12175,8 +12176,8 @@ pub fn NewParser(
return _expr;
}
- if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
- if (p.has_called_macro) {
+ if (comptime allow_macros) {
+ if (p.macro_call_count > 0) {
if (e_.target.data == .e_object and e_.target.data.e_object.was_originally_macro) {
if (e_.target.get(e_.name)) |obj| {
return obj;
@@ -12431,7 +12432,7 @@ pub fn NewParser(
}
}
- if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
+ if (comptime allow_macros) {
if (is_macro_ref) {
const ref = e_.target.data.e_import_identifier.ref;
const import_record_id = p.macro.refs.get(ref).?;
@@ -12439,7 +12440,7 @@ pub fn NewParser(
const record = &p.import_records.items[import_record_id];
const copied = Expr{ .loc = expr.loc, .data = .{ .e_call = e_ } };
const start_error_count = p.log.msgs.items.len;
- p.has_called_macro = true;
+ p.macro_call_count += 1;
const macro_result =
p.options.macro_context.call(
record.path.text,
@@ -12972,8 +12973,8 @@ pub fn NewParser(
// }
// }
// }
- // if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
- // if (p.has_called_macro and data.decls[i].value != null and
+ // if (comptime allow_macros) {
+ // if (p.macro_call_count and data.decls[i].value != null and
// data.decls[i].value.?.data == .e_object and data.decls[i].value.?.data.e_object.was_originally_macro)
// {
// p.maybeInlineMacroObject(&data.decls[i], data.decls[i].value.?);
@@ -13339,87 +13340,18 @@ pub fn NewParser(
var val = data.decls[i].value.?;
const was_anonymous_named_expr = p.isAnonymousNamedExpr(val);
- data.decls[i].value = p.visitExpr(val);
+ const prev_macro_call_count = p.macro_call_count;
- // Optionally preserve the name
- switch (data.decls[i].binding.data) {
- .b_identifier => |id| {
- data.decls[i].value = p.maybeKeepExprSymbolName(
- data.decls[i].value.?,
- p.symbols.items[id.ref.inner_index].original_name,
- was_anonymous_named_expr,
- );
- },
- .b_object => |bound_object| {
- if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
- if (p.has_called_macro and data.decls[i].value != null and
- data.decls[i].value.?.data == .e_object and
- data.decls[i].value.?.data.e_object.was_originally_macro)
- {
- bail: {
- var object = data.decls[i].value.?.data.e_object;
- for (bound_object.properties) |property| {
- if (property.flags.is_spread) break :bail;
- }
- var output_properties = object.properties.slice();
- var end: u32 = 0;
- for (bound_object.properties) |property| {
- if (property.key.asString(p.allocator)) |name| {
- if (object.asProperty(name)) |query| {
- output_properties[end] = output_properties[query.i];
- end += 1;
- }
- }
- }
+ data.decls[i].value = p.visitExpr(val);
- object.properties.len = end;
- }
- }
- }
- },
- .b_array => |bound_array| {
- if (comptime FeatureFlags.is_macro_enabled and jsx_transform_type != .macro) {
- if (p.has_called_macro and data.decls[i].value != null and
- data.decls[i].value.?.data == .e_array and
- data.decls[i].value.?.data.e_array.was_originally_macro)
- {
- bail: {
- var array = data.decls[i].value.?.data.e_array;
- if (bound_array.has_spread) break :bail;
- array.items.len = @minimum(array.items.len, @truncate(u32, bound_array.items.len));
- var slice = array.items.slice();
- outer: for (bound_array.items[0..array.items.len]) |item, item_i| {
- const child_expr = slice[item_i];
- switch (item.binding.data) {
- .b_object => |bound_object| {
- if (child_expr.data != .e_object) continue :outer;
-
- for (bound_object.properties) |property| {
- if (property.flags.is_spread) continue :outer;
- }
- var object = child_expr.data.e_object;
- var output_properties = object.properties.slice();
- var end: u32 = 0;
- for (bound_object.properties) |property| {
- if (property.key.asString(p.allocator)) |name| {
- if (object.asProperty(name)) |query| {
- output_properties[end] = output_properties[query.i];
- end += 1;
- }
- }
- }
-
- object.properties.len = end;
- },
- else => {},
- }
- }
- }
- }
- }
- },
- else => {},
- }
+ p.visitDecl(
+ &data.decls[i],
+ was_anonymous_named_expr,
+ if (comptime allow_macros)
+ prev_macro_call_count != p.macro_call_count
+ else
+ false,
+ );
}
}
@@ -13954,6 +13886,78 @@ pub fn NewParser(
try stmts.append(stmt.*);
}
+ fn visitBindingAndExprForMacro(p: *P, binding: Binding, expr: Expr) void {
+ switch (binding.data) {
+ .b_object => |bound_object| {
+ if (expr.data == .e_object and
+ expr.data.e_object.was_originally_macro)
+ {
+ var object = expr.data.e_object;
+ for (bound_object.properties) |property| {
+ if (property.flags.is_spread) return;
+ }
+ var output_properties = object.properties.slice();
+ var end: u32 = 0;
+ for (bound_object.properties) |property| {
+ if (property.key.asString(p.allocator)) |name| {
+ if (object.asProperty(name)) |query| {
+ switch (query.expr.data) {
+ .e_object, .e_array => p.visitBindingAndExprForMacro(property.value, query.expr),
+ else => {},
+ }
+ output_properties[end] = output_properties[query.i];
+ end += 1;
+ }
+ }
+ }
+
+ object.properties.len = end;
+ }
+ },
+ .b_array => |bound_array| {
+ if (expr.data == .e_array and
+ expr.data.e_array.was_originally_macro and !bound_array.has_spread)
+ {
+ var array = expr.data.e_array;
+
+ array.items.len = @minimum(array.items.len, @truncate(u32, bound_array.items.len));
+ var slice = array.items.slice();
+ for (bound_array.items[0..array.items.len]) |item, item_i| {
+ const child_expr = slice[item_i];
+ if (item.binding.data == .b_missing) {
+ slice[item_i] = p.e(E.Missing{}, expr.loc);
+ continue;
+ }
+
+ p.visitBindingAndExprForMacro(item.binding, child_expr);
+ }
+ }
+ },
+ else => {},
+ }
+ }
+
+ fn visitDecl(p: *P, decl: *Decl, was_anonymous_named_expr: bool, could_be_macro: bool) void {
+ // Optionally preserve the name
+ switch (decl.binding.data) {
+ .b_identifier => |id| {
+ decl.value = p.maybeKeepExprSymbolName(
+ decl.value.?,
+ p.symbols.items[id.ref.inner_index].original_name,
+ was_anonymous_named_expr,
+ );
+ },
+ .b_object, .b_array => {
+ if (comptime allow_macros) {
+ if (could_be_macro and decl.value != null) {
+ p.visitBindingAndExprForMacro(decl.binding, decl.value.?);
+ }
+ }
+ },
+ else => {},
+ }
+ }
+
pub fn markExportedDeclsInsideNamespace(p: *P, ns_ref: Ref, decls: []G.Decl) void {
for (decls) |decl| {
p.markExportedBindingInsideNamespace(ns_ref, decl.binding);
diff --git a/src/json_parser.zig b/src/json_parser.zig
index c732bfd12..c0c695e35 100644
--- a/src/json_parser.zig
+++ b/src/json_parser.zig
@@ -178,7 +178,11 @@ fn JSONLikeParser(opts: js_lexer.JSONOptions) type {
is_single_line = false;
}
try p.lexer.expect(.t_close_bracket);
- return p.e(E.Array{ .items = ExprNodeList.fromList(exprs), .is_single_line = is_single_line }, loc);
+ return p.e(E.Array{
+ .items = ExprNodeList.fromList(exprs),
+ .is_single_line = is_single_line,
+ .was_originally_macro = comptime opts.was_originally_macro,
+ }, loc);
},
.t_open_brace => {
try p.lexer.next();
@@ -247,6 +251,7 @@ fn JSONLikeParser(opts: js_lexer.JSONOptions) type {
return p.e(E.Object{
.properties = G.Property.List.fromList(properties),
.is_single_line = is_single_line,
+ .was_originally_macro = comptime opts.was_originally_macro,
}, loc);
},
else => {
@@ -482,10 +487,21 @@ const DotEnvJSONParser = JSONLikeParser(js_lexer.JSONOptions{
.allow_trailing_commas = true,
.is_json = true,
});
-var empty_string = E.String{ .utf8 = "" };
+
const TSConfigParser = JSONLikeParser(js_lexer.JSONOptions{ .allow_comments = true, .is_json = true, .allow_trailing_commas = true });
+const JSONParserForMacro = JSONLikeParser(
+ js_lexer.JSONOptions{
+ .allow_comments = true,
+ .is_json = true,
+ .json_warn_duplicate_keys = false,
+ .allow_trailing_commas = true,
+ .was_originally_macro = true,
+ },
+);
+
var empty_object = E.Object{};
var empty_array = E.Array{};
+var empty_string = E.String{ .utf8 = "" };
var empty_string_data = Expr.Data{ .e_string = &empty_string };
var empty_object_data = Expr.Data{ .e_object = &empty_object };
var empty_array_data = Expr.Data{ .e_array = &empty_array };
@@ -513,6 +529,29 @@ pub fn ParseJSON(source: *const logger.Source, log: *logger.Log, allocator: std.
return try parser.parseExpr(false);
}
+pub fn ParseJSONForMacro(source: *const logger.Source, log: *logger.Log, allocator: std.mem.Allocator) !Expr {
+ var parser = try JSONParserForMacro.init(allocator, source.*, log);
+ switch (source.contents.len) {
+ // This is to be consisntent with how disabled JS files are handled
+ 0 => {
+ return Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_object_data };
+ },
+ // This is a fast pass I guess
+ 2 => {
+ if (strings.eqlComptime(source.contents[0..1], "\"\"") or strings.eqlComptime(source.contents[0..1], "''")) {
+ return Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_string_data };
+ } else if (strings.eqlComptime(source.contents[0..1], "{}")) {
+ return Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_object_data };
+ } else if (strings.eqlComptime(source.contents[0..1], "[]")) {
+ return Expr{ .loc = logger.Loc{ .start = 0 }, .data = empty_array_data };
+ }
+ },
+ else => {},
+ }
+
+ return try parser.parseExpr(false);
+}
+
pub const JSONParseResult = struct {
expr: Expr,
tag: Tag,