diff options
author | 2022-12-11 13:55:15 -0800 | |
---|---|---|
committer | 2022-12-11 13:55:15 -0800 | |
commit | 768f4539e83b8e91da9163acd611711f04de5e6a (patch) | |
tree | 565ff535003a751adc847629d469b84fecc1de74 /src/js_parser.zig | |
parent | 37b16a900740a070cb29a77109cb8cd23e82f24d (diff) | |
download | bun-768f4539e83b8e91da9163acd611711f04de5e6a.tar.gz bun-768f4539e83b8e91da9163acd611711f04de5e6a.tar.zst bun-768f4539e83b8e91da9163acd611711f04de5e6a.zip |
[transpiler] Fix unbalanced class_name scope
surprised this was never caught sooner
Diffstat (limited to 'src/js_parser.zig')
-rw-r--r-- | src/js_parser.zig | 263 |
1 files changed, 137 insertions, 126 deletions
diff --git a/src/js_parser.zig b/src/js_parser.zig index d48afe7a4..95ec5ff1a 100644 --- a/src/js_parser.zig +++ b/src/js_parser.zig @@ -17453,157 +17453,159 @@ fn NewParser_( class.extends = p.visitExpr(extends); } - p.pushScopeForVisitPass(.class_body, class.body_loc) catch unreachable; - defer { - p.popScope(); - p.enclosing_class_keyword = old_enclosing_class_keyword; - } - - var i: usize = 0; - var constructor_function: ?*E.Function = null; - while (i < class.properties.len) : (i += 1) { - var property = &class.properties[i]; - - if (property.kind == .class_static_block) { - var old_fn_or_arrow_data = p.fn_or_arrow_data_visit; - var old_fn_only_data = p.fn_only_data_visit; - p.fn_or_arrow_data_visit = .{}; - p.fn_only_data_visit = .{ .is_this_nested = true, .is_new_target_allowed = true }; - - p.pushScopeForVisitPass(.class_static_init, property.class_static_block.?.loc) catch unreachable; - - // Make it an error to use "arguments" in a static class block - p.current_scope.forbid_arguments = true; - - var list = property.class_static_block.?.stmts.listManaged(p.allocator); - p.visitStmts(&list, .fn_body) catch unreachable; - property.class_static_block.?.stmts = js_ast.BabyList(Stmt).fromList(list); + { + p.pushScopeForVisitPass(.class_body, class.body_loc) catch unreachable; + defer { p.popScope(); + p.enclosing_class_keyword = old_enclosing_class_keyword; + } - p.fn_or_arrow_data_visit = old_fn_or_arrow_data; - p.fn_only_data_visit = old_fn_only_data; + var i: usize = 0; + var constructor_function: ?*E.Function = null; + while (i < class.properties.len) : (i += 1) { + var property = &class.properties[i]; - continue; - } - property.ts_decorators = p.visitTSDecorators(property.ts_decorators); - const is_private = if (property.key != null) @as(Expr.Tag, property.key.?.data) == .e_private_identifier else false; + if (property.kind == .class_static_block) { + var old_fn_or_arrow_data = p.fn_or_arrow_data_visit; + var old_fn_only_data = p.fn_only_data_visit; + p.fn_or_arrow_data_visit = .{}; + p.fn_only_data_visit = .{ .is_this_nested = true, .is_new_target_allowed = true }; - // Special-case EPrivateIdentifier to allow it here + p.pushScopeForVisitPass(.class_static_init, property.class_static_block.?.loc) catch unreachable; - if (is_private) { - p.recordDeclaredSymbol(property.key.?.data.e_private_identifier.ref) catch unreachable; - } else if (property.key) |key| { - class.properties[i].key = p.visitExpr(key); - } + // Make it an error to use "arguments" in a static class block + p.current_scope.forbid_arguments = true; - // Make it an error to use "arguments" in a class body - p.current_scope.forbid_arguments = true; - defer p.current_scope.forbid_arguments = false; + var list = property.class_static_block.?.stmts.listManaged(p.allocator); + p.visitStmts(&list, .fn_body) catch unreachable; + property.class_static_block.?.stmts = js_ast.BabyList(Stmt).fromList(list); + p.popScope(); - // The value of "this" is shadowed inside property values - const old_is_this_captured = p.fn_only_data_visit.is_this_nested; - const old_this = p.fn_only_data_visit.this_class_static_ref; - p.fn_only_data_visit.is_this_nested = true; - p.fn_only_data_visit.is_new_target_allowed = true; - p.fn_only_data_visit.this_class_static_ref = null; - defer p.fn_only_data_visit.is_this_nested = old_is_this_captured; - defer p.fn_only_data_visit.this_class_static_ref = old_this; + p.fn_or_arrow_data_visit = old_fn_or_arrow_data; + p.fn_only_data_visit = old_fn_only_data; - // We need to explicitly assign the name to the property initializer if it - // will be transformed such that it is no longer an inline initializer. + continue; + } + property.ts_decorators = p.visitTSDecorators(property.ts_decorators); + const is_private = if (property.key != null) @as(Expr.Tag, property.key.?.data) == .e_private_identifier else false; - var constructor_function_: ?*E.Function = null; + // Special-case EPrivateIdentifier to allow it here - var name_to_keep: ?string = null; - if (is_private) {} else if (!property.flags.contains(.is_method) and !property.flags.contains(.is_computed)) { - if (property.key) |key| { - if (@as(Expr.Tag, key.data) == .e_string) { - name_to_keep = key.data.e_string.string(p.allocator) catch unreachable; - } + if (is_private) { + p.recordDeclaredSymbol(property.key.?.data.e_private_identifier.ref) catch unreachable; + } else if (property.key) |key| { + class.properties[i].key = p.visitExpr(key); } - } else if (property.flags.contains(.is_method)) { - if (comptime is_typescript_enabled) { - if (property.value.?.data == .e_function and property.key.?.data == .e_string and - property.key.?.data.e_string.eqlComptime("constructor")) - { - constructor_function_ = property.value.?.data.e_function; - constructor_function = constructor_function_; + + // Make it an error to use "arguments" in a class body + p.current_scope.forbid_arguments = true; + defer p.current_scope.forbid_arguments = false; + + // The value of "this" is shadowed inside property values + const old_is_this_captured = p.fn_only_data_visit.is_this_nested; + const old_this = p.fn_only_data_visit.this_class_static_ref; + p.fn_only_data_visit.is_this_nested = true; + p.fn_only_data_visit.is_new_target_allowed = true; + p.fn_only_data_visit.this_class_static_ref = null; + defer p.fn_only_data_visit.is_this_nested = old_is_this_captured; + defer p.fn_only_data_visit.this_class_static_ref = old_this; + + // We need to explicitly assign the name to the property initializer if it + // will be transformed such that it is no longer an inline initializer. + + var constructor_function_: ?*E.Function = null; + + var name_to_keep: ?string = null; + if (is_private) {} else if (!property.flags.contains(.is_method) and !property.flags.contains(.is_computed)) { + if (property.key) |key| { + if (@as(Expr.Tag, key.data) == .e_string) { + name_to_keep = key.data.e_string.string(p.allocator) catch unreachable; + } + } + } else if (property.flags.contains(.is_method)) { + if (comptime is_typescript_enabled) { + if (property.value.?.data == .e_function and property.key.?.data == .e_string and + property.key.?.data.e_string.eqlComptime("constructor")) + { + constructor_function_ = property.value.?.data.e_function; + constructor_function = constructor_function_; + } } } - } - if (property.value) |val| { - if (name_to_keep) |name| { - const was_anon = p.isAnonymousNamedExpr(val); - property.value = p.maybeKeepExprSymbolName(p.visitExpr(val), name, was_anon); - } else { - property.value = p.visitExpr(val); - } + if (property.value) |val| { + if (name_to_keep) |name| { + const was_anon = p.isAnonymousNamedExpr(val); + property.value = p.maybeKeepExprSymbolName(p.visitExpr(val), name, was_anon); + } else { + property.value = p.visitExpr(val); + } - if (comptime is_typescript_enabled) { - if (constructor_function_ != null and property.value != null and property.value.?.data == .e_function) { - constructor_function = property.value.?.data.e_function; + if (comptime is_typescript_enabled) { + if (constructor_function_ != null and property.value != null and property.value.?.data == .e_function) { + constructor_function = property.value.?.data.e_function; + } } } - } - if (property.initializer) |val| { - // if (property.flags.is_static and ) - if (name_to_keep) |name| { - const was_anon = p.isAnonymousNamedExpr(val); - property.initializer = p.maybeKeepExprSymbolName(p.visitExpr(val), name, was_anon); - } else { - property.initializer = p.visitExpr(val); + if (property.initializer) |val| { + // if (property.flags.is_static and ) + if (name_to_keep) |name| { + const was_anon = p.isAnonymousNamedExpr(val); + property.initializer = p.maybeKeepExprSymbolName(p.visitExpr(val), name, was_anon); + } else { + property.initializer = p.visitExpr(val); + } } } - } - - // note: our version assumes useDefineForClassFields is true - if (comptime is_typescript_enabled) { - if (constructor_function) |constructor| { - var to_add: usize = 0; - for (constructor.func.args) |arg| { - to_add += @boolToInt(arg.is_typescript_ctor_field and arg.binding.data == .b_identifier); - } - - if (to_add > 0) { - // to match typescript behavior, we also must prepend to the class body - var stmts = std.ArrayList(Stmt).fromOwnedSlice(p.allocator, constructor.func.body.stmts); - stmts.ensureUnusedCapacity(to_add) catch unreachable; - var class_body = std.ArrayList(G.Property).fromOwnedSlice(p.allocator, class.properties); - class_body.ensureUnusedCapacity(to_add) catch unreachable; - var j: usize = 0; + // note: our version assumes useDefineForClassFields is true + if (comptime is_typescript_enabled) { + if (constructor_function) |constructor| { + var to_add: usize = 0; for (constructor.func.args) |arg| { - if (arg.is_typescript_ctor_field) { - switch (arg.binding.data) { - .b_identifier => |id| { - const name = p.symbols.items[id.ref.innerIndex()].original_name; - const ident = p.e(E.Identifier{ .ref = id.ref }, arg.binding.loc); - stmts.appendAssumeCapacity( - Expr.assignStmt( - p.e(E.Dot{ - .target = p.e(E.This{}, arg.binding.loc), - .name = name, - .name_loc = arg.binding.loc, - }, arg.binding.loc), - ident, - p.allocator, - ), - ); - // O(N) - class_body.items.len += 1; - std.mem.copyBackwards(G.Property, class_body.items[j + 1 .. class_body.items.len], class_body.items[j .. class_body.items.len - 1]); - class_body.items[j] = G.Property{ .key = ident }; - j += 1; - }, - else => {}, + to_add += @boolToInt(arg.is_typescript_ctor_field and arg.binding.data == .b_identifier); + } + + if (to_add > 0) { + // to match typescript behavior, we also must prepend to the class body + var stmts = std.ArrayList(Stmt).fromOwnedSlice(p.allocator, constructor.func.body.stmts); + stmts.ensureUnusedCapacity(to_add) catch unreachable; + var class_body = std.ArrayList(G.Property).fromOwnedSlice(p.allocator, class.properties); + class_body.ensureUnusedCapacity(to_add) catch unreachable; + var j: usize = 0; + + for (constructor.func.args) |arg| { + if (arg.is_typescript_ctor_field) { + switch (arg.binding.data) { + .b_identifier => |id| { + const name = p.symbols.items[id.ref.innerIndex()].original_name; + const ident = p.e(E.Identifier{ .ref = id.ref }, arg.binding.loc); + stmts.appendAssumeCapacity( + Expr.assignStmt( + p.e(E.Dot{ + .target = p.e(E.This{}, arg.binding.loc), + .name = name, + .name_loc = arg.binding.loc, + }, arg.binding.loc), + ident, + p.allocator, + ), + ); + // O(N) + class_body.items.len += 1; + std.mem.copyBackwards(G.Property, class_body.items[j + 1 .. class_body.items.len], class_body.items[j .. class_body.items.len - 1]); + class_body.items[j] = G.Property{ .key = ident }; + j += 1; + }, + else => {}, + } } } - } - class.properties = class_body.toOwnedSlice(); - constructor.func.body.stmts = stmts.toOwnedSlice(); + class.properties = class_body.toOwnedSlice(); + constructor.func.body.stmts = stmts.toOwnedSlice(); + } } } } @@ -17622,6 +17624,9 @@ fn NewParser_( } } + // class name scope + p.popScope(); + return shadow_ref; } @@ -17676,6 +17681,8 @@ fn NewParser_( @compileError("only_scan_imports_and_do_not_visit must not run this."); } + var initial_scope: *Scope = if (comptime Environment.allow_assert) p.current_scope else undefined; + { // Save the current control-flow liveness. This represents if we are @@ -17761,6 +17768,10 @@ fn NewParser_( } } + if (comptime Environment.allow_assert) + // if this fails it means that scope pushing/popping is not balanced + assert(p.current_scope == initial_scope); + if (!p.options.features.inlining) { return; } |