diff --git a/src/ast/ast.js b/src/ast/ast.js new file mode 100644 index 00000000000000..06be333c5c7767 --- /dev/null +++ b/src/ast/ast.js @@ -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 {}, + }; +} diff --git a/src/bundler.zig b/src/bundler.zig index 77534e602e5868..ef38f7d96c6139 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -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; + } +}; diff --git a/src/js_ast.zig b/src/js_ast.zig index 0b98d1a3848eef..9ddafdf8024b90 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{ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "