diff options
Diffstat (limited to 'src/js_parser/js_parser.zig')
-rw-r--r-- | src/js_parser/js_parser.zig | 295 |
1 files changed, 161 insertions, 134 deletions
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index cb48f08e3..39860b42e 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -3,7 +3,7 @@ usingnamespace @import("imports.zig"); const TemplatePartTuple = std.meta.Tuple(&[_]type{ []E.TemplatePart, logger.Loc }); const ScopeOrderList = std.ArrayListUnmanaged(?ScopeOrder); -var panic_buffer = std.mem.zeros(32 * 1024); +var panic_buffer = std.mem.zeroes([32 * 1024]u8); var panic_stream = std.io.fixedBufferStream(&panic_buffer); pub fn ExpressionTransposer(comptime ctx: type, visitor: fn (ptr: *ctx, arg: Expr, state: anytype) Expr) type { @@ -1771,6 +1771,8 @@ pub const P = struct { // This is a general place to put lots of Expr objects expr_list: List(Expr), + scope_order_to_visit: []ScopeOrder = &([_]ScopeOrder{}), + const TransposeState = struct { is_await_target: bool = false, is_then_catch_target: bool = false, @@ -1827,7 +1829,7 @@ pub const P = struct { p.import_records_for_current_part.append(import_record_index) catch unreachable; p.ignoreUsage(p.require_ref); - return p.e(E.Require{ .import_record_index = import_record_index }, arg.loc); + return p.e(E.Require{ .import_record_index = Ref.toInt(import_record_index) }, arg.loc); }, else => {}, } @@ -1845,13 +1847,16 @@ pub const P = struct { }; pub fn s(p: *P, t: anytype, loc: logger.Loc) Stmt { + // Output.print("\nStmt: {s} - {d}\n", .{ @typeName(@TypeOf(t)), loc.start }); if (@typeInfo(@TypeOf(t)) == .Pointer) { return Stmt.init(t, loc); } else { return Stmt.alloc(p.allocator, t, loc); } } + pub fn e(p: *P, t: anytype, loc: logger.Loc) Expr { + // Output.print("\nExpr: {s} - {d}\n", .{ @typeName(@TypeOf(t)), loc.start }); if (@typeInfo(@TypeOf(t)) == .Pointer) { return Expr.init(t, loc); } else { @@ -2222,6 +2227,23 @@ pub const P = struct { } pub fn prepareForVisitPass(p: *P) !void { + { + var count: usize = 0; + for (p.scopes_in_order.items) |item| { + if (item != null) { + count += 1; + } + } + var i: usize = 0; + p.scope_order_to_visit = try p.allocator.alloc(ScopeOrder, p.scopes_in_order.items.len); + for (p.scopes_in_order.items) |item| { + if (item) |_item| { + p.scope_order_to_visit[i] = _item; + i += 1; + } + } + } + try p.pushScopeForVisitPass(js_ast.Scope.Kind.entry, locModuleScope); p.fn_or_arrow_data_visit.is_outside_fn_or_arrow = true; p.module_scope = p.current_scope; @@ -2266,20 +2288,18 @@ pub const P = struct { } pub fn nextScopeInOrderForVisitPass(p: *P) ScopeOrder { - while (p.scopes_in_order.items[p.scopes_in_order_visitor_index] == null) { - p.scopes_in_order_visitor_index += 1; - } - const scope_order = p.scopes_in_order.items[p.scopes_in_order_visitor_index].?; - p.scopes_in_order_visitor_index += 1; - return scope_order; + const head = p.scope_order_to_visit[0]; + p.scope_order_to_visit = p.scope_order_to_visit[1..p.scope_order_to_visit.len]; + return head; } pub fn pushScopeForVisitPass(p: *P, kind: js_ast.Scope.Kind, loc: logger.Loc) !void { - for (p.scopes_in_order.items[p.scopes_in_order_visitor_index..p.scopes_in_order.items.len]) |scope_order, i| { - if (scope_order) |ord| { - Output.print("Scope ({d}, {d})\n", .{ @enumToInt(ord.scope.kind), ord.loc.start }); - } - } + // Output.print("\n+Loc: {d}\n", .{loc.start}); + // for (p.scopes_in_order.items[p.scopes_in_order_visitor_index..p.scopes_in_order.items.len]) |scope_order, i| { + // if (scope_order) |ord| { + // Output.print("Scope ({d}, {d})\n", .{ @enumToInt(ord.scope.kind), ord.loc.start }); + // } + // } const order = p.nextScopeInOrderForVisitPass(); // Sanity-check that the scopes generated by the first and second passes match @@ -2296,7 +2316,6 @@ pub const P = struct { debugl("<pushScopeForParsePass>"); defer debugl("</pushScopeForParsePass>"); var parent: *Scope = p.current_scope; - p.scope_id += 1; var scope = try p.allocator.create(Scope); scope.* = Scope{ @@ -2308,7 +2327,6 @@ pub const P = struct { .kind = kind, .label_ref = null, .parent = parent, - .id = p.scope_id, }; try parent.children.append(scope); @@ -2351,7 +2369,7 @@ pub const P = struct { // Remember the length in case we call popAndDiscardScope() later const scope_index = p.scopes_in_order.items.len; try p.scopes_in_order.append(p.allocator, ScopeOrder{ .loc = loc, .scope = scope }); - + // Output.print("\nLoc: {d}\n", .{loc.start}); return scope_index; } @@ -3525,6 +3543,7 @@ pub const P = struct { } stmtOpts = ParseStatementOptions{}; try p.declareBinding(kind, value, &stmtOpts); + binding = value; } try p.lexer.expect(.t_open_brace); @@ -5008,23 +5027,27 @@ pub const P = struct { var name: ?js_ast.LocRef = null; _ = p.pushScopeForParsePass(.function_args, loc) catch unreachable; - defer p.popScope(); + // The name is optional if (p.lexer.token == .t_identifier) { - name = js_ast.LocRef{ - .loc = loc, + // Don't declare the name "arguments" since it's shadowed and inaccessible + var _name = js_ast.LocRef{ + .loc = p.lexer.loc(), .ref = null, }; - if (p.lexer.identifier.len > 0 and !strings.eql(p.lexer.identifier, "arguments")) { - (name orelse unreachable).ref = try p.declareSymbol(.hoisted_function, (name orelse unreachable).loc, p.lexer.identifier); + const text = p.lexer.identifier; + if (text.len > 0 and !strings.eql(text, "arguments")) { + _name.ref = try p.declareSymbol(.hoisted_function, _name.loc, text); } else { - (name orelse unreachable).ref = try p.newSymbol(.hoisted_function, p.lexer.identifier); + _name.ref = try p.newSymbol(.hoisted_function, text); } debug("FUNC NAME {s}", .{p.lexer.identifier}); + name = _name; try p.lexer.next(); } + // Even anonymous functions can have TypeScript type parameters if (p.options.ts) { p.skipTypescriptTypeParameters(); } @@ -5036,6 +5059,7 @@ pub const P = struct { }); p.validateFunctionName(func, .expr); + p.popScope(); return p.e(js_ast.E.Function{ .func = func, @@ -6281,6 +6305,7 @@ pub const P = struct { const yes = try p.parseExpr(.comma); p.allow_in = old_allow_in; + try p.lexer.expect(.t_colon); const no = try p.parseExpr(.comma); @@ -7069,6 +7094,7 @@ pub const P = struct { const class = try p.parseClass(classKeyword, name, ParseClassOptions{}); p.popScope(); + return p.e(class, loc); }, .t_new => { @@ -7906,9 +7932,7 @@ pub const P = struct { pub fn visitFunc(p: *P, func: *G.Fn, open_parens_loc: logger.Loc) void { const old_fn_or_arrow_data = p.fn_or_arrow_data_visit; - defer p.fn_or_arrow_data_visit = old_fn_or_arrow_data; const old_fn_only_data = p.fn_only_data_visit; - defer p.fn_only_data_visit = old_fn_only_data; p.fn_or_arrow_data_visit = FnOrArrowDataVisit{ .is_async = func.flags.is_async }; p.fn_only_data_visit = FnOnlyDataVisit{ .is_this_nested = true, .arguments_ref = func.arguments_ref }; @@ -7923,7 +7947,6 @@ pub const P = struct { } p.pushScopeForVisitPass(.function_args, open_parens_loc) catch unreachable; - defer p.popScope(); p.visitArgs( func.args, VisitArgsOpts{ @@ -7933,16 +7956,18 @@ pub const P = struct { }, ); - var body = func.body orelse p.panic("Expected visitFunc to have body {s}", .{func}); - p.pushScopeForVisitPass(.function_body, body.loc) catch unreachable; - defer p.popScope(); - var stmts = List(Stmt).fromOwnedSlice(p.allocator, body.stmts); - var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body, .fn_body_loc = body.loc }; + assert(func.body != null); + p.pushScopeForVisitPass(.function_body, func.body.?.loc) catch unreachable; + var stmts = List(Stmt).fromOwnedSlice(p.allocator, func.body.?.stmts); + var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body, .fn_body_loc = func.body.?.loc }; p.visitStmtsAndPrependTempRefs(&stmts, &temp_opts) catch unreachable; + func.body.?.stmts = stmts.toOwnedSlice(); - body.stmts = stmts.toOwnedSlice(); + p.popScope(); + p.popScope(); - func.body = body; + p.fn_or_arrow_data_visit = old_fn_or_arrow_data; + p.fn_only_data_visit = old_fn_only_data; } pub fn maybeKeepExprSymbolName(p: *P, expr: Expr, original_name: string, was_anonymous_named_expr: bool) Expr { @@ -7976,6 +8001,7 @@ pub const P = struct { } pub fn visitExprInOut(p: *P, expr: Expr, in: ExprIn) Expr { + // Output.print("\nVisit: {s} - {d}\n", .{ @tagName(expr.data), expr.loc.start }); switch (expr.data) { .e_null, .e_super, .e_boolean, .e_big_int, .e_reg_exp, .e_new_target, .e_undefined => {}, .e_string => |e_| { @@ -8713,7 +8739,10 @@ pub const P = struct { const side_effects = SideEffects.toBoolean(e_.test_.data); - if (side_effects.ok) { + if (!side_effects.ok) { + e_.yes = p.visitExpr(e_.yes); + e_.no = p.visitExpr(e_.no); + } else { // Mark the control flow as dead if the branch is never taken if (side_effects.value) { // "true ? live : dead" @@ -8730,9 +8759,6 @@ pub const P = struct { p.is_control_flow_dead = old; e_.no = p.visitExpr(e_.no); } - } else { - e_.yes = p.visitExpr(e_.yes); - e_.no = p.visitExpr(e_.no); } }, .e_await => |e_| { @@ -8788,68 +8814,68 @@ pub const P = struct { .e_object => |e_| { if (in.assign_target != .none) { p.maybeCommaSpreadError(e_.comma_after_spread); - var has_spread = false; - var has_proto = false; + } - var i: usize = 0; - while (i < e_.properties.len) : (i += 1) { - var property = e_.properties[i]; - - if (property.kind != .spread) { - const key = p.visitExpr(property.key orelse Global.panic("Expected property key", .{})); - e_.properties[i].key = key; - - // Forbid duplicate "__proto__" properties according to the specification - if (!property.flags.is_computed and !property.flags.was_shorthand and !property.flags.is_method and in.assign_target == .none and key.data.isStringValue() and strings.eqlComptime( - // __proto__ is utf8, assume it lives in refs - key.data.e_string.utf8, - "__proto__", - )) { - if (has_proto) { - const r = js_lexer.rangeOfIdentifier(p.source, key.loc); - p.log.addRangeError(p.source, r, "Cannot specify the \"__proto__\" property more than once per object") catch unreachable; - } - has_proto = true; - } - } else { - has_spread = true; - } + var has_spread = false; + var has_proto = false; + var i: usize = 0; + while (i < e_.properties.len) : (i += 1) { + var property = e_.properties[i]; - // Extract the initializer for expressions like "({ a: b = c } = d)" - if (in.assign_target != .none and property.initializer != null and property.value != null) { - switch (property.value.?.data) { - .e_binary => |bin| { - if (bin.op == .bin_assign) { - property.initializer = bin.right; - property.value = bin.left; - } - }, - else => {}, + if (property.kind != .spread) { + const key = p.visitExpr(property.key orelse Global.panic("Expected property key", .{})); + e_.properties[i].key = key; + + // Forbid duplicate "__proto__" properties according to the specification + if (!property.flags.is_computed and !property.flags.was_shorthand and !property.flags.is_method and in.assign_target == .none and key.data.isStringValue() and strings.eqlComptime( + // __proto__ is utf8, assume it lives in refs + key.data.e_string.utf8, + "__proto__", + )) { + if (has_proto) { + const r = js_lexer.rangeOfIdentifier(p.source, key.loc); + p.log.addRangeError(p.source, r, "Cannot specify the \"__proto__\" property more than once per object") catch unreachable; } + has_proto = true; } + } else { + has_spread = true; + } - if (property.value != null) { - property.value = p.visitExprInOut(property.value.?, ExprIn{ .assign_target = in.assign_target }); + // Extract the initializer for expressions like "({ a: b = c } = d)" + if (in.assign_target != .none and property.initializer != null and property.value != null) { + switch (property.value.?.data) { + .e_binary => |bin| { + if (bin.op == .bin_assign) { + property.initializer = bin.right; + property.value = bin.left; + } + }, + else => {}, } + } - if (property.initializer != null) { - const was_anonymous_named_expr = p.isAnonymousNamedExpr(property.initializer orelse unreachable); - property.initializer = p.visitExprInOut(property.initializer.?, ExprIn{ .assign_target = in.assign_target }); + if (property.value != null) { + property.value = p.visitExprInOut(property.value.?, ExprIn{ .assign_target = in.assign_target }); + } - if (property.value) |val| { - if (@as(Expr.Tag, val.data) == .e_identifier) { - property.initializer = p.maybeKeepExprSymbolName( - property.initializer orelse unreachable, - p.symbols.items[val.data.e_identifier.ref.inner_index].original_name, - was_anonymous_named_expr, - ); - } + if (property.initializer != null) { + const was_anonymous_named_expr = p.isAnonymousNamedExpr(property.initializer orelse unreachable); + property.initializer = p.visitExprInOut(property.initializer.?, ExprIn{ .assign_target = in.assign_target }); + + if (property.value) |val| { + if (@as(Expr.Tag, val.data) == .e_identifier) { + property.initializer = p.maybeKeepExprSymbolName( + property.initializer orelse unreachable, + p.symbols.items[val.data.e_identifier.ref.inner_index].original_name, + was_anonymous_named_expr, + ); } } - - // TODO: can we avoid htis copy - e_.properties[i] = property; } + + // TODO: can we avoid htis copy + e_.properties[i] = property; } }, .e_import => |e_| { @@ -8931,16 +8957,19 @@ pub const P = struct { p.fn_only_data_visit.is_inside_async_arrow_fn = e_.is_async or p.fn_only_data_visit.is_inside_async_arrow_fn; p.pushScopeForVisitPass(.function_args, expr.loc) catch unreachable; + var dupe = p.allocator.dupe(Stmt, e_.body.stmts) catch unreachable; + p.visitArgs(e_.args, VisitArgsOpts{ .has_rest_arg = e_.has_rest_arg, - .body = e_.body.stmts, + .body = dupe, .is_unique_formal_parameters = true, }); p.pushScopeForVisitPass(.function_body, e_.body.loc) catch unreachable; - var stmts_list = List(Stmt).fromOwnedSlice(p.allocator, e_.body.stmts); + var stmts_list = List(Stmt).fromOwnedSlice(p.allocator, dupe); var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body }; p.visitStmtsAndPrependTempRefs(&stmts_list, &temp_opts) catch unreachable; + p.allocator.free(e_.body.stmts); e_.body.stmts = stmts_list.toOwnedSlice(); p.popScope(); p.popScope(); @@ -9015,17 +9044,18 @@ pub const P = struct { } pub fn keepExprSymbolName(p: *P, _value: Expr, name: string) Expr { - var start = p.expr_list.items.len; - p.expr_list.ensureUnusedCapacity(2) catch unreachable; - p.expr_list.appendAssumeCapacity(_value); - p.expr_list.appendAssumeCapacity(p.e(E.String{ - .utf8 = name, - }, _value.loc)); + return _value; + // var start = p.expr_list.items.len; + // p.expr_list.ensureUnusedCapacity(2) catch unreachable; + // p.expr_list.appendAssumeCapacity(_value); + // p.expr_list.appendAssumeCapacity(p.e(E.String{ + // .utf8 = name, + // }, _value.loc)); - var value = p.callRuntime(_value.loc, "ℹ", p.expr_list.items[start..p.expr_list.items.len]); - // Make sure tree shaking removes this if the function is never used - value.data.e_call.can_be_unwrapped_if_unused = true; - return value; + // var value = p.callRuntime(_value.loc, "ℹ", p.expr_list.items[start..p.expr_list.items.len]); + // // Make sure tree shaking removes this if the function is never used + // value.data.e_call.can_be_unwrapped_if_unused = true; + // return value; } pub fn fnBodyContainsUseStrict(body: []Stmt) ?logger.Loc { @@ -9719,9 +9749,9 @@ pub const P = struct { .s_for => |data| { { p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; - defer p.popScope(); + if (data.init) |initst| { - _ = p.visitForLoopInit(initst, false); + data.init = p.visitForLoopInit(initst, false); } if (data.test_) |test_| { @@ -9735,6 +9765,7 @@ pub const P = struct { } data.body = p.visitLoopBody(data.body); + p.popScope(); } // TODO: Potentially relocate "var" declarations to the top level @@ -9782,32 +9813,36 @@ pub const P = struct { // p.lowerObjectRestInForLoopInit(s.Init, &s.Body) }, .s_try => |data| { + p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; { - p.pushScopeForVisitPass(.block, stmt.loc) catch unreachable; - defer p.popScope(); - p.fn_or_arrow_data_visit.try_body_count += 1; - defer p.fn_or_arrow_data_visit.try_body_count -= 1; var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body); + p.fn_or_arrow_data_visit.try_body_count += 1; p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; + p.fn_or_arrow_data_visit.try_body_count -= 1; data.body = _stmts.toOwnedSlice(); } + p.popScope(); if (data.catch_) |*catch_| { p.pushScopeForVisitPass(.block, catch_.loc) catch unreachable; - defer p.popScope(); - if (catch_.binding != null and @as(Binding.Tag, catch_.binding.?.data) != .b_missing) { - p.visitBinding(catch_.binding.?, null); + { + if (catch_.binding != null) { + p.visitBinding(catch_.binding.?, null); + } + var _stmts = List(Stmt).fromOwnedSlice(p.allocator, catch_.body); + p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; + catch_.body = _stmts.toOwnedSlice(); } - var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body); - p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; - catch_.body = _stmts.toOwnedSlice(); + p.popScope(); } if (data.finally) |*finally| { p.pushScopeForVisitPass(.block, finally.loc) catch unreachable; - var _stmts = List(Stmt).fromOwnedSlice(p.allocator, data.body); - p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; - finally.stmts = _stmts.toOwnedSlice(); + { + var _stmts = List(Stmt).fromOwnedSlice(p.allocator, finally.stmts); + p.visitStmts(&_stmts, StmtsKind.none) catch unreachable; + finally.stmts = _stmts.toOwnedSlice(); + } p.popScope(); } }, @@ -10289,9 +10324,10 @@ pub const P = struct { pub fn visitLoopBody(p: *P, stmt: StmtNodeIndex) StmtNodeIndex { const old_is_inside_loop = p.fn_or_arrow_data_visit.is_inside_loop; p.fn_or_arrow_data_visit.is_inside_loop = true; - defer p.fn_or_arrow_data_visit.is_inside_loop = old_is_inside_loop; p.loop_body = stmt.data; - return p.visitSingleStmt(stmt, .loop_body); + const res = p.visitSingleStmt(stmt, .loop_body); + p.fn_or_arrow_data_visit.is_inside_loop = old_is_inside_loop; + return res; } pub fn visitSingleStmt(p: *P, stmt: Stmt, kind: StmtsKind) Stmt { @@ -10328,19 +10364,9 @@ pub const P = struct { return Stmt{ .data = Prefill.Data.SEmpty, .loc = loc }; } - if (stmts.len == 1) { - switch (stmts[0].data) { - .s_local => |local| { - // "let" and "const" must be put in a block when in a single-statement context - - if (local.kind == .k_var) { - return stmts[0]; - } - }, - else => { - return stmts[0]; - }, - } + if (stmts.len == 1 and std.meta.activeTag(stmts[0].data) != .s_local or (std.meta.activeTag(stmts[0].data) == .s_local and stmts[0].data.s_local.kind == S.Local.Kind.k_var)) { + // "let" and "const" must be put in a block when in a single-statement context + return stmts[0]; } return p.s(S.Block{ .stmts = stmts }, loc); @@ -10622,7 +10648,7 @@ pub const P = struct { // values that introduce new scopes and declare new symbols. If this is an // arrow function, then those new scopes will need to be parented under the // scope of the arrow function itself. - const scopeIndex = try p.pushScopeForParsePass(.function_args, loc); + const scope_index = try p.pushScopeForParsePass(.function_args, loc); // Allow "in" inside parentheses var oldAllowIn = p.allow_in; @@ -10754,12 +10780,12 @@ pub const P = struct { // If we get here, it's not an arrow function so undo the pushing of the // scope we did earlier. This needs to flatten any child scopes into the // parent scope as if the scope was never pushed in the first place. - p.popAndFlattenScope(scopeIndex); + p.popAndFlattenScope(scope_index); // If this isn't an arrow function, then types aren't allowed if (type_colon_range.len > 0) { try p.log.addRangeError(p.source, type_colon_range, "Unexpected \":\""); - p.panic("", .{}); + return error.ParseError; } // Are these arguments for a call to a function named "async"? @@ -10774,7 +10800,7 @@ pub const P = struct { p.logExprErrors(&errors); if (spread_range.len > 0) { try p.log.addRangeError(p.source, type_colon_range, "Unexpected \"...\""); - p.panic("", .{}); + return error.ParseError; } var value = Expr.joinAllWithComma(items, p.allocator); @@ -10784,7 +10810,7 @@ pub const P = struct { // Indicate that we expected an arrow function try p.lexer.expected(.t_equals_greater_than); - p.panic("", .{}); + return error.ParseError; } // This code is tricky. @@ -10793,6 +10819,7 @@ pub const P = struct { // The key is in how we remove scopes from the list // If we do an orderedRemove, it gets very slow. // swapRemove is fast. But a little more dangerous. + // Instead, we just tombstone it. pub fn popAndFlattenScope(p: *P, scope_index: usize) void { // Move up to the parent scope var to_flatten = p.current_scope; |