mirror of
https://github.com/oven-sh/bun
synced 2026-02-05 00:18:53 +00:00
Compare commits
8 Commits
ciro/fix-a
...
snoglobe/a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a6c6bf3c0 | ||
|
|
549f14fa70 | ||
|
|
512615684f | ||
|
|
b01bd3566d | ||
|
|
e13a497cf4 | ||
|
|
927e21dc94 | ||
|
|
e00be30ef6 | ||
|
|
6aefea45b2 |
15
.vscode/launch.json
generated
vendored
15
.vscode/launch.json
generated
vendored
@@ -146,6 +146,21 @@
|
||||
"action": "openExternally",
|
||||
},
|
||||
},
|
||||
// bun build [file]
|
||||
{
|
||||
"type": "lldb",
|
||||
"request": "launch",
|
||||
"name": "bun build [file]",
|
||||
"program": "${workspaceFolder}/build/debug/bun-debug",
|
||||
"args": ["build", "${fileBasename}"],
|
||||
"cwd": "${fileDirname}",
|
||||
"env": {
|
||||
"FORCE_COLOR": "0",
|
||||
"BUN_DEBUG_QUIET_LOGS": "1",
|
||||
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
|
||||
},
|
||||
"console": "internalConsole",
|
||||
},
|
||||
// bun run [file]
|
||||
{
|
||||
"type": "lldb",
|
||||
|
||||
@@ -280,6 +280,7 @@ pub const Flags = struct {
|
||||
is_static,
|
||||
was_shorthand,
|
||||
is_spread,
|
||||
is_accessor,
|
||||
|
||||
pub inline fn init(fields: Fields) Set {
|
||||
return Set.init(fields);
|
||||
@@ -888,6 +889,7 @@ pub const G = struct {
|
||||
normal,
|
||||
get,
|
||||
set,
|
||||
accessor,
|
||||
spread,
|
||||
declare,
|
||||
abstract,
|
||||
|
||||
@@ -235,6 +235,7 @@ pub const PropertyModifierKeyword = enum {
|
||||
p_readonly,
|
||||
p_set,
|
||||
p_static,
|
||||
p_accessor,
|
||||
|
||||
pub const List = ComptimeStringMap(PropertyModifierKeyword, .{
|
||||
.{ "abstract", .p_abstract },
|
||||
@@ -248,6 +249,7 @@ pub const PropertyModifierKeyword = enum {
|
||||
.{ "readonly", .p_readonly },
|
||||
.{ "set", .p_set },
|
||||
.{ "static", .p_static },
|
||||
.{ "accessor", .p_accessor },
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -13287,6 +13287,11 @@ fn NewParser_(
|
||||
return try p.parseProperty(.set, opts, null);
|
||||
}
|
||||
},
|
||||
.p_accessor => {
|
||||
if (!opts.is_async and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == .p_accessor) {
|
||||
return try p.parseProperty(.accessor, opts, null);
|
||||
}
|
||||
},
|
||||
.p_async => {
|
||||
if (!opts.is_async and (js_lexer.PropertyModifierKeyword.List.get(raw) orelse .p_static) == .p_async and !p.lexer.has_newline_before) {
|
||||
opts.is_async = true;
|
||||
@@ -13464,7 +13469,7 @@ fn NewParser_(
|
||||
|
||||
// Parse a class field with an optional initial value
|
||||
if (opts.is_class and
|
||||
kind == .normal and !opts.is_async and
|
||||
(kind == .normal or kind == .accessor) and !opts.is_async and
|
||||
!opts.is_generator and
|
||||
p.lexer.token != .t_open_paren and
|
||||
!has_type_parameters and
|
||||
@@ -13545,6 +13550,7 @@ fn NewParser_(
|
||||
.flags = Flags.Property.init(.{
|
||||
.is_computed = is_computed,
|
||||
.is_static = opts.is_static,
|
||||
.is_accessor = kind == .accessor,
|
||||
}),
|
||||
.key = key,
|
||||
.initializer = initializer,
|
||||
@@ -13763,7 +13769,6 @@ fn NewParser_(
|
||||
// This property may turn out to be a type in TypeScript, which should be ignored
|
||||
if (try p.parseProperty(.normal, &opts, null)) |property| {
|
||||
properties.append(property) catch unreachable;
|
||||
|
||||
// Forbid decorators on class constructors
|
||||
if (opts.ts_decorators.len > 0) {
|
||||
switch ((property.key orelse p.panic("Internal error: Expected property {any} to have a key.", .{property})).data) {
|
||||
@@ -17661,7 +17666,7 @@ fn NewParser_(
|
||||
return expr;
|
||||
}
|
||||
|
||||
_ = p.visitClass(expr.loc, e_, Ref.None);
|
||||
_ = p.visitClass(expr.loc, e_, Ref.None) catch bun.outOfMemory();
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@@ -19321,7 +19326,7 @@ fn NewParser_(
|
||||
return;
|
||||
},
|
||||
.s_class => |class| {
|
||||
_ = p.visitClass(s2.loc, &class.class, data.default_name.ref.?);
|
||||
_ = p.visitClass(s2.loc, &class.class, data.default_name.ref.?) catch bun.outOfMemory();
|
||||
|
||||
if (p.is_control_flow_dead)
|
||||
return;
|
||||
@@ -20070,7 +20075,7 @@ fn NewParser_(
|
||||
}
|
||||
}
|
||||
|
||||
_ = p.visitClass(stmt.loc, &data.class, Ref.None);
|
||||
_ = p.visitClass(stmt.loc, &data.class, Ref.None) catch bun.outOfMemory();
|
||||
|
||||
// Remove the export flag inside a namespace
|
||||
const was_export_inside_namespace = data.is_export and p.enclosing_namespace_arg_ref != null;
|
||||
@@ -20897,7 +20902,7 @@ fn NewParser_(
|
||||
|
||||
// TODO: when we have the `accessor` modifier, add `and !prop.flags.contains(.has_accessor_modifier)` to
|
||||
// the if statement.
|
||||
const descriptor_kind: Expr = if (!prop.flags.contains(.is_method))
|
||||
const descriptor_kind: Expr = if (!prop.flags.contains(.is_method) and !prop.flags.contains(.is_accessor))
|
||||
p.newExpr(E.Undefined{}, loc)
|
||||
else
|
||||
p.newExpr(E.Null{}, loc);
|
||||
@@ -21655,7 +21660,7 @@ fn NewParser_(
|
||||
return res;
|
||||
}
|
||||
|
||||
fn visitClass(p: *P, name_scope_loc: logger.Loc, class: *G.Class, default_name_ref: Ref) Ref {
|
||||
fn visitClass(p: *P, name_scope_loc: logger.Loc, class: *G.Class, default_name_ref: Ref) !Ref {
|
||||
if (only_scan_imports_and_do_not_visit) {
|
||||
@compileError("only_scan_imports_and_do_not_visit must not run this.");
|
||||
}
|
||||
@@ -21705,6 +21710,7 @@ fn NewParser_(
|
||||
}
|
||||
|
||||
var constructor_function: ?*E.Function = null;
|
||||
var saw_accessor = false;
|
||||
for (class.properties) |*property| {
|
||||
if (property.kind == .class_static_block) {
|
||||
const old_fn_or_arrow_data = p.fn_or_arrow_data_visit;
|
||||
@@ -21804,6 +21810,140 @@ fn NewParser_(
|
||||
property.initializer = p.visitExpr(val);
|
||||
}
|
||||
}
|
||||
|
||||
if (property.kind == .accessor) {
|
||||
saw_accessor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (saw_accessor) {
|
||||
var out_properties = Property.List.init(&.{});
|
||||
for (class.properties) |*property| {
|
||||
if (property.kind != .accessor) {
|
||||
try out_properties.push(p.allocator, property.*);
|
||||
continue;
|
||||
}
|
||||
if (@as(Expr.Tag, property.key.?.data) != .e_string and @as(Expr.Tag, property.key.?.data) != .e_private_identifier) {
|
||||
try p.log.addError(p.source, property.key.?.loc, "'accessor' property key must be a string or private identifier");
|
||||
try out_properties.push(p.allocator, property.*);
|
||||
} else if (property.flags.contains(.is_method)) {
|
||||
try p.log.addError(p.source, property.value.?.loc, "'accessor' property cannot be a method");
|
||||
try out_properties.push(p.allocator, property.*);
|
||||
} else if (property.flags.contains(.is_computed)) {
|
||||
try p.log.addError(p.source, property.key.?.loc, "'accessor' property key cannot be computed");
|
||||
try out_properties.push(p.allocator, property.*);
|
||||
} else {
|
||||
// 1. change property name to #name
|
||||
var old_property_name: string = undefined;
|
||||
var new_property_name: string = undefined;
|
||||
var prop_name_ref: Ref = undefined;
|
||||
var accessor_key: Expr = undefined;
|
||||
const is_private_prop = @as(Expr.Tag, property.key.?.data) != .e_string;
|
||||
|
||||
const loc = property.key.?.loc;
|
||||
|
||||
if (!is_private_prop) {
|
||||
old_property_name = try property.key.?.data.e_string.string(p.allocator);
|
||||
new_property_name = try std.fmt.allocPrint(p.allocator, "#{s}", .{old_property_name});
|
||||
accessor_key = p.newExpr(E.String{ .data = old_property_name }, loc);
|
||||
} else {
|
||||
old_property_name = p.symbols.items[property.key.?.data.e_private_identifier.ref.innerIndex()].original_name;
|
||||
new_property_name = try std.fmt.allocPrint(p.allocator, "#_{s}", .{old_property_name[1..]});
|
||||
accessor_key = property.key.?;
|
||||
}
|
||||
|
||||
prop_name_ref = p.generateTempRefKind(.private_field, new_property_name);
|
||||
try p.current_scope.generated.push(p.allocator, prop_name_ref);
|
||||
|
||||
property.key = p.newExpr(E.PrivateIdentifier{ .ref = prop_name_ref }, loc);
|
||||
property.kind = .normal;
|
||||
|
||||
try out_properties.push(p.allocator, property.*);
|
||||
|
||||
// 2. generate getter and setter
|
||||
try out_properties.push(p.allocator, Property{
|
||||
.kind = .get,
|
||||
.ts_decorators = property.ts_decorators,
|
||||
.key = accessor_key,
|
||||
.flags = Flags.Property.init(.{ .is_method = true, .is_static = property.flags.contains(.is_static) }),
|
||||
.value = p.newExpr(E.Function{
|
||||
.func = .{
|
||||
.body = .{
|
||||
.loc = loc,
|
||||
.stmts = try p.allocator.dupe(
|
||||
Stmt,
|
||||
&.{p.s(
|
||||
S.Return{
|
||||
.value = p.newExpr(
|
||||
E.Index{
|
||||
.target = p.newExpr(E.This{}, loc),
|
||||
.index = p.newExpr(E.PrivateIdentifier{
|
||||
.ref = prop_name_ref,
|
||||
}, loc),
|
||||
},
|
||||
loc,
|
||||
),
|
||||
},
|
||||
loc,
|
||||
)},
|
||||
),
|
||||
},
|
||||
},
|
||||
}, loc),
|
||||
});
|
||||
|
||||
const underscore_ref = try p.declareGeneratedSymbol(.other, "_");
|
||||
try out_properties.push(p.allocator, Property{
|
||||
.kind = .set,
|
||||
.ts_decorators = property.ts_decorators,
|
||||
.key = accessor_key,
|
||||
.flags = Flags.Property.init(.{ .is_method = true, .is_static = property.flags.contains(.is_static) }),
|
||||
.value = p.newExpr(E.Function{
|
||||
.func = .{
|
||||
.args = try p.allocator.dupe(Arg, &.{
|
||||
.{
|
||||
.binding = p.b(
|
||||
B.Identifier{
|
||||
.ref = underscore_ref.ref,
|
||||
},
|
||||
loc,
|
||||
),
|
||||
},
|
||||
}),
|
||||
.body = .{
|
||||
.loc = loc,
|
||||
.stmts = try p.allocator.dupe(
|
||||
Stmt,
|
||||
&.{p.s(
|
||||
S.SExpr{
|
||||
.value = Expr.assign(
|
||||
p.newExpr(
|
||||
E.Index{
|
||||
.target = p.newExpr(E.This{}, loc),
|
||||
.index = p.newExpr(E.PrivateIdentifier{
|
||||
.ref = prop_name_ref,
|
||||
}, loc),
|
||||
},
|
||||
loc,
|
||||
),
|
||||
p.newExpr(
|
||||
E.Identifier{
|
||||
.ref = underscore_ref.ref,
|
||||
},
|
||||
loc,
|
||||
),
|
||||
),
|
||||
},
|
||||
loc,
|
||||
)},
|
||||
),
|
||||
},
|
||||
},
|
||||
}, loc),
|
||||
});
|
||||
}
|
||||
}
|
||||
class.properties = out_properties.slice();
|
||||
}
|
||||
|
||||
// note: our version assumes useDefineForClassFields is true
|
||||
@@ -22753,15 +22893,22 @@ fn NewParser_(
|
||||
/// When not transpiling we dont use the renamer, so our solution is to generate really
|
||||
/// hard to collide with variables, instead of actually making things collision free
|
||||
pub fn generateTempRef(p: *P, default_name: ?string) Ref {
|
||||
return p.generateTempRefWithScope(default_name, p.current_scope);
|
||||
return p.generateTempRefWithScope(default_name, .other, p.current_scope);
|
||||
}
|
||||
|
||||
pub fn generateTempRefWithScope(p: *P, default_name: ?string, scope: *Scope) Ref {
|
||||
pub fn generateTempRefKind(p: *P, kind: Symbol.Kind, default_name: ?string) Ref {
|
||||
return p.generateTempRefWithScope(default_name, kind, p.current_scope);
|
||||
}
|
||||
|
||||
pub fn generateTempRefWithScope(p: *P, default_name: ?string, kind: Symbol.Kind, scope: *Scope) Ref {
|
||||
const name = (if (p.willUseRenamer()) default_name else null) orelse brk: {
|
||||
p.temp_ref_count += 1;
|
||||
break :brk std.fmt.allocPrint(p.allocator, "__bun_temp_ref_{x}$", .{p.temp_ref_count}) catch bun.outOfMemory();
|
||||
if (!kind.isPrivate())
|
||||
break :brk std.fmt.allocPrint(p.allocator, "__bun_temp_ref_{x}$", .{p.temp_ref_count}) catch bun.outOfMemory()
|
||||
else
|
||||
break :brk std.fmt.allocPrint(p.allocator, "#__bun_temp_ref_{x}$", .{p.temp_ref_count}) catch bun.outOfMemory();
|
||||
};
|
||||
const ref = p.newSymbol(.other, name) catch bun.outOfMemory();
|
||||
const ref = p.newSymbol(kind, name) catch bun.outOfMemory();
|
||||
|
||||
p.temp_refs_to_declare.append(p.allocator, .{
|
||||
.ref = ref,
|
||||
@@ -23141,7 +23288,7 @@ fn NewParser_(
|
||||
|
||||
ctx_storage.* = .{
|
||||
.hasher = std.hash.Wyhash.init(0),
|
||||
.signature_cb = p.generateTempRefWithScope("_s", scope),
|
||||
.signature_cb = p.generateTempRefWithScope("_s", .other, scope),
|
||||
.user_hooks = .{},
|
||||
};
|
||||
|
||||
|
||||
@@ -1417,7 +1417,6 @@ fn NewPrinter(
|
||||
pub fn printSymbol(p: *Printer, ref: Ref) void {
|
||||
bun.assert(!ref.isNull());
|
||||
const name = p.renamer.nameForSymbol(ref);
|
||||
|
||||
p.printIdentifier(name);
|
||||
}
|
||||
pub fn printClauseAlias(p: *Printer, alias: string) void {
|
||||
@@ -3330,7 +3329,8 @@ fn NewPrinter(
|
||||
.e_template_part,
|
||||
=> {
|
||||
if (Environment.isDebug)
|
||||
Output.panic("Unexpected expression of type .{s}", .{@tagName(expr.data)});
|
||||
//Output.panic("Unexpected expression of type .{s}", .{@tagName(expr.data)});
|
||||
p.fmt("/* Unexpected expression of type .{s} */", .{@tagName(expr.data)}) catch bun.outOfMemory();
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -3784,7 +3784,7 @@ fn NewPrinter(
|
||||
},
|
||||
}
|
||||
|
||||
if (item.kind != .normal) {
|
||||
if (item.kind != .normal and item.kind != .accessor) {
|
||||
if (comptime is_json) {
|
||||
bun.unreachablePanic("item.kind must be normal in json, received: {any}", .{item.kind});
|
||||
}
|
||||
|
||||
@@ -504,9 +504,10 @@ pub const NumberRenamer = struct {
|
||||
var inner: *bun.BabyList(string) = &r.names[ref.sourceIndex()];
|
||||
if (inner.len > ref.innerIndex() and inner.at(ref.innerIndex()).len > 0) return;
|
||||
|
||||
// Don't rename unbound symbols, symbols marked as reserved names, labels, or private names
|
||||
// Don't rename unbound symbols, symbols marked as reserved names or labels
|
||||
const symbol = r.symbols.get(ref).?;
|
||||
if (symbol.slotNamespace() != .default) {
|
||||
const ns = symbol.slotNamespace();
|
||||
if (ns != .default and ns != .private_name) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -697,7 +698,10 @@ pub const NumberRenamer = struct {
|
||||
pub fn find(this: *NumberScope, name: []const u8) NameUse {
|
||||
// This version doesn't allocate
|
||||
if (comptime Environment.allow_assert)
|
||||
bun.assert(JSLexer.isIdentifier(name));
|
||||
if (name[0] == '#')
|
||||
bun.assert(JSLexer.isIdentifier(name[1..]))
|
||||
else
|
||||
bun.assert(JSLexer.isIdentifier(name));
|
||||
|
||||
// avoid rehashing the same string over for each scope
|
||||
const ctx = bun.StringHashMapContext.pre(name);
|
||||
@@ -725,7 +729,10 @@ pub const NumberRenamer = struct {
|
||||
|
||||
/// Caller must use an arena allocator
|
||||
pub fn findUnusedName(this: *NumberScope, allocator: std.mem.Allocator, temp_allocator: std.mem.Allocator, input_name: []const u8) UnusedName {
|
||||
var name = bun.MutableString.ensureValidIdentifier(input_name, temp_allocator) catch unreachable;
|
||||
var name = if (input_name[0] == '#')
|
||||
std.fmt.allocPrint(allocator, "#{s}", .{bun.MutableString.ensureValidIdentifier(input_name[1..], temp_allocator) catch unreachable}) catch bun.outOfMemory()
|
||||
else
|
||||
bun.MutableString.ensureValidIdentifier(input_name, temp_allocator) catch bun.outOfMemory();
|
||||
|
||||
switch (NameUse.find(this, name)) {
|
||||
.unused => {},
|
||||
|
||||
Reference in New Issue
Block a user