aboutsummaryrefslogtreecommitdiff
path: root/src/js_parser/js_parser.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/js_parser/js_parser.zig')
-rw-r--r--src/js_parser/js_parser.zig504
1 files changed, 335 insertions, 169 deletions
diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig
index 63e135295..be5cc6a36 100644
--- a/src/js_parser/js_parser.zig
+++ b/src/js_parser/js_parser.zig
@@ -18,7 +18,9 @@ pub fn ExpressionTransposer(comptime ctx: type, visitor: fn (ptr: *ctx, arg: Exp
pub fn maybeTransposeIf(self: *@This(), arg: Expr, state: anytype) Expr {
switch (arg.data) {
- .e_if => |ex| {
+ .e_if => {
+ const ex = arg.getIf();
+
ex.yes = self.maybeTransposeIf(ex.yes, state);
ex.no = self.maybeTransposeIf(ex.no, state);
return arg;
@@ -407,7 +409,7 @@ pub const ImportScanner = struct {
if (decl.value) |val| {
while (true) {
if (@as(Expr.Tag, val.data) == .e_dot) {
- value = val.data.e_dot.target;
+ value = val.getDot().target;
} else {
break;
}
@@ -419,7 +421,7 @@ pub const ImportScanner = struct {
if (@as(Expr.Tag, val.data) == .e_identifier) {
// Is this import statement unused?
if (@as(Binding.Tag, decl.binding.data) == .b_identifier and p.symbols.items[decl.binding.data.b_identifier.ref.inner_index].use_count_estimate == 0) {
- p.ignoreUsage(val.data.e_identifier.ref);
+ p.ignoreUsage(val.getIdentifier().ref);
scanner.removed_import_equals = true;
continue;
@@ -507,16 +509,20 @@ pub const SideEffects = enum(u2) {
pub fn toNumber(data: Expr.Data) ?f64 {
switch (data) {
- .e_null => |e| {
+ .e_null => {
return 0;
},
- .e_undefined => |e| {
+ .e_undefined => {
return std.math.nan_f64;
},
- .e_boolean => |e| {
+ .e_boolean => {
+ const e = Expr.Data.Store.Boolean.at(data.e_boolean);
+
return if (e.value) 1.0 else 0.0;
},
- .e_number => |e| {
+ .e_number => {
+ const e = Expr.Data.Store.Number.at(data.e_number);
+
return e.value;
},
else => {},
@@ -542,12 +548,14 @@ pub const SideEffects = enum(u2) {
return null;
},
- .e_dot => |dot| {
- if (dot.can_be_removed_if_unused) {
+ .e_dot => {
+ if (expr.getDot().can_be_removed_if_unused) {
return null;
}
},
- .e_identifier => |ident| {
+ .e_identifier => {
+ const ident = expr.getIdentifier();
+
if (ident.must_keep_due_to_with_stmt) {
return expr;
}
@@ -556,7 +564,8 @@ pub const SideEffects = enum(u2) {
return null;
}
},
- .e_if => |__if__| {
+ .e_if => {
+ const __if__ = expr.getIf();
__if__.yes = simpifyUnusedExpr(p, __if__.yes) orelse __if__.yes.toEmpty();
__if__.no = simpifyUnusedExpr(p, __if__.no) orelse __if__.no.toEmpty();
@@ -566,7 +575,9 @@ pub const SideEffects = enum(u2) {
}
},
- .e_call => |call| {
+ .e_call => {
+ const call = expr.getCall();
+
// A call that has been marked "__PURE__" can be removed if all arguments
// can be removed. The annotation causes us to ignore the target.
if (call.can_be_unwrapped_if_unused) {
@@ -574,7 +585,8 @@ pub const SideEffects = enum(u2) {
}
},
- .e_binary => |bin| {
+ .e_binary => {
+ const bin = expr.getBinary();
switch (bin.op) {
// We can simplify "==" and "!=" even though they can call "toString" and/or
// "valueOf" if we can statically determine that the types of both sides are
@@ -589,7 +601,8 @@ pub const SideEffects = enum(u2) {
}
},
- .e_new => |call| {
+ .e_new => {
+ const call = expr.getNew();
// A constructor call that has been marked "__PURE__" can be removed if all arguments
// can be removed. The annotation causes us to ignore the target.
if (call.can_be_unwrapped_if_unused) {
@@ -702,30 +715,41 @@ pub const SideEffects = enum(u2) {
pub fn eql(left: Expr.Data, right: Expr.Data, p: *P) Equality {
var equality = Equality{};
switch (left) {
- .e_null => |l| {
+ .e_null => {
equality.equal = @as(Expr.Tag, right) == Expr.Tag.e_null;
equality.ok = equality.equal;
},
- .e_undefined => |l| {
+ .e_undefined => {
equality.ok = @as(Expr.Tag, right) == Expr.Tag.e_undefined;
equality.equal = equality.ok;
},
- .e_boolean => |l| {
+ .e_boolean => {
+ const l = Expr.Data.Store.Boolean.at(left.e_boolean);
+ const r = Expr.Data.Store.Boolean.at(right.e_boolean);
+
equality.ok = @as(Expr.Tag, right) == Expr.Tag.e_boolean;
- equality.equal = equality.ok and l.value == right.e_boolean.value;
+ equality.equal = equality.ok and l.value == r.value;
},
- .e_number => |l| {
+ .e_number => {
+ const l = Expr.Data.Store.Number.at(left.e_number);
+ const r = Expr.Data.Store.Number.at(right.e_number);
+
equality.ok = @as(Expr.Tag, right) == Expr.Tag.e_number;
- equality.equal = equality.ok and l.value == right.e_number.value;
+ equality.equal = equality.ok and l.value == r.value;
},
- .e_big_int => |l| {
+ .e_big_int => {
+ const l = Expr.Data.Store.BigInt.at(left.e_big_int);
+ const r = Expr.Data.Store.BigInt.at(right.e_big_int);
+
equality.ok = @as(Expr.Tag, right) == Expr.Tag.e_big_int;
- equality.equal = equality.ok and strings.eql(l.value, right.e_big_int.value);
+ equality.equal = equality.ok and strings.eql(l.value, r.value);
},
- .e_string => |l| {
+ .e_string => {
+ const l = Expr.Data.Store.String.at(left.e_string);
+ const r = Expr.Data.Store.String.at(right.e_string);
+
equality.ok = @as(Expr.Tag, right) == Expr.Tag.e_string;
if (equality.ok) {
- const r = right.e_string;
equality.equal = r.eql(E.String, l);
}
},
@@ -743,7 +767,8 @@ pub const SideEffects = enum(u2) {
.e_null, .e_undefined, .e_boolean, .e_number, .e_big_int, .e_string => {
return true;
},
- .e_unary => |e| {
+ .e_unary => {
+ const e = Expr.Data.Store.Unary.at(data.e_unary);
switch (e.op) {
// number or bigint
.un_pos,
@@ -766,7 +791,8 @@ pub const SideEffects = enum(u2) {
else => {},
}
},
- .e_binary => |e| {
+ .e_binary => {
+ const e = Expr.Data.Store.Binary.at(data.e_binary);
switch (e.op) {
// boolean
.bin_lt,
@@ -819,7 +845,8 @@ pub const SideEffects = enum(u2) {
else => {},
}
},
- .e_if => |e| {
+ .e_if => {
+ const e = Expr.Data.Store.If.at(data.e_if);
return isPrimitiveWithSideEffects(e.yes.data) and isPrimitiveWithSideEffects(e.no.data);
},
else => {},
@@ -875,7 +902,8 @@ pub const SideEffects = enum(u2) {
return Result{ .value = true, .side_effects = .could_have_side_effects, .ok = true };
},
- .e_unary => |e| {
+ .e_unary => {
+ const e = Expr.Data.Store.Unary.at(exp.e_unary);
switch (e.op) {
// Always number or bigint
.un_pos, .un_neg, .un_cpl, .un_pre_dec, .un_pre_inc, .un_post_dec, .un_post_inc => {
@@ -894,7 +922,8 @@ pub const SideEffects = enum(u2) {
}
},
- .e_binary => |e| {
+ .e_binary => {
+ const e = Expr.Data.Store.Binary.at(exp.e_binary);
switch (e.op) {
// always string or number or bigint
.bin_add,
@@ -957,16 +986,20 @@ pub const SideEffects = enum(u2) {
.e_null, .e_undefined => {
return Result{ .ok = true, .value = false, .side_effects = .no_side_effects };
},
- .e_boolean => |e| {
+ .e_boolean => {
+ const e = Expr.Data.Store.Boolean.at(exp.e_boolean);
return Result{ .ok = true, .value = e.value, .side_effects = .no_side_effects };
},
- .e_number => |e| {
+ .e_number => {
+ const e = Expr.Data.Store.Number.at(exp.e_number);
return Result{ .ok = true, .value = e.value != 0.0 and !std.math.isNan(e.value), .side_effects = .no_side_effects };
},
- .e_big_int => |e| {
+ .e_big_int => {
+ const e = Expr.Data.Store.BigInt.at(exp.e_big_int);
return Result{ .ok = true, .value = !strings.eqlComptime(e.value, "0"), .side_effects = .no_side_effects };
},
- .e_string => |e| {
+ .e_string => {
+ const e = Expr.Data.Store.String.at(exp.e_string);
return Result{ .ok = true, .value = std.math.max(e.value.len, e.utf8.len) > 0, .side_effects = .no_side_effects };
},
.e_function, .e_arrow, .e_reg_exp => {
@@ -975,7 +1008,8 @@ pub const SideEffects = enum(u2) {
.e_object, .e_array, .e_class => {
return Result{ .ok = true, .value = true, .side_effects = .could_have_side_effects };
},
- .e_unary => |e_| {
+ .e_unary => {
+ const e_ = Expr.Data.Store.Unary.at(exp.e_unary);
switch (e_.op) {
.un_void => {
return Result{ .ok = true, .value = false, .side_effects = .could_have_side_effects };
@@ -995,7 +1029,8 @@ pub const SideEffects = enum(u2) {
else => {},
}
},
- .e_binary => |e_| {
+ .e_binary => {
+ const e_ = Expr.Data.Store.Binary.at(exp.e_binary);
switch (e_.op) {
.bin_logical_or => {
// "anything || truthy" is truthy
@@ -1683,7 +1718,7 @@ pub const Prefill = struct {
pub var Filename = Expr.Data{ .e_string = &Prefill.String.Filename };
pub var LineNumber = Expr.Data{ .e_string = &Prefill.String.LineNumber };
pub var ColumnNumber = Expr.Data{ .e_string = &Prefill.String.ColumnNumber };
- pub var This = Expr.Data{ .e_this = &Prefill.Value.EThis };
+ pub var This = Expr.Data{ .e_this = E.This{} };
};
pub const Runtime = struct {
pub var JSXFilename = "__jsxFilename";
@@ -1696,12 +1731,12 @@ pub const Prefill = struct {
};
};
-var keyExprData = Expr.Data{ .e_string = &Prefill.String.Key };
-var jsxChildrenKeyData = Expr.Data{ .e_string = &Prefill.String.Children };
+// var keyExprData = Expr.Data{ .e_string = Prefill.String.Key };
+// var jsxChildrenKeyData = Expr.Data{ .e_string = Prefill.String.Children };
var nullExprValueData = E.Null{};
var falseExprValueData = E.Boolean{ .value = false };
-var nullValueExpr = Expr.Data{ .e_null = &nullExprValueData };
-var falseValueExpr = Expr.Data{ .e_boolean = &falseExprValueData };
+var nullValueExpr = Expr.Data{ .e_null = nullExprValueData };
+var falseValueExpr = Expr.Data{ .e_boolean = falseExprValueData };
// P is for Parser!
// public only because of Binding.ToExpr
@@ -1947,13 +1982,13 @@ pub const P = struct {
}
const str = arg.data.e_string;
- const import_record_index = p.addImportRecord(.dynamic, arg.loc, str.string(p.allocator) catch unreachable);
+ const import_record_index = p.addImportRecord(.dynamic, arg.loc, arg.getString().string(p.allocator) catch unreachable);
p.import_records.items[import_record_index].handles_import_errors = (state.is_await_target and p.fn_or_arrow_data_visit.try_body_count != 0) or state.is_then_catch_target;
p.import_records_for_current_part.append(import_record_index) catch unreachable;
return p.e(E.Import{
.expr = arg,
.import_record_index = Ref.toInt(import_record_index),
- // .leading_interior_comments = arg.data.e_string.
+ // .leading_interior_comments = arg.getString().
}, state.loc);
}
@@ -1973,7 +2008,9 @@ pub const P = struct {
pub fn transposeRequire(p: *P, arg: Expr, transpose_state: anytype) Expr {
switch (arg.data) {
- .e_string => |str| {
+ .e_string => {
+ const str = arg.getString();
+
// Ignore calls to require() if the control flow is provably dead here.
// We don't want to spend time scanning the required files if they will
// never be used.
@@ -2592,10 +2629,14 @@ pub const P = struct {
.e_missing => {
return null;
},
- .e_identifier => |ex| {
+ .e_identifier => {
+ const ex = expr.getIdentifier();
+
return p.b(B.Identifier{ .ref = ex.ref }, expr.loc);
},
- .e_array => |ex| {
+ .e_array => {
+ const ex = expr.getArray();
+
if (ex.comma_after_spread) |spread| {
invalid_loc.append(spread) catch unreachable;
}
@@ -2612,7 +2653,7 @@ pub const P = struct {
var _expr = item;
if (@as(Expr.Tag, item.data) == .e_spread) {
is_spread = true;
- item = item.data.e_spread.value;
+ item = item.getSpread().value;
}
const res = p.convertExprToBindingAndInitializer(&item, invalid_loc, is_spread);
items.append(js_ast.ArrayBinding{ .binding = res.binding orelse unreachable, .default_value = res.override_expr }) catch unreachable;
@@ -2624,7 +2665,9 @@ pub const P = struct {
.is_single_line = ex.is_single_line,
}, expr.loc);
},
- .e_object => |ex| {
+ .e_object => {
+ const ex = expr.getObject();
+
if (ex.comma_after_spread) |sp| {
invalid_loc.append(sp) catch unreachable;
}
@@ -2676,7 +2719,9 @@ pub const P = struct {
var override: ?ExprNodeIndex = null;
// zig syntax is sometimes painful
switch (expr.*.data) {
- .e_binary => |bin| {
+ .e_binary => {
+ const bin = expr.getBinary();
+
if (bin.op == .bin_assign) {
initializer = bin.right;
expr = &bin.left;
@@ -4050,7 +4095,9 @@ pub const P = struct {
// Handle the default export of an abstract class in TypeScript
if (p.options.ts and is_identifier and (p.lexer.token == .t_class or opts.ts_decorators != null) and strings.eqlComptime(name, "abstract")) {
switch (expr.data) {
- .e_identifier => |ident| {
+ .e_identifier => {
+ var ident = expr.getIdentifier();
+
var stmtOpts = ParseStatementOptions{
.ts_decorators = opts.ts_decorators,
.is_name_optional = true,
@@ -4880,7 +4927,9 @@ pub const P = struct {
}
if (is_identifier) {
switch (expr.data) {
- .e_identifier => |ident| {
+ .e_identifier => {
+ var ident = expr.getIdentifier();
+
if (p.lexer.token == .t_colon and !opts.hasDecorators()) {
_ = try p.pushScopeForParsePass(.label, loc);
defer p.popScope();
@@ -5244,22 +5293,20 @@ pub const P = struct {
if (strings.eqlComptime(name, "require") and p.lexer.token == .t_open_paren) {
// "import ns = require('x')"
try p.lexer.next();
- const path = p.e(p.lexer.toEString(), p.lexer.loc());
+ var path = p.e(p.lexer.toEString(), p.lexer.loc());
try p.lexer.expect(.t_string_literal);
try p.lexer.expect(.t_close_paren);
const args = p.allocator.alloc(ExprNodeIndex, 1) catch unreachable;
args[0] = path;
- var call_ptr = p.allocator.create(E.Call) catch unreachable;
- call_ptr.* = E.Call{ .target = value, .args = args };
- value.data = .{ .e_call = call_ptr };
+ value.data = .{ .e_call = Expr.Data.Store.Call.append(E.Call{ .target = value, .args = args }) };
} else {
// "import Foo = Bar"
// "import Foo = Bar.Baz"
while (p.lexer.token == .t_dot) {
try p.lexer.next();
- var dot = p.allocator.create(E.Dot) catch unreachable;
- dot.* = E.Dot{ .target = value, .name = p.lexer.identifier, .name_loc = p.lexer.loc() };
- value.data = .{ .e_dot = dot };
+ value.data = .{ .e_dot = Expr.Data.Store.Dot.append(
+ E.Dot{ .target = value, .name = p.lexer.identifier, .name_loc = p.lexer.loc() },
+ ) };
try p.lexer.expect(.t_identifier);
}
}
@@ -5969,9 +6016,11 @@ pub const P = struct {
if (isDirectivePrologue) {
isDirectivePrologue = false;
switch (stmt.data) {
- .s_expr => |expr| {
- switch (stmt.getExpr().value.data) {
- .e_string => |str| {
+ .s_expr => {
+ const expr = stmt.getExpr();
+ switch (expr.value.data) {
+ .e_string => {
+ const str = expr.value.getString();
if (!str.prefer_template) {
// stmt.data = Stmt.Data{
// .s_directive = p.m(S.Directive{
@@ -6584,10 +6633,14 @@ pub const P = struct {
if (had_pure_comment_before and level.lt(.call)) {
expr = try p.parseSuffix(expr, @intToEnum(Level, @enumToInt(Level.call) - 1), errors, flags);
switch (expr.data) {
- .e_call => |ex| {
+ .e_call => {
+ const ex = expr.getCall();
+
ex.can_be_unwrapped_if_unused = true;
},
- .e_new => |ex| {
+ .e_new => {
+ const ex = expr.getNew();
+
ex.can_be_unwrapped_if_unused = true;
},
else => {},
@@ -6673,10 +6726,14 @@ pub const P = struct {
pub fn markExprAsParenthesized(p: *P, expr: *Expr) void {
switch (expr.data) {
- .e_array => |ex| {
+ .e_array => {
+ const ex = expr.getArray();
+
ex.is_parenthesized = true;
},
- .e_object => |ex| {
+ .e_object => {
+ const ex = expr.getObject();
+
ex.is_parenthesized = true;
},
else => {
@@ -6752,7 +6809,9 @@ pub const P = struct {
// Handle index signatures
if (p.options.ts and p.lexer.token == .t_colon and wasIdentifier and opts.is_class) {
switch (expr.data) {
- .e_identifier => |ident| {
+ .e_identifier => {
+ var ident = expr.getIdentifier();
+
try p.lexer.next();
try p.skipTypeScriptType(.lowest);
try p.lexer.expect(.t_close_bracket);
@@ -6901,7 +6960,9 @@ pub const P = struct {
// Forbid the names "constructor" and "prototype" in some cases
if (!is_computed) {
switch (key.data) {
- .e_string => |str| {
+ .e_string => {
+ const str = key.getString();
+
if (str.eql(string, "constructor") or (opts.is_static and str.eql(string, "prototype"))) {
// TODO: fmt error message to include string value.
p.log.addRangeError(p.source, key_range, "Invalid field name") catch unreachable;
@@ -6924,7 +6985,9 @@ pub const P = struct {
// Special-case private identifiers
switch (key.data) {
- .e_private_identifier => |private| {
+ .e_private_identifier => {
+ const private = key.getPrivateIdentifier();
+
const name = p.loadNameFromRef(private.ref);
if (strings.eqlComptime(name, "#constructor")) {
p.log.addRangeError(p.source, key_range, "Invalid field name \"#constructor\"") catch unreachable;
@@ -6968,7 +7031,9 @@ pub const P = struct {
// Forbid the names "constructor" and "prototype" in some cases
if (opts.is_class and !is_computed) {
switch (key.data) {
- .e_string => |str| {
+ .e_string => {
+ const str = key.getString();
+
if (!opts.is_static and str.eql(string, "constructor")) {
if (kind == .get) {
p.log.addRangeError(p.source, key_range, "Class constructor cannot be a getter") catch unreachable;
@@ -7035,7 +7100,9 @@ pub const P = struct {
// Special-case private identifiers
switch (key.data) {
- .e_private_identifier => |private| {
+ .e_private_identifier => {
+ const private = key.getPrivateIdentifier();
+
var declare: Symbol.Kind = undefined;
var suffix: string = "";
switch (kind) {
@@ -7172,7 +7239,9 @@ pub const P = struct {
// Forbid decorators on class constructors
if (opts.ts_decorators.len > 0) {
switch ((property.key orelse p.panic("Internal error: Expected property {s} to have a key.", .{property})).data) {
- .e_string => |str| {
+ .e_string => {
+ const str = property.key.?.getString();
+
if (str.eql(string, "constructor")) {
p.log.addError(p.source, first_decorator_loc, "TypeScript does not allow decorators on class constructors") catch unreachable;
}
@@ -7996,7 +8065,9 @@ pub const P = struct {
// Warn about "!a in b" instead of "!(a in b)"
switch (left.data) {
- .e_unary => |unary| {
+ .e_unary => {
+ const unary = expr.getUnary();
+
if (unary.op == .un_not) {
// TODO:
// p.log.addRangeWarning(source: ?Source, r: Range, text: string)
@@ -8017,7 +8088,9 @@ pub const P = struct {
// example of code with this problem: https://github.com/mrdoob/three.js/pull/11182.
if (!p.options.suppress_warnings_about_weird_code) {
switch (left.data) {
- .e_unary => |unary| {
+ .e_unary => {
+ const unary = expr.getUnary();
+
if (unary.op == .un_not) {
// TODO:
// p.log.addRangeWarning(source: ?Source, r: Range, text: string)
@@ -9212,7 +9285,7 @@ pub const P = struct {
// These never have side effects
.s_function => {},
.s_class => {
- if (!p.classCanBeRemovedIfUnused(&stmt.getClass().class)) {
+ if (!p.classCanBeRemovedIfUnused(&s2.getClass().class)) {
return false;
}
},
@@ -9346,14 +9419,18 @@ pub const P = struct {
// 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_| {
+ .e_string => {
+ const e_ = expr.getString();
+
// If you're using this, you're probably not using 0-prefixed legacy octal notation
// if e.LegacyOctalLoc.Start > 0 {
},
- .e_number => |e_| {
+ .e_number => {
+ const e_ = expr.getNumber();
+
// idc about legacy octal loc
},
- .e_this => |e_| {
+ .e_this => {
if (p.valueForThis(expr.loc)) |exp| {
return exp;
}
@@ -9365,8 +9442,8 @@ pub const P = struct {
// }
},
- .e_import_meta => |exp| {
- const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta and exp == p.delete_target.e_import_meta;
+ .e_import_meta => {
+ const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta and &expr.data.e_import_meta == &p.delete_target.e_import_meta;
if (p.define.dots.get("meta")) |meta| {
for (meta) |define| {
@@ -9382,11 +9459,15 @@ pub const P = struct {
return p.e(E.Identifier{ .ref = p.import_meta_ref }, expr.loc);
}
},
- .e_spread => |exp| {
+ .e_spread => {
+ const exp = expr.getSpread();
+
exp.value = p.visitExpr(exp.value);
},
- .e_identifier => |e_| {
- const is_delete_target = @as(Expr.Tag, p.delete_target) == .e_identifier and e_ == p.delete_target.e_identifier;
+ .e_identifier => {
+ const e_ = expr.getIdentifier();
+
+ const is_delete_target = @as(Expr.Tag, p.delete_target) == .e_identifier and expr.data.e_identifier.index == p.delete_target.e_identifier.index;
const name = p.loadNameFromRef(e_.ref);
if (p.isStrictMode() and js_lexer.StrictModeReservedWords.has(name)) {
@@ -9437,10 +9518,14 @@ pub const P = struct {
.was_originally_identifier = true,
});
},
- .e_private_identifier => |e_| {
+ .e_private_identifier => {
+ const e_ = expr.getPrivateIdentifier();
+
p.panic("Unexpected private identifier. This is an internal error - not your fault.", .{});
},
- .e_jsx_element => |e_| {
+ .e_jsx_element => {
+ const e_ = expr.getJsxElement();
+
const tag = tagger: {
if (e_.tag) |_tag| {
break :tagger p.visitExpr(_tag);
@@ -9475,7 +9560,7 @@ pub const P = struct {
if (e_.properties.len > 0) {
if (e_.key) |key| {
var props = List(G.Property).fromOwnedSlice(p.allocator, e_.properties);
- props.append(G.Property{ .key = Expr{ .loc = key.loc, .data = keyExprData }, .value = key }) catch unreachable;
+ // props.append(G.Property{ .key = Expr{ .loc = key.loc, .data = keyExprData }, .value = key }) catch unreachable;
args[0] = p.e(E.Object{ .properties = props.toOwnedSlice() }, expr.loc);
} else {
args[0] = p.e(E.Object{ .properties = e_.properties }, expr.loc);
@@ -9513,7 +9598,7 @@ pub const P = struct {
for (e_.children) |child, i| {
e_.children[i] = p.visitExpr(child);
}
- const children_key = Expr{ .data = jsxChildrenKeyData, .loc = expr.loc };
+ const children_key = p.e(E.String{ .utf8 = "key" }, expr.loc);
if (e_.children.len == 1) {
props.append(G.Property{
@@ -9540,29 +9625,29 @@ pub const P = struct {
}
if (p.options.jsx.development) {
- args[3] = Expr{ .loc = expr.loc, .data = falseValueExpr };
+ // args[3] = Expr{ .loc = expr.loc, .data = falseValueExpr };
// placeholder src prop for now
- var source = p.allocator.alloc(G.Property, 3) catch unreachable;
- p.recordUsage(p.jsx_filename_ref);
- source[0] = G.Property{
- .key = Expr{ .loc = expr.loc, .data = Prefill.Data.Filename },
- .value = p.e(E.Identifier{ .ref = p.jsx_filename_ref }, expr.loc),
- };
+ // var source = p.allocator.alloc(G.Property, 3) catch unreachable;
+ // p.recordUsage(p.jsx_filename_ref);
+ // source[0] = G.Property{
+ // .key = Expr{ .loc = expr.loc, .data = Prefill.Data.Filename },
+ // .value = p.e(E.Identifier{ .ref = p.jsx_filename_ref }, expr.loc),
+ // };
- source[1] = G.Property{
- .key = Expr{ .loc = expr.loc, .data = Prefill.Data.LineNumber },
- .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
- };
+ // source[1] = G.Property{
+ // .key = Expr{ .loc = expr.loc, .data = Prefill.Data.LineNumber },
+ // .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
+ // };
- source[2] = G.Property{
- .key = Expr{ .loc = expr.loc, .data = Prefill.Data.ColumnNumber },
- .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
- };
+ // source[2] = G.Property{
+ // .key = Expr{ .loc = expr.loc, .data = Prefill.Data.ColumnNumber },
+ // .value = p.e(E.Number{ .value = @intToFloat(f64, expr.loc.start) }, expr.loc),
+ // };
- args[4] = p.e(E.Object{
- .properties = source,
- }, expr.loc);
- args[5] = Expr{ .data = Prefill.Data.This, .loc = expr.loc };
+ // args[4] = p.e(E.Object{
+ // .properties = source,
+ // }, expr.loc);
+ // args[5] = Expr{ .data = Prefill.Data.This, .loc = expr.loc };
}
return p.e(E.Call{
@@ -9577,7 +9662,9 @@ pub const P = struct {
}
},
- .e_template => |e_| {
+ .e_template => {
+ const e_ = expr.getTemplate();
+
if (e_.tag) |tag| {
e_.tag = p.visitExpr(tag);
}
@@ -9588,10 +9675,14 @@ pub const P = struct {
}
},
- .e_binary => |e_| {
+ .e_binary => {
+ const e_ = expr.getBinary();
+
switch (e_.left.data) {
// Special-case private identifiers
- .e_private_identifier => |private| {
+ .e_private_identifier => {
+ const private = expr.getPrivateIdentifier();
+
if (e_.op == .bin_in) {
const name = p.loadNameFromRef(private.ref);
const result = p.findSymbol(e_.left.loc, name) catch unreachable;
@@ -9612,8 +9703,8 @@ pub const P = struct {
else => {},
}
- const is_call_target = @as(Expr.Tag, p.call_target) == .e_binary and e_ == p.call_target.e_binary;
- const is_stmt_expr = @as(Expr.Tag, p.stmt_expr_value) == .e_binary and e_ == p.stmt_expr_value.e_binary;
+ const is_call_target = @as(Expr.Tag, p.call_target) == .e_binary and expr.data.e_binary.index == p.call_target.e_binary.index;
+ const is_stmt_expr = @as(Expr.Tag, p.stmt_expr_value) == .e_binary and expr.data.e_binary.index == p.stmt_expr_value.e_binary.index;
const was_anonymous_named_expr = p.isAnonymousNamedExpr(e_.right);
e_.left = p.visitExprInOut(e_.left, ExprIn{
@@ -9861,7 +9952,7 @@ pub const P = struct {
// Optionally preserve the name
if (@as(Expr.Tag, e_.left.data) == .e_identifier) {
- e_.right = p.maybeKeepExprSymbolName(e_.right, p.symbols.items[e_.left.data.e_identifier.ref.inner_index].original_name, was_anonymous_named_expr);
+ e_.right = p.maybeKeepExprSymbolName(e_.right, p.symbols.items[e_.left.getIdentifier().ref.inner_index].original_name, was_anonymous_named_expr);
}
},
.bin_add_assign => {
@@ -9912,9 +10003,11 @@ pub const P = struct {
else => {},
}
},
- .e_index => |e_| {
- const is_call_target = std.meta.activeTag(p.call_target) == .e_index and e_ == p.call_target.e_index;
- const is_delete_target = std.meta.activeTag(p.delete_target) == .e_index and e_ == p.delete_target.e_index;
+ .e_index => {
+ const e_ = expr.getIndex();
+
+ const is_call_target = std.meta.activeTag(p.call_target) == .e_index and expr.data.e_index.index == p.call_target.e_index.index;
+ const is_delete_target = std.meta.activeTag(p.delete_target) == .e_index and expr.data.e_index.index == p.delete_target.e_index.index;
const target = p.visitExprInOut(e_.target, ExprIn{
// this is awkward due to a zig compiler bug
@@ -9928,7 +10021,7 @@ pub const P = struct {
in.assign_target,
is_delete_target,
e_.target,
- e_.index.data.e_string.string(p.allocator) catch unreachable,
+ e_.index.getString().string(p.allocator) catch unreachable,
e_.index.loc,
is_call_target,
)) |val| {
@@ -9940,20 +10033,22 @@ pub const P = struct {
// though this is a run-time error, we make it a compile-time error when
// bundling because scope hoisting means these will no longer be run-time
// errors.
- if ((in.assign_target != .none or is_delete_target) and @as(Expr.Tag, e_.target.data) == .e_identifier and p.symbols.items[e_.target.data.e_identifier.ref.inner_index].kind == .import) {
+ if ((in.assign_target != .none or is_delete_target) and @as(Expr.Tag, e_.target.data) == .e_identifier and p.symbols.items[e_.target.getIdentifier().ref.inner_index].kind == .import) {
const r = js_lexer.rangeOfIdentifier(p.source, e_.target.loc);
p.log.addRangeErrorFmt(
p.source,
r,
p.allocator,
"Cannot assign to property on import \"{s}\"",
- .{p.symbols.items[e_.target.data.e_identifier.ref.inner_index].original_name},
+ .{p.symbols.items[e_.target.getIdentifier().ref.inner_index].original_name},
) catch unreachable;
}
return p.e(e_, expr.loc);
},
- .e_unary => |e_| {
+ .e_unary => {
+ const e_ = expr.getUnary();
+
switch (e_.op) {
.un_typeof => {
e_.value = p.visitExprInOut(e_.value, ExprIn{ .assign_target = e_.op.unaryAssignTarget() });
@@ -10017,9 +10112,11 @@ pub const P = struct {
},
}
},
- .e_dot => |e_| {
- const is_delete_target = @as(Expr.Tag, p.delete_target) == .e_dot and e_ == p.delete_target.e_dot;
- const is_call_target = @as(Expr.Tag, p.call_target) == .e_dot and e_ == p.call_target.e_dot;
+ .e_dot => {
+ const e_ = expr.getDot();
+
+ const is_delete_target = @as(Expr.Tag, p.delete_target) == .e_dot and expr.data.e_dot.index == p.delete_target.e_dot.index;
+ const is_call_target = @as(Expr.Tag, p.call_target) == .e_dot and expr.data.e_dot.index == p.call_target.e_dot.index;
if (p.define.dots.get(e_.name)) |parts| {
for (parts) |define| {
@@ -10045,7 +10142,7 @@ pub const P = struct {
}
// Track ".then().catch()" chains
- if (is_call_target and @as(Expr.Tag, p.then_catch_chain.next_target) == .e_dot and p.then_catch_chain.next_target.e_dot == e_) {
+ if (is_call_target and @as(Expr.Tag, p.then_catch_chain.next_target) == .e_dot and p.then_catch_chain.next_target.e_dot.index == expr.data.e_dot.index) {
if (strings.eqlComptime(e_.name, "catch")) {
p.then_catch_chain = ThenCatchChain{
.next_target = e_.target.data,
@@ -10074,8 +10171,10 @@ pub const P = struct {
}
}
},
- .e_if => |e_| {
- const is_call_target = @as(Expr.Data, p.call_target) == .e_if and e_ == p.call_target.e_if;
+ .e_if => {
+ const e_ = expr.getIf();
+
+ const is_call_target = @as(Expr.Data, p.call_target) == .e_if and expr.data.e_if.index == p.call_target.e_if.index;
e_.test_ = p.visitExpr(e_.test_);
@@ -10103,16 +10202,22 @@ pub const P = struct {
}
}
},
- .e_await => |e_| {
+ .e_await => {
+ const e_ = expr.getAwait();
+
p.await_target = e_.value.data;
e_.value = p.visitExpr(e_.value);
},
- .e_yield => |e_| {
+ .e_yield => {
+ const e_ = expr.getYield();
+
if (e_.value) |val| {
e_.value = p.visitExpr(val);
}
},
- .e_array => |e_| {
+ .e_array => {
+ const e_ = expr.getArray();
+
if (in.assign_target != .none) {
if (e_.comma_after_spread) |spread| {
p.log.addRangeError(p.source, logger.Range{ .loc = spread, .len = 1 }, "Unexpected \",\" after rest pattern") catch unreachable;
@@ -10126,10 +10231,14 @@ pub const P = struct {
const data = item.data;
switch (data) {
.e_missing => {},
- .e_spread => |spread| {
+ .e_spread => {
+ const spread = item.getSpread();
+
spread.value = p.visitExprInOut(spread.value, ExprIn{ .assign_target = in.assign_target });
},
- .e_binary => |e2| {
+ .e_binary => {
+ const e2 = item.getBinary();
+
if (in.assign_target != .none and e2.op == .bin_assign) {
const was_anonymous_named_expr = p.isAnonymousNamedExpr(e2.right);
e2.left = p.visitExprInOut(e2.left, ExprIn{ .assign_target = .replace });
@@ -10138,7 +10247,7 @@ pub const P = struct {
if (@as(Expr.Tag, e2.left.data) == .e_identifier) {
e2.right = p.maybeKeepExprSymbolName(
e2.right,
- p.symbols.items[e2.left.data.e_identifier.ref.inner_index].original_name,
+ p.symbols.items[e2.left.getIdentifier().ref.inner_index].original_name,
was_anonymous_named_expr,
);
}
@@ -10153,7 +10262,9 @@ pub const P = struct {
e_.items[i] = item;
}
},
- .e_object => |e_| {
+ .e_object => {
+ const e_ = expr.getObject();
+
if (in.assign_target != .none) {
p.maybeCommaSpreadError(e_.comma_after_spread);
}
@@ -10167,7 +10278,7 @@ pub const P = struct {
// 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,
+ key.getString().utf8,
"__proto__",
)) {
if (has_proto) {
@@ -10183,7 +10294,9 @@ pub const P = struct {
// 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| {
+ .e_binary => {
+ const bin = property.value.?.getBinary();
+
if (bin.op == .bin_assign) {
property.initializer = bin.right;
property.value = bin.left;
@@ -10205,7 +10318,7 @@ pub const P = struct {
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,
+ p.symbols.items[val.getIdentifier().ref.inner_index].original_name,
was_anonymous_named_expr,
);
}
@@ -10213,30 +10326,34 @@ pub const P = struct {
}
}
},
- .e_import => |e_| {
+ .e_import => {
+ const e_ = expr.getImport();
+
const state = TransposeState{
- .is_await_target = if (p.await_target != null) p.await_target.?.e_import == e_ else false,
- .is_then_catch_target = e_ == p.then_catch_chain.next_target.e_import and p.then_catch_chain.has_catch,
+ .is_await_target = if (p.await_target != null) p.await_target.?.e_import.eql(expr.data.e_index) else false,
+ .is_then_catch_target = expr.data.e_import.eql(p.then_catch_chain.next_target.e_import) and p.then_catch_chain.has_catch,
.loc = e_.expr.loc,
};
e_.expr = p.visitExpr(e_.expr);
return p.import_transposer.maybeTransposeIf(e_.expr, state);
},
- .e_call => |e_| {
+ .e_call => {
+ const e_ = expr.getCall();
+
p.call_target = e_.target.data;
p.then_catch_chain = ThenCatchChain{
.next_target = e_.target.data,
.has_multiple_args = e_.args.len >= 2,
- .has_catch = @as(Expr.Tag, p.then_catch_chain.next_target) == .e_call and p.then_catch_chain.next_target.e_call == e_ and p.then_catch_chain.has_catch,
+ .has_catch = @as(Expr.Tag, p.then_catch_chain.next_target) == .e_call and p.then_catch_chain.next_target.e_call.eql(expr.data.e_call) and p.then_catch_chain.has_catch,
};
// Prepare to recognize "require.resolve()" calls
// const could_be_require_resolve = (e_.args.len == 1 and @as(
// Expr.Tag,
// e_.target.data,
- // ) == .e_dot and e_.target.data.e_dot.optional_chain == null and strings.eql(
+ // ) == .e_dot and e_.target.getDot().optional_chain == null and strings.eql(
// e_.target.dat.e_dot.name,
// "resolve",
// ));
@@ -10252,7 +10369,7 @@ pub const P = struct {
has_spread = has_spread or @as(Expr.Tag, e_.args[i].data) == .e_spread;
}
- if (e_.optional_chain == null and @as(Expr.Tag, e_.target.data) == .e_identifier and e_.target.data.e_identifier.ref.eql(p.require_ref)) {
+ if (e_.optional_chain == null and @as(Expr.Tag, e_.target.data) == .e_identifier and e_.target.getIdentifier().ref.eql(p.require_ref)) {
// Heuristic: omit warnings inside try/catch blocks because presumably
// the try/catch statement is there to handle the potential run-time
// error from the unbundled require() call failing.
@@ -10266,7 +10383,9 @@ pub const P = struct {
return expr;
},
- .e_new => |e_| {
+ .e_new => {
+ const e_ = expr.getNew();
+
e_.target = p.visitExpr(e_.target);
// p.warnA
@@ -10275,7 +10394,9 @@ pub const P = struct {
e_.args[i] = p.visitExpr(e_.args[i]);
}
},
- .e_arrow => |e_| {
+ .e_arrow => {
+ const e_ = expr.getArrow();
+
const old_fn_or_arrow_data = std.mem.toBytes(p.fn_or_arrow_data_visit);
p.fn_or_arrow_data_visit = FnOrArrowDataVisit{
.is_arrow = true,
@@ -10310,13 +10431,17 @@ pub const P = struct {
p.fn_only_data_visit.is_inside_async_arrow_fn = old_inside_async_arrow_fn;
p.fn_or_arrow_data_visit = std.mem.bytesToValue(@TypeOf(p.fn_or_arrow_data_visit), &old_fn_or_arrow_data);
},
- .e_function => |e_| {
+ .e_function => {
+ const e_ = expr.getFunction();
+
e_.func = p.visitFunc(e_.func, expr.loc);
if (e_.func.name) |name| {
return p.keepExprSymbolName(expr, p.symbols.items[name.ref.?.inner_index].original_name);
}
},
- .e_class => |e_| {
+ .e_class => {
+ const e_ = expr.getClass();
+
// This might be wrong.
_ = p.visitClass(expr.loc, e_);
},
@@ -10387,7 +10512,7 @@ pub const P = struct {
// 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;
+ // value.getCall().can_be_unwrapped_if_unused = true;
// return value;
}
@@ -10473,13 +10598,19 @@ pub const P = struct {
return true;
},
- .e_dot => |ex| {
+ .e_dot => {
+ const ex = expr.getDot();
+
return ex.can_be_removed_if_unused;
},
- .e_class => |ex| {
+ .e_class => {
+ const ex = expr.getClass();
+
return p.classCanBeRemovedIfUnused(ex);
},
- .e_identifier => |ex| {
+ .e_identifier => {
+ const ex = expr.getIdentifier();
+
if (ex.must_keep_due_to_with_stmt) {
return false;
}
@@ -10507,7 +10638,9 @@ pub const P = struct {
return true;
}
},
- .e_import_identifier => |ex| {
+ .e_import_identifier => {
+ const ex = expr.getImportIdentifier();
+
// References to an ES6 import item are always side-effect free in an
// ECMAScript environment.
//
@@ -10526,10 +10659,14 @@ pub const P = struct {
// references as being side-effect free.
return true;
},
- .e_if => |ex| {
+ .e_if => {
+ const ex = expr.getIf();
+
return p.exprCanBeRemovedIfUnused(ex.test_) and p.exprCanBeRemovedIfUnused(ex.yes) and p.exprCanBeRemovedIfUnused(ex.no);
},
- .e_array => |ex| {
+ .e_array => {
+ const ex = expr.getArray();
+
for (ex.items) |item| {
if (!p.exprCanBeRemovedIfUnused(item)) {
return false;
@@ -10538,7 +10675,9 @@ pub const P = struct {
return true;
},
- .e_object => |ex| {
+ .e_object => {
+ const ex = expr.getObject();
+
for (ex.properties) |property| {
// The key must still be evaluated if it's computed or a spread
@@ -10554,7 +10693,9 @@ pub const P = struct {
}
return true;
},
- .e_call => |ex| {
+ .e_call => {
+ const ex = expr.getCall();
+
// A call that has been marked "__PURE__" can be removed if all arguments
// can be removed. The annotation causes us to ignore the target.
if (ex.can_be_unwrapped_if_unused) {
@@ -10567,7 +10708,9 @@ pub const P = struct {
return true;
},
- .e_new => |ex| {
+ .e_new => {
+ const ex = expr.getNew();
+
// A call that has been marked "__PURE__" can be removed if all arguments
// can be removed. The annotation causes us to ignore the target.
if (ex.can_be_unwrapped_if_unused) {
@@ -10580,7 +10723,9 @@ pub const P = struct {
return true;
},
- .e_unary => |ex| {
+ .e_unary => {
+ const ex = expr.getUnary();
+
switch (ex.op) {
.un_typeof, .un_void, .un_not => {
return p.exprCanBeRemovedIfUnused(ex.value);
@@ -10588,7 +10733,9 @@ pub const P = struct {
else => {},
}
},
- .e_binary => |ex| {
+ .e_binary => {
+ const ex = expr.getBinary();
+
switch (ex.op) {
.bin_strict_eq, .bin_strict_ne, .bin_comma, .bin_logical_or, .bin_logical_and, .bin_nullish_coalescing => {
return p.exprCanBeRemovedIfUnused(ex.left) and p.exprCanBeRemovedIfUnused(ex.right);
@@ -10620,7 +10767,7 @@ pub const P = struct {
is_call_target: bool,
) ?Expr {
if (@as(Expr.Tag, target.data) == .e_identifier) {
- const id = target.data.e_identifier;
+ const id = target.getIdentifier();
// Rewrite property accesses on explicit namespace imports as an identifier.
// This lets us replace them easily in the printer to rebind them to
@@ -10830,7 +10977,9 @@ pub const P = struct {
// Discard type-only export default statements
if (p.options.ts) {
switch (expr.data) {
- .e_identifier => |ident| {
+ .e_identifier => {
+ var ident = expr.getIdentifier();
+
const symbol = p.symbols.items[ident.ref.inner_index];
if (symbol.kind == .unbound) {
if (p.local_type_names.get(symbol.original_name)) |local_type| {
@@ -11362,13 +11511,17 @@ pub const P = struct {
if (enum_value.value != null) {
enum_value.value = p.visitExpr(enum_value.value.?);
switch (enum_value.value.?.data) {
- .e_number => |num| {
+ .e_number => {
+ const num = assign_target.getNumber();
+
// prob never allocates in practice
values_so_far.put(name.string(p.allocator) catch unreachable, num.value) catch unreachable;
has_numeric_value = true;
next_numeric_value = num.value + 1.0;
},
- .e_string => |str| {
+ .e_string => {
+ const str = assign_target.getString();
+
has_string_value = true;
},
else => {},
@@ -11751,10 +11904,14 @@ pub const P = struct {
.e_arrow => {
return true;
},
- .e_function => |func| {
+ .e_function => {
+ const func = expr.getFunction();
+
return func.func.name == null;
},
- .e_class => |class| {
+ .e_class => {
+ const class = expr.getClass();
+
return class.class_name == null;
},
else => {
@@ -11765,7 +11922,9 @@ pub const P = struct {
pub fn valueForDefine(p: *P, loc: logger.Loc, assign_target: js_ast.AssignTarget, is_delete_target: bool, define_data: *const DefineData) Expr {
switch (define_data.value) {
- .e_identifier => |ident| {
+ .e_identifier => {
+ var ident = Expr.Data.Store.Identifier.at(define_data.value.e_identifier);
+
return p.handleIdentifier(
loc,
ident,
@@ -11788,7 +11947,9 @@ pub const P = struct {
pub fn isDotDefineMatch(p: *P, expr: Expr, parts: []const string) bool {
switch (expr.data) {
- .e_dot => |ex| {
+ .e_dot => {
+ const ex = expr.getDot();
+
if (parts.len > 1) {
if (ex.optional_chain != null) {
return false;
@@ -11800,10 +11961,12 @@ pub const P = struct {
return is_tail_match and p.isDotDefineMatch(ex.target, parts[0..last]);
}
},
- .e_import_meta => |ex| {
+ .e_import_meta => {
return parts.len == 2 and strings.eqlComptime(parts[0], "import") and strings.eqlComptime(parts[1], "meta");
},
- .e_identifier => |ex| {
+ .e_identifier => {
+ const ex = expr.getIdentifier();
+
// The last expression must be an identifier
if (parts.len == 1) {
const name = p.loadNameFromRef(ex.ref);
@@ -12027,7 +12190,7 @@ pub const P = struct {
// Special-case EPrivateIdentifier to allow it here
if (is_private) {
- p.recordDeclaredSymbol(property.key.?.data.e_private_identifier.ref) catch unreachable;
+ p.recordDeclaredSymbol(property.key.?.getPrivateIdentifier().ref) catch unreachable;
} else if (property.key) |key| {
class.properties[i].key = p.visitExpr(key);
}
@@ -12050,7 +12213,7 @@ pub const P = struct {
if (is_private) {} else if (!property.flags.is_method and !property.flags.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;
+ name_to_keep = key.getString().string(p.allocator) catch unreachable;
}
}
}
@@ -12327,7 +12490,9 @@ pub const P = struct {
while (i < items.len) : (i += 1) {
var is_spread = false;
switch (items[i].data) {
- .e_spread => |v| {
+ .e_spread => {
+ const v = items[i].getSpread();
+
is_spread = true;
items[i] = v.value;
},
@@ -12604,6 +12769,7 @@ pub const P = struct {
pub fn init(allocator: *std.mem.Allocator, log: *logger.Log, source: *const logger.Source, define: *Define, lexer: js_lexer.Lexer, opts: Parser.Options) !*P {
Stmt.Data.Store.create(allocator);
+ Expr.Data.Store.create(allocator);
var scope_order = try ScopeOrderList.initCapacity(allocator, 1);
var scope = try allocator.create(Scope);