Compare commits

...

8 Commits

Author SHA1 Message Date
Jarred Sumner
5a6c6bf3c0 Merge branch 'main' into snoglobe/accessors 2024-10-21 18:42:52 -07:00
snoglobe
549f14fa70 fix comments 2024-10-11 13:40:41 -07:00
snoglobe
512615684f catch unreachable is bad 2024-10-10 15:13:22 -07:00
snoglobe
b01bd3566d bun build [file] debug config 2024-10-10 15:10:12 -07:00
snoglobe
e13a497cf4 fixed actually using renamer 2024-10-10 15:10:01 -07:00
snoglobe
927e21dc94 computed properties cannot be accessor fields 2024-10-09 19:53:56 -07:00
snoglobe
e00be30ef6 fix symbol collision 2024-10-09 19:47:16 -07:00
snoglobe
6aefea45b2 initial suport 2024-10-09 19:11:44 -07:00
6 changed files with 192 additions and 19 deletions

15
.vscode/launch.json generated vendored
View File

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

View File

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

View File

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

View File

@@ -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 = .{},
};

View File

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

View File

@@ -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 => {},