mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
macro
This commit is contained in:
106
src/ast/ast.js
Normal file
106
src/ast/ast.js
Normal file
@@ -0,0 +1,106 @@
|
||||
globalThis.BunASTNode ??= class BunASTNode {
|
||||
position = -1;
|
||||
};
|
||||
|
||||
if (!globalThis.BunAST) {
|
||||
globalThis.BunAST = {
|
||||
EArray: class EArray extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EUnary: class EUnary extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EBinary: class EBinary extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EClass: class EClass extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ENew: class ENew extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EFunction: class EFunction extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ECall: class ECall extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EDot: class EDot extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EIndex: class EIndex extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EArrow: class EArrow extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EIdentifier: class EIdentifier extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EImportIdentifier: class EImportIdentifier extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EPrivateIdentifier: class EPrivateIdentifier extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EJsxElement: class EJsxElement extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EObject: class EObject extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ESpread: class ESpread extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ETemplatePart: class ETemplatePart extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ETemplate: class ETemplate extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ERegExp: class ERegExp extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EAwait: class EAwait extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EYield: class EYield extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EIf: class EIf extends BunASTNode {
|
||||
no = Number.MAX_SAFE_INTEGER;
|
||||
yes = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ERequire: class ERequire extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EImport: class EImport extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EBoolean: class EBoolean extends BunASTNode {
|
||||
val = false;
|
||||
},
|
||||
ENumber: class ENumber extends BunASTNode {
|
||||
val = 0;
|
||||
},
|
||||
EBigInt: class EBigInt extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EString: class EString extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EMissing: class EMissing extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EThis: class EThis extends BunASTNode {},
|
||||
ESuper: class ESuper extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
ENull: class ENull extends BunASTNode {},
|
||||
EUndefined: class EUndefined extends BunASTNode {},
|
||||
ENewTarget: class ENewTarget extends BunASTNode {
|
||||
#ptr = Number.MAX_SAFE_INTEGER;
|
||||
},
|
||||
EImportMeta: class EImportMeta extends BunASTNode {},
|
||||
};
|
||||
}
|
||||
@@ -123,6 +123,8 @@ pub const Bundler = struct {
|
||||
// must be pointer array because we can't we don't want the source to point to invalid memory if the array size is reallocated
|
||||
virtual_modules: std.ArrayList(*ClientEntryPoint),
|
||||
|
||||
macro_context: ?*js_ast.Macro.MacroContext = null,
|
||||
|
||||
pub const isCacheEnabled = cache_files;
|
||||
|
||||
pub fn clone(this: *ThisBundler, allocator: *std.mem.Allocator, to: *ThisBundler) !void {
|
||||
@@ -2412,6 +2414,13 @@ pub const Bundler = struct {
|
||||
bundler.options.jsx.supports_fast_refresh;
|
||||
opts.filepath_hash_for_hmr = file_hash orelse 0;
|
||||
opts.warn_about_unbundled_modules = bundler.options.platform != .bun;
|
||||
|
||||
if (bundler.macro_context == null) {
|
||||
bundler.macro_context = js_ast.Macro.MacroContext.init(bundler);
|
||||
}
|
||||
|
||||
opts.macro_context = &bundler.macro_context.?;
|
||||
|
||||
const value = (bundler.resolver.caches.js.parse(
|
||||
allocator,
|
||||
opts,
|
||||
@@ -3317,3 +3326,72 @@ pub const ResolveQueue = std.fifo.LinearFifo(
|
||||
_resolver.Result,
|
||||
std.fifo.LinearFifoBufferType.Dynamic,
|
||||
);
|
||||
|
||||
// This is not very fast.
|
||||
// The idea is: we want to generate a unique entry point per macro function export that registers the macro
|
||||
// Registering the macro happens in VirtualMachine
|
||||
// We "register" it which just marks the JSValue as protected.
|
||||
// This is mostly a workaround for being unable to call ESM exported functions from C++.
|
||||
// When that is resolved, we should remove this.
|
||||
pub const MacroEntryPoint = struct {
|
||||
code_buffer: [std.fs.MAX_PATH_BYTES * 2 + 500]u8 = undefined,
|
||||
output_code_buffer: [std.fs.MAX_PATH_BYTES * 8 + 500]u8 = undefined,
|
||||
source: logger.Source = undefined,
|
||||
|
||||
pub fn generateID(entry_path: string, function_name: string, buf: []u8, len: *u32) i32 {
|
||||
var hasher = std.hash.Wyhash.init(0);
|
||||
hasher.update(js_ast.Macro.namespaceWithColon);
|
||||
hasher.update(entry_path);
|
||||
hasher.update(function_name);
|
||||
const truncated_u32 = @truncate(u32, hasher.final());
|
||||
|
||||
const specifier = std.fmt.bufPrint(buf, js_ast.Macro.namespaceWithColon ++ "//{x}.js", .{truncated_u32}) catch unreachable;
|
||||
len.* = @truncate(u32, specifier.len);
|
||||
|
||||
return generateIDFromSpecifier(specifier);
|
||||
}
|
||||
|
||||
pub fn generateIDFromSpecifier(specifier: string) i32 {
|
||||
return @bitCast(i32, @truncate(u32, std.hash.Wyhash.hash(0, specifier)));
|
||||
}
|
||||
|
||||
pub fn generate(
|
||||
entry: *MacroEntryPoint,
|
||||
bundler: *Bundler,
|
||||
import_path: Fs.PathName,
|
||||
function_name: string,
|
||||
macro_id: i32,
|
||||
macro_label_: string,
|
||||
) !void {
|
||||
const dir_to_use: string = import_path.dirWithTrailingSlash();
|
||||
std.mem.copy(u8, entry.code_buffer[0..macro_label_.len], macro_label_);
|
||||
const macro_label = entry.code_buffer[0..macro_label_.len];
|
||||
|
||||
const code = try std.fmt.bufPrint(
|
||||
entry.code_buffer[macro_label.len..],
|
||||
\\//Auto-generated file
|
||||
\\import * as Macros from '{s}{s}';
|
||||
\\
|
||||
\\if (!('{s}' in Macros)) {{
|
||||
\\ throw new Error("Macro '{s}' not found in '{s}{s}'");
|
||||
\\}}
|
||||
\\
|
||||
\\Bun.registerMacro({d}, Macros['{s}']);
|
||||
,
|
||||
.{
|
||||
dir_to_use,
|
||||
import_path.filename,
|
||||
function_name,
|
||||
function_name,
|
||||
dir_to_use,
|
||||
import_path.filename,
|
||||
macro_id,
|
||||
function_name,
|
||||
},
|
||||
);
|
||||
|
||||
entry.source = logger.Source.initPathString(macro_label, code);
|
||||
entry.source.path.text = macro_label;
|
||||
entry.source.path.namespace = js_ast.Macro.namespace;
|
||||
}
|
||||
};
|
||||
|
||||
501
src/js_ast.zig
501
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();
|
||||
}
|
||||
@@ -3921,6 +4077,343 @@ pub fn printmem(comptime format: string, args: anytype) void {
|
||||
// Output.print(format, args);
|
||||
}
|
||||
|
||||
pub const Macro = struct {
|
||||
const JavaScript = @import("./javascript/jsc/javascript.zig");
|
||||
const JSC = @import("./javascript/jsc/bindings/bindings.zig");
|
||||
const JSCBase = @import("./javascript/jsc/base.zig");
|
||||
const Resolver = @import("./resolver/resolver.zig").Resolver;
|
||||
const isPackagePath = @import("./resolver/resolver.zig").isPackagePath;
|
||||
const ResolveResult = @import("./resolver/resolver.zig").Result;
|
||||
const DotEnv = @import("./env_loader.zig");
|
||||
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 > namespaceWithColon.len and strings.eqlComptimeIgnoreLen(str[0..namespaceWithColon.len], namespaceWithColon));
|
||||
}
|
||||
|
||||
pub const MacroContext = struct {
|
||||
pub const MacroMap = std.AutoArrayHashMap(i32, Macro);
|
||||
|
||||
resolver: *Resolver,
|
||||
env: *DotEnv.Loader,
|
||||
macros: MacroMap,
|
||||
|
||||
pub fn init(bundler: *Bundler) MacroContext {
|
||||
return MacroContext{
|
||||
.macros = MacroMap.init(default_allocator),
|
||||
.resolver = &bundler.resolver,
|
||||
.env = bundler.env,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn call(
|
||||
this: *MacroContext,
|
||||
import_record_path: string,
|
||||
source_dir: string,
|
||||
log: *logger.Log,
|
||||
source: *const logger.Source,
|
||||
import_range: logger.Range,
|
||||
caller: Expr,
|
||||
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(isMacroPath(import_record_path));
|
||||
|
||||
const resolve_result = this.resolver.resolve(source_dir, import_record_path[namespaceWithColon.len..], .stmt) catch |err| {
|
||||
switch (err) {
|
||||
error.ModuleNotFound => {
|
||||
log.addResolveError(
|
||||
source,
|
||||
import_range,
|
||||
log.msgs.allocator,
|
||||
"Macro \"{s}\" not found",
|
||||
.{import_record_path},
|
||||
.stmt,
|
||||
) catch unreachable;
|
||||
return error.MacroNotFound;
|
||||
},
|
||||
else => {
|
||||
log.addRangeErrorFmt(
|
||||
source,
|
||||
import_range,
|
||||
log.msgs.allocator,
|
||||
"{s} resolving macro \"{s}\"",
|
||||
.{ @errorName(err), import_record_path },
|
||||
) catch unreachable;
|
||||
return err;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
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.* = Macro.init(
|
||||
default_allocator,
|
||||
this.resolver,
|
||||
resolve_result,
|
||||
log,
|
||||
this.env,
|
||||
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();
|
||||
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
||||
pub const JSExpr = struct {
|
||||
expr: Expr,
|
||||
pub const Class = JSCBase.NewClass(
|
||||
JSExpr,
|
||||
.{
|
||||
.name = "JSExpr",
|
||||
.read_only = true,
|
||||
},
|
||||
.{
|
||||
.toString = .{
|
||||
.rfn = toString,
|
||||
},
|
||||
// .toNumber = .{
|
||||
// .rfn = toNumber,
|
||||
// },
|
||||
},
|
||||
.{
|
||||
.tag = .{
|
||||
.get = getTag,
|
||||
.ro = true,
|
||||
},
|
||||
.tagName = .{
|
||||
.get = getTagName,
|
||||
.ro = true,
|
||||
},
|
||||
.position = .{
|
||||
.get = getPosition,
|
||||
.ro = true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// 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,
|
||||
function: js.JSObjectRef,
|
||||
thisObject: js.JSObjectRef,
|
||||
arguments: []const js.JSValueRef,
|
||||
exception: js.ExceptionRef,
|
||||
) js.JSObjectRef {
|
||||
switch (this.expr.data) {
|
||||
.e_string => |str| {
|
||||
if (str.isBlank()) {
|
||||
return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
|
||||
}
|
||||
|
||||
if (str.isUTF8()) {
|
||||
return JSC.ZigString.init(str.utf8).toValue(JavaScript.VirtualMachine.vm.global).asRef();
|
||||
} else {
|
||||
return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len));
|
||||
}
|
||||
},
|
||||
.e_template => |template| {
|
||||
const str = template.head;
|
||||
|
||||
if (str.isBlank()) {
|
||||
return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
|
||||
}
|
||||
|
||||
if (str.isUTF8()) {
|
||||
return JSC.ZigString.init(str.utf8).toValue(JavaScript.VirtualMachine.vm.global).asRef();
|
||||
} else {
|
||||
return js.JSValueMakeString(ctx, js.JSStringCreateWithCharactersNoCopy(str.value.ptr, str.value.len));
|
||||
}
|
||||
},
|
||||
else => {
|
||||
return JSC.ZigString.init("").toValue(JavaScript.VirtualMachine.vm.global).asRef();
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getTag(
|
||||
this: *JSExpr,
|
||||
ctx: js.JSContextRef,
|
||||
thisObject: js.JSValueRef,
|
||||
prop: js.JSStringRef,
|
||||
exception: js.ExceptionRef,
|
||||
) js.JSObjectRef {
|
||||
return JSC.JSValue.jsNumberFromU16(@intCast(u16, @enumToInt(std.meta.activeTag(this.expr.data)))).asRef();
|
||||
}
|
||||
pub fn getTagName(
|
||||
this: *JSExpr,
|
||||
ctx: js.JSContextRef,
|
||||
thisObject: js.JSValueRef,
|
||||
prop: js.JSStringRef,
|
||||
exception: js.ExceptionRef,
|
||||
) js.JSObjectRef {
|
||||
return JSC.ZigString.init(@tagName(this.expr.data)).toValue(JavaScript.VirtualMachine.vm.global).asRef();
|
||||
}
|
||||
pub fn getPosition(
|
||||
this: *JSExpr,
|
||||
ctx: js.JSContextRef,
|
||||
thisObject: js.JSValueRef,
|
||||
prop: js.JSStringRef,
|
||||
exception: js.ExceptionRef,
|
||||
) js.JSObjectRef {
|
||||
return JSC.JSValue.jsNumberFromInt32(this.expr.loc.start).asRef();
|
||||
}
|
||||
};
|
||||
|
||||
resolver: *Resolver,
|
||||
vm: *JavaScript.VirtualMachine = undefined,
|
||||
|
||||
resolved: ResolveResult = undefined,
|
||||
disabled: bool = false,
|
||||
|
||||
pub fn init(
|
||||
allocator: *std.mem.Allocator,
|
||||
resolver: *Resolver,
|
||||
resolved: ResolveResult,
|
||||
log: *logger.Log,
|
||||
env: *DotEnv.Loader,
|
||||
function_name: string,
|
||||
specifier: string,
|
||||
hash: i32,
|
||||
) !Macro {
|
||||
const path = resolved.path_pair.primary;
|
||||
|
||||
var vm: *JavaScript.VirtualMachine = if (JavaScript.VirtualMachine.vm_loaded)
|
||||
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;
|
||||
};
|
||||
|
||||
vm.enableMacroMode();
|
||||
|
||||
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,
|
||||
.resolver = resolver,
|
||||
};
|
||||
}
|
||||
|
||||
pub const Runner = struct {
|
||||
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(
|
||||
macro: Macro,
|
||||
log: *logger.Log,
|
||||
allocator: *std.mem.Allocator,
|
||||
function_name: string,
|
||||
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] = 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] =
|
||||
JSExpr.Class.make(
|
||||
macro.vm.global.ref(),
|
||||
&expr_nodes_buf[i + 1],
|
||||
);
|
||||
}
|
||||
args_buf[args.len + 2] = null;
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
test "Binding.init" {
|
||||
var binding = Binding.alloc(
|
||||
std.heap.page_allocator,
|
||||
|
||||
Reference in New Issue
Block a user