aboutsummaryrefslogtreecommitdiff
path: root/src/javascript
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/javascript/jsc/bindings/bindings.cpp13
-rw-r--r--src/javascript/jsc/bindings/bindings.zig6
-rw-r--r--src/javascript/jsc/javascript.zig130
3 files changed, 135 insertions, 14 deletions
diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp
index 407b452d2..407a03b96 100644
--- a/src/javascript/jsc/bindings/bindings.cpp
+++ b/src/javascript/jsc/bindings/bindings.cpp
@@ -512,7 +512,7 @@ bWTF__String JSC__JSString__value(JSC__JSString *arg0, JSC__JSGlobalObject *arg1
JSC__JSValue JSC__JSModuleLoader__callExportedFunction(JSC__JSGlobalObject *globalObject,
ZigString specifier, ZigString functionName,
JSC__JSValue *arguments,
- unsigned char args_len,
+ unsigned char argumentsCount,
ZigException *zig_exception) {
JSC::VM &vm = globalObject->vm();
JSC::JSLockHolder lock(vm);
@@ -531,22 +531,21 @@ JSC__JSValue JSC__JSModuleLoader__callExportedFunction(JSC__JSGlobalObject *glob
if (JSC::JSModuleRecord *record =
JSC::jsDynamicCast<JSC::JSModuleRecord *>(vm, entry->getDirect(vm, recordIdentifier))) {
auto fn_impl = WTF::ExternalStringImpl::createStatic(functionName.ptr, functionName.len);
- auto fn_ident = reinterpret_cast<WTF::UniquedStringImpl *>(specifier_impl.ptr());
- auto env = record->getModuleNamespace(globalObject);
+ auto fn_ident = reinterpret_cast<WTF::UniquedStringImpl *>(fn_impl.ptr());
+ auto moduleNamespace = record->getModuleNamespace(globalObject);
if (JSC::JSValue macroFunctionExport =
- env->getIfPropertyExists(globalObject, JSC::PropertyName(fn_ident))) {
+ moduleNamespace->getIfPropertyExists(globalObject, JSC::PropertyName(fn_ident))) {
if (JSC::JSObject *macroFunction = JSC::asObject(macroFunctionExport.asCell())) {
- // auto functionNameImpl =
- // WTF::ExternalStringImpl::createStatic(functionName.ptr, functionName.len);
JSC::VMEntryScope entryScope(vm, globalObject);
auto callData = JSC::getCallData(vm, macroFunction);
if (callData.type == JSC::CallData::Type::None) return JSC::JSValue::encode({});
JSC::MarkedArgumentBuffer argList;
- for (size_t i = 0; i < args_len; i++) argList.append(JSC::JSValue::decode(arguments[i]));
+ for (size_t i = 0; i < argumentsCount; i++)
+ argList.append(JSC::JSValue::decode(arguments[i]));
NakedPtr<JSC::Exception> uncaughtException;
JSC::JSValue reval = JSC::call(globalObject, macroFunction, callData,
diff --git a/src/javascript/jsc/bindings/bindings.zig b/src/javascript/jsc/bindings/bindings.zig
index 96c1767b4..09fa04d3b 100644
--- a/src/javascript/jsc/bindings/bindings.zig
+++ b/src/javascript/jsc/bindings/bindings.zig
@@ -1445,11 +1445,11 @@ pub const JSValue = enum(i64) {
}
pub inline fn asRef(this: JSValue) C_API.JSValueRef {
- return @intToPtr(C_API.JSValueRef, @intCast(usize, @enumToInt(this)));
+ return @intToPtr(C_API.JSValueRef, @bitCast(usize, @enumToInt(this)));
}
pub inline fn fromRef(this: C_API.JSValueRef) JSValue {
- return @intToEnum(JSValue, @intCast(i64, @ptrToInt(this)));
+ return @intToEnum(JSValue, @bitCast(i64, @ptrToInt(this)));
}
pub inline fn asObjectRef(this: JSValue) C_API.JSObjectRef {
@@ -1457,7 +1457,7 @@ pub const JSValue = enum(i64) {
}
pub inline fn asVoid(this: JSValue) *c_void {
- return @intToPtr(*c_void, @intCast(usize, @enumToInt(this)));
+ return @intToPtr(*c_void, @bitCast(usize, @enumToInt(this)));
}
pub const Extern = [_][]const u8{ "toZigString", "createStringArray", "createEmptyObject", "putRecord", "asPromise", "isClass", "getNameProperty", "getClassName", "getErrorsProperty", "toInt32", "toBoolean", "isInt32", "isIterable", "forEach", "isAggregateError", "toZigException", "isException", "toWTFString", "hasProperty", "getPropertyNames", "getDirect", "putDirect", "get", "getIfExists", "asString", "asObject", "asNumber", "isError", "jsNull", "jsUndefined", "jsTDZValue", "jsBoolean", "jsDoubleNumber", "jsNumberFromDouble", "jsNumberFromChar", "jsNumberFromU16", "jsNumberFromInt32", "jsNumberFromInt64", "jsNumberFromUint64", "isUndefined", "isNull", "isUndefinedOrNull", "isBoolean", "isAnyInt", "isUInt32AsAnyInt", "isInt32AsAnyInt", "isNumber", "isString", "isBigInt", "isHeapBigInt", "isBigInt32", "isSymbol", "isPrimitive", "isGetterSetter", "isCustomGetterSetter", "isObject", "isCell", "asCell", "toString", "toStringOrNull", "toPropertyKey", "toPropertyKeyValue", "toObject", "toString", "getPrototype", "getPropertyByPropertyName", "eqlValue", "eqlCell", "isCallable" };
diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig
index 9e6f5defd..e461f48d3 100644
--- a/src/javascript/jsc/javascript.zig
+++ b/src/javascript/jsc/javascript.zig
@@ -9,8 +9,10 @@ const Api = @import("../../api/schema.zig").Api;
const options = @import("../../options.zig");
const Bundler = @import("../../bundler.zig").Bundler;
const ServerEntryPoint = @import("../../bundler.zig").ServerEntryPoint;
+const MacroEntryPoint = @import("../../bundler.zig").MacroEntryPoint;
const js_printer = @import("../../js_printer.zig");
const js_parser = @import("../../js_parser.zig");
+const js_ast = @import("../../js_ast.zig");
const hash_map = @import("../../hash_map.zig");
const http = @import("../../http.zig");
const ImportKind = ast.ImportKind;
@@ -158,6 +160,41 @@ pub const Bun = struct {
return JSValue.createStringArray(VirtualMachine.vm.global, styles.ptr, styles.len).asRef();
}
+ pub fn registerMacro(
+ this: void,
+ ctx: js.JSContextRef,
+ function: js.JSObjectRef,
+ thisObject: js.JSObjectRef,
+ arguments: []const js.JSValueRef,
+ exception: js.ExceptionRef,
+ ) js.JSValueRef {
+ if (arguments.len != 2 or !js.JSValueIsNumber(ctx, arguments[0])) {
+ JSError(getAllocator(ctx), "Internal error registering macros: invalid args", .{}, ctx, exception);
+ return js.JSValueMakeUndefined(ctx);
+ }
+ // TODO: make this faster
+ const id = @truncate(i32, @floatToInt(i64, js.JSValueToNumber(ctx, arguments[0], exception)));
+ if (id == -1 or id == 0) {
+ JSError(getAllocator(ctx), "Internal error registering macros: invalid id", .{}, ctx, exception);
+ return js.JSValueMakeUndefined(ctx);
+ }
+
+ if (!js.JSValueIsObject(ctx, arguments[1]) or !js.JSObjectIsFunction(ctx, arguments[1])) {
+ JSError(getAllocator(ctx), "Macro must be a function. Received: {s}", .{@tagName(js.JSValueGetType(ctx, arguments[1]))}, ctx, exception);
+ return js.JSValueMakeUndefined(ctx);
+ }
+
+ var get_or_put_result = VirtualMachine.vm.macros.getOrPut(id) catch unreachable;
+ if (get_or_put_result.found_existing) {
+ js.JSValueUnprotect(ctx, get_or_put_result.value_ptr.*);
+ }
+
+ js.JSValueProtect(ctx, arguments[1]);
+ get_or_put_result.value_ptr.* = arguments[1];
+
+ return js.JSValueMakeUndefined(ctx);
+ }
+
pub fn getRouteFiles(
this: void,
ctx: js.JSContextRef,
@@ -330,6 +367,13 @@ pub const Bun = struct {
.@"return" = "string",
},
},
+ .registerMacro = .{
+ .rfn = Bun.registerMacro,
+ .ts = d.ts{
+ .name = "registerMacro",
+ .@"return" = "undefined",
+ },
+ },
},
.{
.main = .{
@@ -367,6 +411,9 @@ pub const VirtualMachine = struct {
allocator: *std.mem.Allocator,
node_modules: ?*NodeModuleBundle = null,
bundler: Bundler,
+
+ macro_mode: bool = false,
+
watcher: ?*http.Watcher = null,
console: *ZigConsoleClient,
log: *logger.Log,
@@ -375,6 +422,7 @@ pub const VirtualMachine = struct {
process: js.JSObjectRef = null,
blobs: *Blob.Group = undefined,
flush_list: std.ArrayList(string),
+ macro_entry_points: std.AutoArrayHashMap(i32, *MacroEntryPoint),
entry_point: ServerEntryPoint = undefined,
arena: *std.heap.ArenaAllocator = undefined,
@@ -383,9 +431,22 @@ pub const VirtualMachine = struct {
transpiled_count: usize = 0,
resolved_count: usize = 0,
had_errors: bool = false,
+ macros: MacroMap,
pub threadlocal var vm_loaded = false;
pub threadlocal var vm: *VirtualMachine = undefined;
+ pub const MacroMap = std.AutoArrayHashMap(i32, js.JSObjectRef);
+
+ pub fn enableMacroMode(this: *VirtualMachine) void {
+ this.bundler.options.platform = .bunMacro;
+ this.macro_mode = true;
+ }
+
+ pub fn disableMacroMode(this: *VirtualMachine) void {
+ this.bundler.options.platform = .bun;
+ this.macro_mode = false;
+ }
+
pub fn init(
allocator: *std.mem.Allocator,
_args: Api.TransformOptions,
@@ -420,6 +481,8 @@ pub const VirtualMachine = struct {
.console = console,
.node_modules = bundler.options.node_modules_bundle,
.log = log,
+ .macros = MacroMap.init(allocator),
+ .macro_entry_points = @TypeOf(VirtualMachine.vm.macro_entry_points).init(allocator),
.flush_list = std.ArrayList(string).init(allocator),
.blobs = try Blob.Group.init(allocator),
};
@@ -550,7 +613,7 @@ pub const VirtualMachine = struct {
var parse_result = ParseResult{ .source = vm.entry_point.source, .ast = main_ast, .loader = .js, .input_fd = null };
var file_path = Fs.Path.init(bundler.fs.top_level_dir);
file_path.name.dir = bundler.fs.top_level_dir;
- file_path.name.base = "bun:main";
+ file_path.name.base = main_file_name;
try bundler.linker.link(
file_path,
&parse_result,
@@ -579,6 +642,19 @@ pub const VirtualMachine = struct {
.hash = 0,
.bytecodecache_fd = 0,
};
+ } else if (_specifier.len > js_ast.Macro.namespaceWithColon.len and
+ strings.eqlComptimeIgnoreLen(_specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon))
+ {
+ if (vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(_specifier))) |entry| {
+ return ResolvedSource{
+ .allocator = null,
+ .source_code = ZigString.init(entry.source.contents),
+ .specifier = ZigString.init(_specifier),
+ .source_url = ZigString.init(_specifier),
+ .hash = 0,
+ .bytecodecache_fd = 0,
+ };
+ }
}
const specifier = normalizeSpecifier(_specifier);
@@ -691,10 +767,16 @@ pub const VirtualMachine = struct {
ret.result = null;
ret.path = vm.entry_point.source.path.text;
return;
+ } else if (specifier.len > js_ast.Macro.namespaceWithColon.len and strings.eqlComptimeIgnoreLen(specifier[0..js_ast.Macro.namespaceWithColon.len], js_ast.Macro.namespaceWithColon)) {
+ ret.result = null;
+ ret.path = specifier;
+ return;
}
+ const is_special_source = strings.eqlComptime(source, main_file_name) or js_ast.Macro.isMacroPath(source);
+
const result = try vm.bundler.resolver.resolve(
- if (!strings.eqlComptime(source, main_file_name)) Fs.PathName.init(source).dirWithTrailingSlash() else VirtualMachine.vm.bundler.fs.top_level_dir,
+ if (!is_special_source) Fs.PathName.init(source).dirWithTrailingSlash() else VirtualMachine.vm.bundler.fs.top_level_dir,
specifier,
.stmt,
);
@@ -970,6 +1052,46 @@ pub const VirtualMachine = struct {
return promise;
}
+ pub fn loadMacroEntryPoint(this: *VirtualMachine, entry_path: string, function_name: string, specifier: string, hash: i32) !*JSInternalPromise {
+ var entry_point_entry = try this.macro_entry_points.getOrPut(hash);
+
+ if (!entry_point_entry.found_existing) {
+ var macro_entry_pointer: *MacroEntryPoint = this.allocator.create(MacroEntryPoint) catch unreachable;
+ entry_point_entry.value_ptr.* = macro_entry_pointer;
+ try macro_entry_pointer.generate(&this.bundler, Fs.PathName.init(entry_path), function_name, hash, specifier);
+ }
+ var entry_point = entry_point_entry.value_ptr.*;
+
+ var promise: *JSInternalPromise = undefined;
+ // We first import the node_modules bundle. This prevents any potential TDZ issues.
+ // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick.
+ if (this.node_modules != null) {
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(std.mem.span(bun_file_import_path)));
+
+ this.global.vm().drainMicrotasks();
+
+ while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
+ this.global.vm().drainMicrotasks();
+ }
+
+ if (promise.status(this.global.vm()) == JSPromise.Status.Rejected) {
+ return promise;
+ }
+
+ _ = promise.result(this.global.vm());
+ }
+
+ promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.init(entry_point.source.path.text));
+
+ this.global.vm().drainMicrotasks();
+
+ while (promise.status(this.global.vm()) == JSPromise.Status.Pending) {
+ this.global.vm().drainMicrotasks();
+ }
+
+ return promise;
+ }
+
// When the Error-like object is one of our own, it's best to rely on the object directly instead of serializing it to a ZigException.
// This is for:
// - BuildError
@@ -1049,7 +1171,7 @@ pub const VirtualMachine = struct {
var build_error = private_data_ptr.as(BuildError);
if (!build_error.logged) {
var writer = Output.errorWriter();
- build_error.msg.formatWriter(@TypeOf(writer), writer, allow_ansi_color) catch {};
+ build_error.msg.writeFormat(writer, allow_ansi_color) catch {};
build_error.logged = true;
}
this.had_errors = this.had_errors or build_error.msg.kind == .err;
@@ -1065,7 +1187,7 @@ pub const VirtualMachine = struct {
var resolve_error = private_data_ptr.as(ResolveError);
if (!resolve_error.logged) {
var writer = Output.errorWriter();
- resolve_error.msg.formatWriter(@TypeOf(writer), writer, allow_ansi_color) catch {};
+ resolve_error.msg.writeFormat(writer, allow_ansi_color) catch {};
resolve_error.logged = true;
}