aboutsummaryrefslogtreecommitdiff
path: root/src/js_ast.zig
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/js_ast.zig288
1 files changed, 252 insertions, 36 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 1761e20ca..9ddafdf80 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -321,7 +321,7 @@ pub const Binding = struct {
*B.Object => {
return Binding{ .loc = loc, .data = B{ .b_object = t } };
},
- *B.Missing => {
+ B.Missing => {
return Binding{ .loc = loc, .data = B{ .b_missing = t } };
},
else => {
@@ -1700,6 +1700,7 @@ pub const Stmt = struct {
pub const All = NewBaseStore(Union, 128);
threadlocal var has_inited = false;
+ pub threadlocal var disable_reset = false;
pub fn create(allocator: *std.mem.Allocator) void {
if (has_inited) {
return;
@@ -1710,6 +1711,7 @@ pub const Stmt = struct {
}
pub fn reset() void {
+ if (disable_reset) return;
All.reset();
}
@@ -2367,7 +2369,7 @@ pub const Expr = struct {
return Expr{
.loc = loc,
.data = Data{
- .e_number = Data.Store.All.append(Type, st),
+ .e_number = st,
},
};
},
@@ -2542,8 +2544,160 @@ pub const Expr = struct {
e_class,
e_require,
+ pub inline fn toPublicValue(this: Tag) u16 {
+ return @intCast(u16, @enumToInt(this)) + 16;
+ }
+
+ pub inline fn fromPublicValue(comptime ValueType: type, value: ValueType) ?Tag {
+ if (value < 16 or value > @enumToInt(Tag.e_require)) return null;
+
+ switch (comptime ValueType) {
+ f64 => {
+ return @intToEnum(@floatToInt(u16, value - 16), Tag);
+ },
+ else => {
+ return @intToEnum(@intCast(u6, @intCast(u16, value) - 16), Tag);
+ },
+ }
+ }
+
+ pub const names_strings = [_]string{
+ "<array>",
+ "<unary>",
+ "<binary>",
+ "<boolean>",
+ "<super>",
+ "<null>",
+ "<void>",
+ "<new>",
+ "<function>",
+ "<ntarget>",
+ "<import>",
+ "<call>",
+ "<dot>",
+ "<index>",
+ "<arrow>",
+ "<id>",
+ "<importid>",
+ "<private>",
+ "<jsx>",
+ "<missing>",
+ "<number>",
+ "<bigint>",
+ "<object>",
+ "<spread>",
+ "<string>",
+ "<tpart>",
+ "<template>",
+ "<regexp>",
+ "<await>",
+ "<yield>",
+ "<if>",
+ "<resolve>",
+ "<import>",
+ "<this>",
+ "<class>",
+ "<require>",
+ };
+ pub const valid_names_list: string = brk: {
+ var names_list = names_strings[0];
+ for (names_strings[1..]) |name_str, i| {
+ names_list = names_list ++ "\n " ++ name_str;
+ }
+ break :brk " " ++ names_list;
+ };
+
+ pub const TagName = std.EnumArray(Tag, string);
+
+ pub const names: TagName = brk: {
+ var array = TagName.initUndefined();
+ array.set(.e_array, names_strings[0]);
+ array.set(.e_unary, names_strings[1]);
+ array.set(.e_binary, names_strings[2]);
+ array.set(.e_boolean, names_strings[3]);
+ array.set(.e_super, names_strings[4]);
+ array.set(.e_null, names_strings[5]);
+ array.set(.e_undefined, names_strings[6]);
+ array.set(.e_new, names_strings[7]);
+ array.set(.e_function, names_strings[8]);
+ array.set(.e_new_target, names_strings[9]);
+ array.set(.e_import_meta, names_strings[10]);
+ array.set(.e_call, names_strings[11]);
+ array.set(.e_dot, names_strings[12]);
+ array.set(.e_index, names_strings[13]);
+ array.set(.e_arrow, names_strings[14]);
+ array.set(.e_identifier, names_strings[15]);
+ array.set(.e_import_identifier, names_strings[16]);
+ array.set(.e_private_identifier, names_strings[17]);
+ array.set(.e_jsx_element, names_strings[18]);
+ array.set(.e_missing, names_strings[19]);
+ array.set(.e_number, names_strings[20]);
+ array.set(.e_big_int, names_strings[21]);
+ array.set(.e_object, names_strings[22]);
+ array.set(.e_spread, names_strings[23]);
+ array.set(.e_string, names_strings[24]);
+ array.set(.e_template_part, names_strings[25]);
+ array.set(.e_template, names_strings[26]);
+ array.set(.e_reg_exp, names_strings[27]);
+ array.set(.e_await, names_strings[28]);
+ array.set(.e_yield, names_strings[29]);
+ array.set(.e_if, names_strings[30]);
+ array.set(.e_require_or_require_resolve, names_strings[31]);
+ array.set(.e_import, names_strings[32]);
+ array.set(.e_this, names_strings[33]);
+ array.set(.e_class, names_strings[34]);
+ array.set(.e_require, names_strings[35]);
+ break :brk array;
+ };
+ pub const TagExactSizeMatcher = strings.ExactSizeMatcher(8);
+ pub fn find(name_: string) ?Tag {
+ return switch (TagExactSizeMatcher.match(name_)) {
+ TagExactSizeMatcher.case("array") => Tag.e_array,
+ TagExactSizeMatcher.case("unary") => Tag.e_unary,
+ TagExactSizeMatcher.case("binary") => Tag.e_binary,
+ TagExactSizeMatcher.case("boolean") => Tag.e_boolean,
+ TagExactSizeMatcher.case("true") => Tag.e_boolean,
+ TagExactSizeMatcher.case("false") => Tag.e_boolean,
+ TagExactSizeMatcher.case("super") => Tag.e_super,
+ TagExactSizeMatcher.case("null") => Tag.e_null,
+ TagExactSizeMatcher.case("void") => Tag.e_undefined,
+ TagExactSizeMatcher.case("new") => Tag.e_new,
+ TagExactSizeMatcher.case("function") => Tag.e_function,
+ TagExactSizeMatcher.case("ntarget") => Tag.e_new_target,
+ TagExactSizeMatcher.case("imeta") => Tag.e_import_meta,
+ TagExactSizeMatcher.case("call") => Tag.e_call,
+ TagExactSizeMatcher.case("dot") => Tag.e_dot,
+ TagExactSizeMatcher.case("index") => Tag.e_index,
+ TagExactSizeMatcher.case("arrow") => Tag.e_arrow,
+ TagExactSizeMatcher.case("id") => Tag.e_identifier,
+ TagExactSizeMatcher.case("importid") => Tag.e_import_identifier,
+ TagExactSizeMatcher.case("jsx") => Tag.e_jsx_element,
+ TagExactSizeMatcher.case("missing") => Tag.e_missing,
+ TagExactSizeMatcher.case("number") => Tag.e_number,
+ TagExactSizeMatcher.case("bigint") => Tag.e_big_int,
+ TagExactSizeMatcher.case("object") => Tag.e_object,
+ TagExactSizeMatcher.case("spread") => Tag.e_spread,
+ TagExactSizeMatcher.case("string") => Tag.e_string,
+ TagExactSizeMatcher.case("tpart") => Tag.e_template_part,
+ TagExactSizeMatcher.case("template") => Tag.e_template,
+ TagExactSizeMatcher.case("regexp") => Tag.e_reg_exp,
+ TagExactSizeMatcher.case("await") => Tag.e_await,
+ TagExactSizeMatcher.case("yield") => Tag.e_yield,
+ TagExactSizeMatcher.case("if") => Tag.e_if,
+ TagExactSizeMatcher.case("import") => Tag.e_import,
+ TagExactSizeMatcher.case("this") => Tag.e_this,
+ TagExactSizeMatcher.case("class") => Tag.e_class,
+ TagExactSizeMatcher.case("require") => Tag.e_require,
+ else => null,
+ };
+ }
+
+ pub inline fn name(this: Tag) string {
+ return names.get(this);
+ }
+
pub fn jsonStringify(self: @This(), opts: anytype, o: anytype) !void {
- return try std.json.stringify(@tagName(self), opts, o);
+ return try std.json.stringify(self.name(), opts, o);
}
pub fn isArray(self: Tag) bool {
@@ -3074,7 +3228,7 @@ pub const Expr = struct {
e_import: *E.Import,
e_boolean: E.Boolean,
- e_number: *E.Number,
+ e_number: E.Number,
e_big_int: *E.BigInt,
e_string: *E.String,
@@ -3129,6 +3283,7 @@ pub const Expr = struct {
);
threadlocal var has_inited = false;
+ pub threadlocal var disable_reset = false;
pub fn create(allocator: *std.mem.Allocator) void {
if (has_inited) {
return;
@@ -3140,6 +3295,7 @@ pub const Expr = struct {
}
pub fn reset() void {
+ if (disable_reset) return;
All.reset();
Identifier.reset();
}
@@ -3932,15 +4088,17 @@ pub const Macro = struct {
const js = @import("./javascript/jsc/JavascriptCore.zig");
const Zig = @import("./javascript/jsc/bindings/exports.zig");
const Bundler = @import("./bundler.zig").Bundler;
+ const MacroEntryPoint = @import("./bundler.zig").MacroEntryPoint;
pub const namespace: string = "macro";
+ pub const namespaceWithColon: string = namespace ++ ":";
pub fn isMacroPath(str: string) bool {
- return (str.len > "macro:".len and strings.eqlComptimeIgnoreLen(str[0.."macro:".len], "macro:"));
+ return (str.len > namespaceWithColon.len and strings.eqlComptimeIgnoreLen(str[0..namespaceWithColon.len], namespaceWithColon));
}
pub const MacroContext = struct {
- pub const MacroMap = std.AutoArrayHashMap(u64, Macro);
+ pub const MacroMap = std.AutoArrayHashMap(i32, Macro);
resolver: *Resolver,
env: *DotEnv.Loader,
@@ -3956,7 +4114,7 @@ pub const Macro = struct {
pub fn call(
this: *MacroContext,
- specifier: string,
+ import_record_path: string,
source_dir: string,
log: *logger.Log,
source: *const logger.Source,
@@ -3965,10 +4123,14 @@ pub const Macro = struct {
args: []Expr,
function_name: string,
) anyerror!Expr {
+ Expr.Data.Store.disable_reset = true;
+ Stmt.Data.Store.disable_reset = true;
+ defer Expr.Data.Store.disable_reset = false;
+ defer Stmt.Data.Store.disable_reset = false;
// const is_package_path = isPackagePath(specifier);
- std.debug.assert(strings.eqlComptime(specifier[0..6], "macro:"));
+ std.debug.assert(isMacroPath(import_record_path));
- const resolve_result = this.resolver.resolve(source_dir, specifier["macro:".len..], .stmt) catch |err| {
+ const resolve_result = this.resolver.resolve(source_dir, import_record_path[namespaceWithColon.len..], .stmt) catch |err| {
switch (err) {
error.ModuleNotFound => {
log.addResolveError(
@@ -3976,7 +4138,7 @@ pub const Macro = struct {
import_range,
log.msgs.allocator,
"Macro \"{s}\" not found",
- .{specifier},
+ .{import_record_path},
.stmt,
) catch unreachable;
return error.MacroNotFound;
@@ -3987,28 +4149,57 @@ pub const Macro = struct {
import_range,
log.msgs.allocator,
"{s} resolving macro \"{s}\"",
- .{ @errorName(err), specifier },
+ .{ @errorName(err), import_record_path },
) catch unreachable;
return err;
},
}
};
- var macro_entry = this.macros.getOrPut(std.hash.Wyhash.hash(0, resolve_result.path_pair.primary.text)) catch unreachable;
+ var specifier_buf: [64]u8 = undefined;
+ var specifier_buf_len: u32 = 0;
+ const hash = MacroEntryPoint.generateID(
+ resolve_result.path_pair.primary.text,
+ function_name,
+ &specifier_buf,
+ &specifier_buf_len,
+ );
+
+ var macro_entry = this.macros.getOrPut(hash) catch unreachable;
if (!macro_entry.found_existing) {
- macro_entry.value_ptr.* = try Macro.init(
+ macro_entry.value_ptr.* = Macro.init(
default_allocator,
this.resolver,
resolve_result,
log,
this.env,
- specifier,
- source_dir,
- );
+ function_name,
+ specifier_buf[0..specifier_buf_len],
+ hash,
+ ) catch |err| {
+ macro_entry.value_ptr.* = Macro{ .resolver = undefined, .disabled = true };
+ return err;
+ };
+ Output.flush();
}
defer Output.flush();
- return Macro.Runner.run(macro_entry.value_ptr.*, log, default_allocator, function_name, caller, args, source);
+ const macro = macro_entry.value_ptr.*;
+ if (macro.disabled) {
+ return caller;
+ }
+ macro.vm.enableMacroMode();
+ defer macro.vm.disableMacroMode();
+ return Macro.Runner.run(
+ macro,
+ log,
+ default_allocator,
+ function_name,
+ caller,
+ args,
+ source,
+ hash,
+ );
// this.macros.getOrPut(key: K)
}
};
@@ -4045,6 +4236,15 @@ pub const Macro = struct {
},
);
+ // pub fn isInstanceOf(
+ // ctx: js.JSContextRef,
+ // obj: js.JSObjectRef,
+ // value: js.JSValueRef,
+ // exception: js.ExceptionRef,
+ // ) bool {
+ // js.JSValueToNumber(ctx, value, exception);
+ // }
+
pub fn toString(
this: *JSExpr,
ctx: js.JSContextRef,
@@ -4117,6 +4317,7 @@ pub const Macro = struct {
vm: *JavaScript.VirtualMachine = undefined,
resolved: ResolveResult = undefined,
+ disabled: bool = false,
pub fn init(
allocator: *std.mem.Allocator,
@@ -4124,8 +4325,9 @@ pub const Macro = struct {
resolved: ResolveResult,
log: *logger.Log,
env: *DotEnv.Loader,
+ function_name: string,
specifier: string,
- source_dir: string,
+ hash: i32,
) !Macro {
const path = resolved.path_pair.primary;
@@ -4133,20 +4335,29 @@ pub const Macro = struct {
JavaScript.VirtualMachine.vm
else brk: {
var _vm = try JavaScript.VirtualMachine.init(default_allocator, resolver.opts.transform_options, null, log, env);
+ _vm.enableMacroMode();
+
_vm.bundler.configureLinker();
_vm.bundler.configureDefines() catch unreachable;
break :brk _vm;
};
- JavaScript.VirtualMachine.vm_loaded = true;
+ vm.enableMacroMode();
- var loaded_result = try vm.loadEntryPoint(path.text);
+ var loaded_result = try vm.loadMacroEntryPoint(path.text, function_name, specifier, hash);
if (loaded_result.status(vm.global.vm()) == JSC.JSPromise.Status.Rejected) {
vm.defaultErrorHandler(loaded_result.result(vm.global.vm()), null);
+ vm.disableMacroMode();
return error.MacroLoadError;
}
+ JavaScript.VirtualMachine.vm_loaded = true;
+
+ // We don't need to do anything with the result.
+ // We just want to make sure the promise is finished.
+ _ = loaded_result.result(vm.global.vm());
+
return Macro{
.vm = vm,
.resolved = resolved,
@@ -4155,7 +4366,7 @@ pub const Macro = struct {
}
pub const Runner = struct {
- threadlocal var args_buf: [32]JSC.JSValue = undefined;
+ threadlocal var args_buf: [32]js.JSObjectRef = undefined;
threadlocal var expr_nodes_buf: [32]JSExpr = undefined;
threadlocal var exception_holder: Zig.ZigException.Holder = undefined;
pub fn run(
@@ -4166,32 +4377,37 @@ pub const Macro = struct {
caller: Expr,
args: []Expr,
source: *const logger.Source,
+ id: i32,
) Expr {
+ if (comptime isDebug) Output.prettyln("<r><d>[macro]<r> call <d><b>{s}<r>", .{function_name});
+
exception_holder = Zig.ZigException.Holder.init();
expr_nodes_buf[0] = JSExpr{ .expr = caller };
- args_buf[0] = JSC.JSValue.fromRef(JSExpr.Class.make(
+ args_buf[0] = JSExpr.Class.make(
macro.vm.global.ref(),
&expr_nodes_buf[0],
- ).?);
+ );
for (args) |arg, i| {
expr_nodes_buf[i + 1] = JSExpr{ .expr = arg };
- args_buf[i + 1] = JSC.JSValue.fromRef(
+ args_buf[i + 1] =
JSExpr.Class.make(
- macro.vm.global.ref(),
- &expr_nodes_buf[i + 1],
- ).?,
+ macro.vm.global.ref(),
+ &expr_nodes_buf[i + 1],
);
}
+ args_buf[args.len + 2] = null;
- // Step 1. Resolve the macro specifier
- const result = JSC.JSModuleLoader.callExportedFunction(
- macro.vm.global,
- JSC.ZigString.init(macro.resolved.path_pair.primary.text),
- JSC.ZigString.init(function_name),
- &args_buf,
- @truncate(u8, args.len + 1),
- &exception_holder.zig_exception,
- );
+ var macro_callback = macro.vm.macros.get(id) orelse return caller;
+ var result = js.JSObjectCallAsFunctionReturnValue(macro.vm.global.ref(), macro_callback, null, args.len + 1, &args_buf);
+ var promise = JSC.JSPromise.resolvedPromise(macro.vm.global, result);
+ macro.vm.global.vm().drainMicrotasks();
+
+ if (promise.status(macro.vm.global.vm()) == .Rejected) {
+ macro.vm.defaultErrorHandler(promise.result(macro.vm.global.vm()), null);
+ return caller;
+ }
+
+ const value = promise.result(macro.vm.global.vm());
return caller;
}