This commit is contained in:
Jarred Sumner
2021-09-24 15:33:02 -07:00
parent 29b986684d
commit bdfb5a91b1
3 changed files with 681 additions and 4 deletions

106
src/ast/ast.js Normal file
View 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 {},
};
}

View File

@@ -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;
}
};

View File

@@ -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,