diff options
author | 2021-05-27 21:35:28 -0700 | |
---|---|---|
committer | 2021-05-27 21:35:28 -0700 | |
commit | cbf0b77e52d77ae8a0fe00606e7be2d2af39b83c (patch) | |
tree | 405873010d4a64741ecb2891045c7b261e923ca3 | |
parent | b6e7f01e6ae61bd47b23b918d86d511c390e3510 (diff) | |
download | bun-cbf0b77e52d77ae8a0fe00606e7be2d2af39b83c.tar.gz bun-cbf0b77e52d77ae8a0fe00606e7be2d2af39b83c.tar.zst bun-cbf0b77e52d77ae8a0fe00606e7be2d2af39b83c.zip |
lists
-rw-r--r-- | src/bundler.zig | 4 | ||||
-rw-r--r-- | src/cli.zig | 3 | ||||
-rw-r--r-- | src/defines.zig | 335 | ||||
-rw-r--r-- | src/js_ast.zig | 1177 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 504 | ||||
-rw-r--r-- | src/js_printer.zig | 176 | ||||
-rw-r--r-- | src/resolver/package_json.zig | 21 | ||||
-rw-r--r-- | src/resolver/tsconfig_json.zig | 43 |
8 files changed, 1571 insertions, 692 deletions
diff --git a/src/bundler.zig b/src/bundler.zig index 4e68964df..dcb5a25c5 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -247,7 +247,10 @@ pub const Bundler = struct { const output_file = try bundler.print( result, ); + js_ast.Stmt.Data.Store.reset(); + js_ast.Expr.Data.Store.reset(); + return output_file; } @@ -286,6 +289,7 @@ pub const Bundler = struct { js_printer.Options{ .to_module_ref = Ref.RuntimeRef }, &_linker, ); + // allocator.free(result.source.contents); return options.OutputFile{ .path = out_path, diff --git a/src/cli.zig b/src/cli.zig index dbacee76f..93d473e85 100644 --- a/src/cli.zig +++ b/src/cli.zig @@ -387,6 +387,9 @@ pub const Cli = struct { } } + Output.println("Expr count: {d}", .{js_ast.Expr.icount}); + Output.println("Stmt count: {d}", .{js_ast.Stmt.icount}); + if (!did_write) { for (result.output_files) |file, i| { try writer.writeAll(file.contents); diff --git a/src/defines.zig b/src/defines.zig index 214094847..c16bf9edb 100644 --- a/src/defines.zig +++ b/src/defines.zig @@ -63,63 +63,63 @@ pub const DefineData = struct { var user_defines = UserDefines.init(allocator); try user_defines.ensureCapacity(defines.count()); - var iter = defines.iterator(); - while (iter.next()) |entry| { - var splitter = std.mem.split(entry.key, "."); - while (splitter.next()) |part| { - if (!js_lexer.isIdentifier(part)) { - if (strings.eql(part, entry.key)) { - try log.addErrorFmt(null, logger.Loc{}, allocator, "The define key \"{s}\" must be a valid identifier", .{entry.key}); - } else { - try log.addErrorFmt(null, logger.Loc{}, allocator, "The define key \"{s}\" contains invalid identifier \"{s}\"", .{ part, entry.key }); - } - break; - } - } - - if (js_lexer.isIdentifier(entry.value) and !js_lexer.Keywords.has(entry.value)) { - var ident: *js_ast.E.Identifier = try allocator.create(js_ast.E.Identifier); - ident.ref = Ref.None; - ident.can_be_removed_if_unused = true; - user_defines.putAssumeCapacity( - entry.key, - DefineData{ - .value = js_ast.Expr.Data{ .e_identifier = ident }, - .original_name = entry.value, - .can_be_removed_if_unused = true, - }, - ); - // user_defines.putAssumeCapacity( - // entry.key, - // DefineData{ .value = js_ast.Expr.Data{.e_identifier = } }, - // ); - continue; - } - var _log = log; - var source = logger.Source{ - .contents = entry.value, - .path = defines_path, - .identifier_name = "defines", - .key_path = fs.Path.initWithNamespace("defines", "internal"), - }; - var expr = try json_parser.ParseJSON(&source, _log, allocator); - var data: js_ast.Expr.Data = undefined; - switch (expr.data) { - .e_missing => { - continue; - }, - .e_null, .e_boolean, .e_string, .e_number, .e_object, .e_array => { - data = expr.data; - }, - else => { - continue; - }, - } - - user_defines.putAssumeCapacity(entry.key, DefineData{ - .value = data, - }); - } + // var iter = defines.iterator(); + // while (iter.next()) |entry| { + // var splitter = std.mem.split(entry.key, "."); + // while (splitter.next()) |part| { + // if (!js_lexer.isIdentifier(part)) { + // if (strings.eql(part, entry.key)) { + // try log.addErrorFmt(null, logger.Loc{}, allocator, "The define key \"{s}\" must be a valid identifier", .{entry.key}); + // } else { + // try log.addErrorFmt(null, logger.Loc{}, allocator, "The define key \"{s}\" contains invalid identifier \"{s}\"", .{ part, entry.key }); + // } + // break; + // } + // } + + // if (js_lexer.isIdentifier(entry.value) and !js_lexer.Keywords.has(entry.value)) { + // var ident: *js_ast.E.Identifier = try allocator.create(js_ast.E.Identifier); + // ident.ref = Ref.None; + // ident.can_be_removed_if_unused = true; + // user_defines.putAssumeCapacity( + // entry.key, + // DefineData{ + // .value = js_ast.Expr.Data{ .e_identifier = ident }, + // .original_name = entry.value, + // .can_be_removed_if_unused = true, + // }, + // ); + // // user_defines.putAssumeCapacity( + // // entry.key, + // // DefineData{ .value = js_ast.Expr.Data{.e_identifier = } }, + // // ); + // continue; + // } + // var _log = log; + // var source = logger.Source{ + // .contents = entry.value, + // .path = defines_path, + // .identifier_name = "defines", + // .key_path = fs.Path.initWithNamespace("defines", "internal"), + // }; + // var expr = try json_parser.ParseJSON(&source, _log, allocator); + // var data: js_ast.Expr.Data = undefined; + // switch (expr.data) { + // .e_missing => { + // continue; + // }, + // .e_null, .e_boolean, .e_string, .e_number, .e_object, .e_array => { + // data = expr.data; + // }, + // else => { + // continue; + // }, + // } + + // user_defines.putAssumeCapacity(entry.key, DefineData{ + // .value = data, + // }); + // } return user_defines; } @@ -157,117 +157,118 @@ pub const Define = struct { define.allocator = allocator; define.identifiers = std.StringHashMap(IdentifierDefine).init(allocator); define.dots = std.StringHashMap([]DotDefine).init(allocator); - try define.identifiers.ensureCapacity(641); - try define.dots.ensureCapacity(64); - - var undefined_val = try allocator.create(js_ast.E.Undefined); - var val = js_ast.Expr.Data{ .e_undefined = undefined_val }; - var ident_define = IdentifierDefine{ - .value = val, - }; - var value_define = DefineData{ .value = val, .valueless = true }; - // Step 1. Load the globals into the hash tables - for (GlobalDefinesKey) |global| { - if (global.len == 1) { - - // TODO: when https://github.com/ziglang/zig/pull/8596 is merged, switch to putAssumeCapacityNoClobber - define.identifiers.putAssumeCapacity(global[0], value_define); - } else { - const key = global[global.len - 1]; - // TODO: move this to comptime - // TODO: when https://github.com/ziglang/zig/pull/8596 is merged, switch to putAssumeCapacityNoClobber - if (define.dots.getEntry(key)) |entry| { - var list = try std.ArrayList(DotDefine).initCapacity(allocator, entry.value.len + 1); - list.appendSliceAssumeCapacity(entry.value); - list.appendAssumeCapacity(DotDefine{ - .parts = global[0..global.len], - .data = value_define, - }); - - define.dots.putAssumeCapacity(key, list.toOwnedSlice()); - } else { - var list = try std.ArrayList(DotDefine).initCapacity(allocator, 1); - list.appendAssumeCapacity(DotDefine{ - .parts = global[0..global.len], - .data = value_define, - }); - - define.dots.putAssumeCapacity(key, list.toOwnedSlice()); - } - } - } - - var nan_val = try allocator.create(js_ast.E.Number); - nan_val.value = std.math.nan_f64; - - var inf_val = try allocator.create(js_ast.E.Number); - inf_val.value = std.math.inf_f64; - - // Step 2. Swap in certain literal values because those can be constant folded - define.identifiers.putAssumeCapacity("undefined", value_define); - define.identifiers.putAssumeCapacity("NaN", DefineData{ - .value = js_ast.Expr.Data{ .e_number = nan_val }, - }); - define.identifiers.putAssumeCapacity("Infinity", DefineData{ - .value = js_ast.Expr.Data{ .e_number = inf_val }, - }); - - // Step 3. Load user data into hash tables - // At this stage, user data has already been validated. - if (_user_defines) |user_defines| { - var iter = user_defines.iterator(); - while (iter.next()) |user_define| { - // If it has a dot, then it's a DotDefine. - // e.g. process.env.NODE_ENV - if (strings.lastIndexOfChar(user_define.key, '.')) |last_dot| { - const tail = user_define.key[last_dot + 1 .. user_define.key.len]; - const remainder = user_define.key[0..last_dot]; - const count = std.mem.count(u8, remainder, ".") + 1; - var parts = try allocator.alloc(string, count + 1); - var splitter = std.mem.split(remainder, "."); - var i: usize = 0; - while (splitter.next()) |split| : (i += 1) { - parts[i] = split; - } - parts[i] = tail; - var didFind = false; - var initial_values: []DotDefine = &([_]DotDefine{}); - - // "NODE_ENV" - if (define.dots.getEntry(tail)) |entry| { - for (entry.value) |*part| { - // ["process", "env"] === ["process", "env"] (if that actually worked) - if (arePartsEqual(part.parts, parts)) { - part.data = part.data.merge(user_define.value); - didFind = true; - break; - } - } - - initial_values = entry.value; - } - - if (!didFind) { - var list = try std.ArrayList(DotDefine).initCapacity(allocator, initial_values.len + 1); - if (initial_values.len > 0) { - list.appendSliceAssumeCapacity(initial_values); - } - - list.appendAssumeCapacity(DotDefine{ - .data = user_define.value, - // TODO: do we need to allocate this? - .parts = parts, - }); - try define.dots.put(tail, list.toOwnedSlice()); - } - } else { - // e.g. IS_BROWSER - try define.identifiers.put(user_define.key, user_define.value); - } - } - } - return define; + // try define.identifiers.ensureCapacity(641); + // try define.dots.ensureCapacity(64); + + // var undefined_val = try allocator.create(js_ast.E.Undefined); + // var val = js_ast.Expr.Data{ .e_undefined = undefined_val }; + // var ident_define = IdentifierDefine{ + // .value = val, + // }; + // var value_define = DefineData{ .value = val, .valueless = true }; + // // Step 1. Load the globals into the hash tables + // for (GlobalDefinesKey) |global| { + // if (global.len == 1) { + + // // TODO: when https://github.com/ziglang/zig/pull/8596 is merged, switch to putAssumeCapacityNoClobber + // define.identifiers.putAssumeCapacity(global[0], value_define); + // } else { + // const key = global[global.len - 1]; + // // TODO: move this to comptime + // // TODO: when https://github.com/ziglang/zig/pull/8596 is merged, switch to putAssumeCapacityNoClobber + // if (define.dots.getEntry(key)) |entry| { + // var list = try std.ArrayList(DotDefine).initCapacity(allocator, entry.value.len + 1); + // list.appendSliceAssumeCapacity(entry.value); + // list.appendAssumeCapacity(DotDefine{ + // .parts = global[0..global.len], + // .data = value_define, + // }); + + // define.dots.putAssumeCapacity(key, list.toOwnedSlice()); + // } else { + // var list = try std.ArrayList(DotDefine).initCapacity(allocator, 1); + // list.appendAssumeCapacity(DotDefine{ + // .parts = global[0..global.len], + // .data = value_define, + // }); + + // define.dots.putAssumeCapacity(key, list.toOwnedSlice()); + // } + // } + // } + + // var nan_val = try allocator.create(js_ast.E.Number); + // nan_val.value = std.math.nan_f64; + + // var inf_val = try allocator.create(js_ast.E.Number); + // inf_val.value = std.math.inf_f64; + + // // Step 2. Swap in certain literal values because those can be constant folded + // define.identifiers.putAssumeCapacity("undefined", value_define); + // define.identifiers.putAssumeCapacity("NaN", DefineData{ + // .value = js_ast.Expr.Data{ .e_number = nan_val }, + // }); + // define.identifiers.putAssumeCapacity("Infinity", DefineData{ + // .value = js_ast.Expr.Data{ .e_number = inf_val }, + // }); + + // // Step 3. Load user data into hash tables + // // At this stage, user data has already been validated. + // if (_user_defines) |user_defines| { + // var iter = user_defines.iterator(); + // while (iter.next()) |user_define| { + // // If it has a dot, then it's a DotDefine. + // // e.g. process.env.NODE_ENV + // if (strings.lastIndexOfChar(user_define.key, '.')) |last_dot| { + // const tail = user_define.key[last_dot + 1 .. user_define.key.len]; + // const remainder = user_define.key[0..last_dot]; + // const count = std.mem.count(u8, remainder, ".") + 1; + // var parts = try allocator.alloc(string, count + 1); + // var splitter = std.mem.split(remainder, "."); + // var i: usize = 0; + // while (splitter.next()) |split| : (i += 1) { + // parts[i] = split; + // } + // parts[i] = tail; + // var didFind = false; + // var initial_values: []DotDefine = &([_]DotDefine{}); + + // // "NODE_ENV" + // if (define.dots.getEntry(tail)) |entry| { + // for (entry.value) |*part| { + // // ["process", "env"] === ["process", "env"] (if that actually worked) + // if (arePartsEqual(part.parts, parts)) { + // part.data = part.data.merge(user_define.value); + // didFind = true; + // break; + // } + // } + + // initial_values = entry.value; + // } + + // if (!didFind) { + // var list = try std.ArrayList(DotDefine).initCapacity(allocator, initial_values.len + 1); + // if (initial_values.len > 0) { + // list.appendSliceAssumeCapacity(initial_values); + // } + + // list.appendAssumeCapacity(DotDefine{ + // .data = user_define.value, + // // TODO: do we need to allocate this? + // .parts = parts, + // }); + // try define.dots.put(tail, list.toOwnedSlice()); + // } + // } else { + // // e.g. IS_BROWSER + // try define.identifiers.put(user_define.key, user_define.value); + // } + // } + // } + + // return define; } }; diff --git a/src/js_ast.zig b/src/js_ast.zig index 425ff6a83..08439a6cb 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -8,6 +8,15 @@ usingnamespace @import("ast/base.zig"); const ImportRecord = @import("import_record.zig").ImportRecord; const allocators = @import("allocators.zig"); +pub const ListIndex = packed struct { + index: u31, + is_overflowing: bool = false, + + pub fn eql(a: ListIndex, b: ListIndex) bool { + return @bitCast(u32, a) == @bitCast(u32, b); + } +}; + // There are three types. // 1. Expr (expression) // 2. Stmt (statement) @@ -1171,8 +1180,9 @@ pub const Stmt = struct { pub inline fn getWith(self: *const @This()) *S.With { return Data.Store.With.at(self.data.s_with); } - + pub var icount: usize = 0; pub fn init(origData: anytype, loc: logger.Loc) Stmt { + icount += 1; if (@typeInfo(@TypeOf(origData)) != .Pointer and @TypeOf(origData) != S.Empty) { @compileError("Stmt.init needs a pointer."); } @@ -1295,6 +1305,7 @@ pub const Stmt = struct { } pub fn alloc(allocator: *std.mem.Allocator, origData: anytype, loc: logger.Loc) Stmt { + icount += 1; switch (@TypeOf(origData)) { S.Block => { return Stmt.comptime_alloc(allocator, "s_block", S.Block, origData, loc); @@ -1439,46 +1450,41 @@ pub const Stmt = struct { }; pub const Data = union(Tag) { - s_block: Store.ListIndex, - s_break: Store.ListIndex, - s_class: Store.ListIndex, - s_comment: Store.ListIndex, - s_continue: Store.ListIndex, + s_block: ListIndex, + s_break: ListIndex, + s_class: ListIndex, + s_comment: ListIndex, + s_continue: ListIndex, s_debugger: S.Debugger, - s_directive: Store.ListIndex, - s_do_while: Store.ListIndex, + s_directive: ListIndex, + s_do_while: ListIndex, s_empty: S.Empty, // special case, its a zero value type - s_enum: Store.ListIndex, - s_export_clause: Store.ListIndex, - s_export_default: Store.ListIndex, - s_export_equals: Store.ListIndex, - s_export_from: Store.ListIndex, - s_export_star: Store.ListIndex, - s_expr: Store.ListIndex, - s_for_in: Store.ListIndex, - s_for_of: Store.ListIndex, - s_for: Store.ListIndex, - s_function: Store.ListIndex, - s_if: Store.ListIndex, - s_import: Store.ListIndex, - s_label: Store.ListIndex, - s_lazy_export: Store.ListIndex, - s_local: Store.ListIndex, - s_namespace: Store.ListIndex, - s_return: Store.ListIndex, - s_switch: Store.ListIndex, - s_throw: Store.ListIndex, - s_try: Store.ListIndex, + s_enum: ListIndex, + s_export_clause: ListIndex, + s_export_default: ListIndex, + s_export_equals: ListIndex, + s_export_from: ListIndex, + s_export_star: ListIndex, + s_expr: ListIndex, + s_for_in: ListIndex, + s_for_of: ListIndex, + s_for: ListIndex, + s_function: ListIndex, + s_if: ListIndex, + s_import: ListIndex, + s_label: ListIndex, + s_lazy_export: ListIndex, + s_local: ListIndex, + s_namespace: ListIndex, + s_return: ListIndex, + s_switch: ListIndex, + s_throw: ListIndex, + s_try: ListIndex, s_type_script: S.TypeScript, - s_while: Store.ListIndex, - s_with: Store.ListIndex, + s_while: ListIndex, + s_with: ListIndex, pub const Store = struct { - pub const ListIndex = packed struct { - index: u31, - is_overflowing: bool = false, - }; - pub const Block = NewStore(S.Block); pub const Break = NewStore(S.Break); pub const Class = NewStore(S.Class); @@ -1687,7 +1693,7 @@ pub const Stmt = struct { } pub fn NewStore(comptime ValueType: type) type { - const count = 1024; + const count = 8096; const max_index = count - 1; const list_count = count; return struct { @@ -1931,15 +1937,122 @@ pub const Expr = struct { } pub const Query = struct { expr: Expr, loc: logger.Loc }; - pub fn getProperty(expr: *const Expr, name: string) ?Query { + pub fn getArray(exp: *const Expr) *E.Array { + return Data.Store.Array.at(exp.data.e_array); + } + pub fn getUnary(exp: *const Expr) *E.Unary { + return Data.Store.Unary.at(exp.data.e_unary); + } + pub fn getBinary(exp: *const Expr) *E.Binary { + return Data.Store.Binary.at(exp.data.e_binary); + } + pub fn getThis(exp: *const Expr) *E.This { + return E.This{}; + } + pub fn getClass(exp: *const Expr) *E.Class { + return Data.Store.Class.at(exp.data.e_class); + } + pub fn getBoolean(exp: *const Expr) *E.Boolean { + return Data.Store.Boolean.at(exp.data.e_boolean); + } + pub fn getSuper(exp: *const Expr) *E.Super { + return Data.Store.Super.at(exp.data.e_super); + } + pub fn getNull(exp: *const Expr) *E.Null { + return Data.Store.Null.at(exp.data.e_null); + } + pub fn getUndefined(exp: *const Expr) *E.Undefined { + return Data.Store.Undefined.at(exp.data.e_undefined); + } + pub fn getNew(exp: *const Expr) *E.New { + return Data.Store.New.at(exp.data.e_new); + } + pub fn getNewTarget(exp: *const Expr) *E.NewTarget { + return &E.NewTarget{}; + } + pub fn getFunction(exp: *const Expr) *E.Function { + return Data.Store.Function.at(exp.data.e_function); + } + + pub fn getCall(exp: *const Expr) *E.Call { + return Data.Store.Call.at(exp.data.e_call); + } + pub fn getDot(exp: *const Expr) *E.Dot { + return Data.Store.Dot.at(exp.data.e_dot); + } + pub fn getIndex(exp: *const Expr) *E.Index { + return Data.Store.Index.at(exp.data.e_index); + } + pub fn getArrow(exp: *const Expr) *E.Arrow { + return Data.Store.Arrow.at(exp.data.e_arrow); + } + pub fn getIdentifier(exp: *const Expr) *E.Identifier { + return Data.Store.Identifier.at(exp.data.e_identifier); + } + pub fn getImportIdentifier(exp: *const Expr) *E.ImportIdentifier { + return Data.Store.ImportIdentifier.at(exp.data.e_import_identifier); + } + pub fn getPrivateIdentifier(exp: *const Expr) *E.PrivateIdentifier { + return Data.Store.PrivateIdentifier.at(exp.data.e_private_identifier); + } + pub fn getJsxElement(exp: *const Expr) *E.JSXElement { + return Data.Store.JSXElement.at(exp.data.e_jsx_element); + } + pub fn getMissing(exp: *const Expr) *E.Missing { + return Data.Store.Missing.at(exp.data.e_missing); + } + pub fn getNumber(exp: *const Expr) *E.Number { + return Data.Store.Number.at(exp.data.e_number); + } + pub fn getBigInt(exp: *const Expr) *E.BigInt { + return Data.Store.BigInt.at(exp.data.e_big_int); + } + pub fn getObject(exp: *const Expr) *E.Object { + return Data.Store.Object.at(exp.data.e_object); + } + pub fn getSpread(exp: *const Expr) *E.Spread { + return Data.Store.Spread.at(exp.data.e_spread); + } + pub fn getString(exp: *const Expr) *E.String { + return Data.Store.String.at(exp.data.e_string); + } + pub fn getTemplatePart(exp: *const Expr) *E.TemplatePart { + return Data.Store.TemplatePart.at(exp.data.e_template_part); + } + pub fn getTemplate(exp: *const Expr) *E.Template { + return Data.Store.Template.at(exp.data.e_template); + } + pub fn getRegExp(exp: *const Expr) *E.RegExp { + return Data.Store.RegExp.at(exp.data.e_reg_exp); + } + pub fn getAwait(exp: *const Expr) *E.Await { + return Data.Store.Await.at(exp.data.e_await); + } + pub fn getYield(exp: *const Expr) *E.Yield { + return Data.Store.Yield.at(exp.data.e_yield); + } + pub fn getIf(exp: *const Expr) *E.If { + return Data.Store.If.at(exp.data.e_if); + } + pub fn getRequire(exp: *const Expr) *E.Require { + return Data.Store.Require.at(exp.data.e_require); + } + pub fn getRequireOrRequireResolve(exp: *const Expr) *E.RequireOrRequireResolve { + return Data.Store.RequireOrRequireResolve.at(exp.data.e_require_or_require_resolve); + } + pub fn getImport(exp: *const Expr) *E.Import { + return Data.Store.Import.at(exp.data.e_import); + } + + pub fn asProperty(expr: *const Expr, name: string) ?Query { if (std.meta.activeTag(expr.data) != .e_object) return null; - const obj: *const E.Object = expr.data.e_object; + const obj = expr.getObject(); for (obj.properties) |prop| { const value = prop.value orelse continue; const key = prop.key orelse continue; if (std.meta.activeTag(key.data) != .e_string) continue; - const key_str: *const E.String = key.data.e_string; + const key_str: *const E.String = key.getString(); if (key_str.eql(string, name)) { return Query{ .expr = value, .loc = key.loc }; } @@ -1948,20 +2061,20 @@ pub const Expr = struct { return null; } - pub fn getString(expr: *const Expr, allocator: *std.mem.Allocator) ?string { + pub fn asString(expr: *const Expr, allocator: *std.mem.Allocator) ?string { if (std.meta.activeTag(expr.data) != .e_string) return null; - const key_str: *const E.String = expr.data.e_string; + const key_str: *const E.String = expr.getString(); return if (key_str.isUTF8()) key_str.utf8 else key_str.string(allocator) catch null; } - pub fn getBool( + pub fn asBool( expr: *const Expr, ) ?bool { if (std.meta.activeTag(expr.data) != .e_boolean) return null; - const obj = expr.data.e_boolean; + const obj = expr.getBoolean(); return obj.value; } @@ -2021,7 +2134,7 @@ pub const Expr = struct { return null; } - return [2]f64{ left.e_number.value, right.e_number.value }; + return [2]f64{ Expr.Data.Store.Number.at(left.e_number).value, Expr.Data.Store.Number.at(right.e_number).value }; } pub fn isAnonymousNamed(e: *Expr) bool { @@ -2041,411 +2154,584 @@ pub const Expr = struct { } } + pub var icount: usize = 0; pub fn init(exp: anytype, loc: logger.Loc) Expr { - switch (@TypeOf(exp)) { - *E.Array => { + icount += 1; + const st = exp.*; + switch (@TypeOf(st)) { + E.Array => { return Expr{ .loc = loc, - .data = Data{ .e_array = exp }, + .data = Data{ + .e_array = Data.Store.Array.append(st), + }, }; }, - *E.Unary => { + E.Class => { return Expr{ .loc = loc, - .data = Data{ .e_unary = exp }, + .data = Data{ + .e_class = Data.Store.Class.append(st), + }, }; }, - *E.Binary => { + E.Unary => { return Expr{ .loc = loc, - .data = Data{ .e_binary = exp }, + .data = Data{ + .e_unary = Data.Store.Unary.append(st), + }, }; }, - *E.This => { + E.Binary => { return Expr{ .loc = loc, - .data = Data{ .e_this = exp }, + .data = Data{ + .e_binary = Data.Store.Binary.append(st), + }, }; }, - *E.Boolean => { + E.This => { return Expr{ .loc = loc, - .data = Data{ .e_boolean = exp }, + .data = Data{ + .e_this = st, + }, }; }, - *E.Super => { + E.Boolean => { return Expr{ .loc = loc, - .data = Data{ .e_super = exp }, + .data = Data{ + .e_boolean = Data.Store.Boolean.append(st), + }, }; }, - *E.Null => { + E.Super => { return Expr{ .loc = loc, - .data = Data{ .e_null = exp }, + .data = Data{ + .e_super = st, + }, }; }, - *E.Undefined => { + E.Null => { return Expr{ .loc = loc, - .data = Data{ .e_undefined = exp }, + .data = Data{ + .e_null = st, + }, }; }, - *E.New => { + E.Undefined => { return Expr{ .loc = loc, - .data = Data{ .e_new = exp }, + .data = Data{ + .e_undefined = st, + }, }; }, - *E.NewTarget => { + E.New => { return Expr{ .loc = loc, - .data = Data{ .e_new_target = exp }, + .data = Data{ + .e_new = Data.Store.New.append(st), + }, }; }, - *E.Function => { + E.NewTarget => { return Expr{ .loc = loc, - .data = Data{ .e_function = exp }, + .data = Data{ + .e_new_target = st, + }, }; }, - *E.ImportMeta => { + E.Function => { return Expr{ .loc = loc, - .data = Data{ .e_import_meta = exp }, + .data = Data{ + .e_function = Data.Store.Function.append(st), + }, }; }, - *E.Call => { + E.ImportMeta => { return Expr{ .loc = loc, - .data = Data{ .e_call = exp }, + .data = Data{ + .e_import_meta = Data.Store.ImportMeta.append(st), + }, }; }, - *E.Dot => { + E.Call => { return Expr{ .loc = loc, - .data = Data{ .e_dot = exp }, + .data = Data{ + .e_call = Data.Store.Call.append(st), + }, }; }, - *E.Index => { + E.Dot => { return Expr{ .loc = loc, - .data = Data{ .e_index = exp }, + .data = Data{ + .e_dot = Data.Store.Dot.append(st), + }, }; }, - *E.Arrow => { + E.Index => { return Expr{ .loc = loc, - .data = Data{ .e_arrow = exp }, + .data = Data{ + .e_index = Data.Store.Index.append(st), + }, }; }, - *E.Identifier => { + E.Arrow => { return Expr{ .loc = loc, - .data = Data{ .e_identifier = exp }, + .data = Data{ + .e_arrow = Data.Store.Arrow.append(st), + }, }; }, - *E.ImportIdentifier => { + E.Identifier => { return Expr{ .loc = loc, - .data = Data{ .e_import_identifier = exp }, + .data = Data{ + .e_identifier = Data.Store.Identifier.append(st), + }, }; }, - *E.PrivateIdentifier => { + E.ImportIdentifier => { return Expr{ .loc = loc, - .data = Data{ .e_private_identifier = exp }, + .data = Data{ + .e_import_identifier = Data.Store.ImportIdentifier.append(st), + }, }; }, - *E.JSXElement => { + E.PrivateIdentifier => { return Expr{ .loc = loc, - .data = Data{ .e_jsx_element = exp }, + .data = Data{ + .e_private_identifier = Data.Store.PrivateIdentifier.append(st), + }, }; }, - *E.Missing => { + E.JSXElement => { return Expr{ .loc = loc, - .data = Data{ .e_missing = exp }, + .data = Data{ + .e_jsx_element = Data.Store.JSXElement.append(st), + }, }; }, - *E.Number => { - return Expr{ - .loc = loc, - .data = Data{ .e_number = exp }, - }; + E.Missing => { + return Expr{ .loc = loc, .data = Data{ .e_missing = E.Missing{} } }; }, - *E.BigInt => { + E.Number => { return Expr{ .loc = loc, - .data = Data{ .e_big_int = exp }, + .data = Data{ + .e_number = Data.Store.Number.append(st), + }, }; }, - *E.Object => { + E.BigInt => { return Expr{ .loc = loc, - .data = Data{ .e_object = exp }, + .data = Data{ + .e_big_int = Data.Store.BigInt.append(st), + }, }; }, - *E.Spread => { + E.Object => { return Expr{ .loc = loc, - .data = Data{ .e_spread = exp }, + .data = Data{ + .e_object = Data.Store.Object.append(st), + }, }; }, - *E.String => { + E.Spread => { return Expr{ .loc = loc, - .data = Data{ .e_string = exp }, + .data = Data{ + .e_spread = Data.Store.Spread.append(st), + }, }; }, - *E.TemplatePart => { + E.String => { return Expr{ .loc = loc, - .data = Data{ .e_template_part = exp }, + .data = Data{ + .e_string = Data.Store.String.append(st), + }, }; }, - *E.Class => { + E.TemplatePart => { return Expr{ .loc = loc, - .data = Data{ .e_class = exp }, + .data = Data{ + .e_template_part = Data.Store.TemplatePart.append(st), + }, }; }, - *E.Template => { + E.Template => { return Expr{ .loc = loc, - .data = Data{ .e_template = exp }, + .data = Data{ + .e_template = Data.Store.Template.append(st), + }, }; }, - *E.RegExp => { + E.RegExp => { return Expr{ .loc = loc, - .data = Data{ .e_reg_exp = exp }, + .data = Data{ + .e_reg_exp = Data.Store.RegExp.append(st), + }, }; }, - *E.Await => { + E.Await => { return Expr{ .loc = loc, - .data = Data{ .e_await = exp }, + .data = Data{ + .e_await = Data.Store.Await.append(st), + }, }; }, - *E.Yield => { + E.Yield => { return Expr{ .loc = loc, - .data = Data{ .e_yield = exp }, + .data = Data{ + .e_yield = Data.Store.Yield.append(st), + }, }; }, - *E.If => { + E.If => { return Expr{ .loc = loc, - .data = Data{ .e_if = exp }, + .data = Data{ + .e_if = Data.Store.If.append(st), + }, }; }, - - *E.Import => { + E.RequireOrRequireResolve => { return Expr{ .loc = loc, - .data = Data{ .e_import = exp }, + .data = Data{ + .e_require_or_require_resolve = Data.Store.RequireOrRequireResolve.append(st), + }, }; }, - *E.Require => { + E.Import => { return Expr{ .loc = loc, - .data = Data{ .e_require = exp }, + .data = Data{ + .e_import = Data.Store.Import.append(st), + }, }; }, - *E.RequireOrRequireResolve => { + E.Require => { return Expr{ .loc = loc, - .data = Data{ .e_require_or_require_resolve = exp }, + .data = Data{ + .e_require = Data.Store.Require.append(st), + }, }; }, else => { - @compileError("Expr.init needs a pointer to E.*"); + @compileError("Invalid type passed to Expr.init"); }, } } pub fn alloc(allocator: *std.mem.Allocator, st: anytype, loc: logger.Loc) Expr { + icount += 1; switch (@TypeOf(st)) { E.Array => { - var dat = allocator.create(E.Array) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_array = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_array = Data.Store.Array.append(st), + }, + }; }, E.Class => { - var dat = allocator.create(E.Class) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_class = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_class = Data.Store.Class.append(st), + }, + }; }, E.Unary => { - var dat = allocator.create(E.Unary) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_unary = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_unary = Data.Store.Unary.append(st), + }, + }; }, E.Binary => { - var dat = allocator.create(E.Binary) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_binary = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_binary = Data.Store.Binary.append(st), + }, + }; }, E.This => { - var dat = allocator.create(E.This) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_this = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_this = st, + }, + }; }, E.Boolean => { - var dat = allocator.create(E.Boolean) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_boolean = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_boolean = Data.Store.Boolean.append(st), + }, + }; }, E.Super => { - var dat = allocator.create(E.Super) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_super = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_super = st, + }, + }; }, E.Null => { - var dat = allocator.create(E.Null) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_null = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_null = st, + }, + }; }, E.Undefined => { - var dat = allocator.create(E.Undefined) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_undefined = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_undefined = st, + }, + }; }, E.New => { - var dat = allocator.create(E.New) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_new = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_new = Data.Store.New.append(st), + }, + }; }, E.NewTarget => { - var dat = allocator.create(E.NewTarget) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_new_target = dat } }; + return Expr{ + .loc = loc, + .data = Data{ .e_new_target = st }, + }; }, E.Function => { - var dat = allocator.create(E.Function) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_function = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_function = Data.Store.Function.append(st), + }, + }; }, E.ImportMeta => { - var dat = allocator.create(E.ImportMeta) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_import_meta = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_import_meta = st, + }, + }; }, E.Call => { - var dat = allocator.create(E.Call) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_call = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_call = Data.Store.Call.append(st), + }, + }; }, E.Dot => { - var dat = allocator.create(E.Dot) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_dot = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_dot = Data.Store.Dot.append(st), + }, + }; }, E.Index => { - var dat = allocator.create(E.Index) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_index = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_index = Data.Store.Index.append(st), + }, + }; }, E.Arrow => { - var dat = allocator.create(E.Arrow) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_arrow = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_arrow = Data.Store.Arrow.append(st), + }, + }; }, E.Identifier => { - var dat = allocator.create(E.Identifier) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_identifier = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_identifier = Data.Store.Identifier.append(st), + }, + }; }, E.ImportIdentifier => { - var dat = allocator.create(E.ImportIdentifier) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_import_identifier = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_import_identifier = Data.Store.ImportIdentifier.append(st), + }, + }; }, E.PrivateIdentifier => { - var dat = allocator.create(E.PrivateIdentifier) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_private_identifier = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_private_identifier = Data.Store.PrivateIdentifier.append(st), + }, + }; }, E.JSXElement => { - var dat = allocator.create(E.JSXElement) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_jsx_element = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_jsx_element = Data.Store.JSXElement.append(st), + }, + }; }, E.Missing => { return Expr{ .loc = loc, .data = Data{ .e_missing = E.Missing{} } }; }, E.Number => { - var dat = allocator.create(E.Number) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_number = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_number = Data.Store.Number.append(st), + }, + }; }, E.BigInt => { - var dat = allocator.create(E.BigInt) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_big_int = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_big_int = Data.Store.BigInt.append(st), + }, + }; }, E.Object => { - var dat = allocator.create(E.Object) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_object = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_object = Data.Store.Object.append(st), + }, + }; }, E.Spread => { - var dat = allocator.create(E.Spread) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_spread = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_spread = Data.Store.Spread.append(st), + }, + }; }, E.String => { - var dat = allocator.create(E.String) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_string = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_string = Data.Store.String.append(st), + }, + }; }, E.TemplatePart => { - var dat = allocator.create(E.TemplatePart) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_template_part = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_template_part = Data.Store.TemplatePart.append(st), + }, + }; }, E.Template => { - var dat = allocator.create(E.Template) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_template = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_template = Data.Store.Template.append(st), + }, + }; }, E.RegExp => { - var dat = allocator.create(E.RegExp) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_reg_exp = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_reg_exp = Data.Store.RegExp.append(st), + }, + }; }, E.Await => { - var dat = allocator.create(E.Await) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_await = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_await = Data.Store.Await.append(st), + }, + }; }, E.Yield => { - var dat = allocator.create(E.Yield) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_yield = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_yield = Data.Store.Yield.append(st), + }, + }; }, E.If => { - var dat = allocator.create(E.If) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_if = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_if = Data.Store.If.append(st), + }, + }; }, E.RequireOrRequireResolve => { - var dat = allocator.create(E.RequireOrRequireResolve) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_require_or_require_resolve = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_require_or_require_resolve = Data.Store.RequireOrRequireResolve.append(st), + }, + }; }, E.Import => { - var dat = allocator.create(E.Import) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_import = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_import = Data.Store.Import.append(st), + }, + }; }, E.Require => { - var dat = allocator.create(E.Require) catch unreachable; - dat.* = st; - return Expr{ .loc = loc, .data = Data{ .e_require = dat } }; + return Expr{ + .loc = loc, + .data = Data{ + .e_require = Data.Store.Require.append(st), + }, + }; }, else => { @@ -2850,13 +3136,16 @@ pub const Expr = struct { return true; }, - .e_if => |ex| { + .e_if => { + const ex = a.getIf(); return isBoolean(ex.yes) and isBoolean(ex.no); }, - .e_unary => |ex| { + .e_unary => { + const ex = a.getUnary(); return ex.op == .un_not or ex.op == .un_delete; }, - .e_binary => |ex| { + .e_binary => { + const ex = a.getBinary(); switch (ex.op) { .bin_strict_eq, .bin_strict_ne, .bin_loose_eq, .bin_loose_ne, .bin_lt, .bin_gt, .bin_le, .bin_ge, .bin_instanceof, .bin_in => { return true; @@ -2905,13 +3194,16 @@ pub const Expr = struct { .e_null, .e_undefined => { return expr.at(E.Boolean{ .value = true }, allocator); }, - .e_boolean => |b| { + .e_boolean => { + const b = expr.getBoolean(); return expr.at(E.Boolean{ .value = b.value }, allocator); }, - .e_number => |n| { + .e_number => { + const n = expr.getNumber(); return expr.at(E.Boolean{ .value = (n.value == 0 or std.math.isNan(n.value)) }, allocator); }, - .e_big_int => |b| { + .e_big_int => { + const b = expr.getBigInt(); return expr.at(E.Boolean{ .value = strings.eql(b.value, "0") }, allocator); }, .e_function, @@ -2921,12 +3213,14 @@ pub const Expr = struct { return expr.at(E.Boolean{ .value = false }, allocator); }, // "!!!a" => "!a" - .e_unary => |un| { + .e_unary => { + const un = expr.getUnary(); if (un.op == Op.Code.un_not and isBoolean(un.value)) { return un.value; } }, - .e_binary => |ex| { + .e_binary => { + const ex = expr.getBinary(); // TODO: evaluate whether or not it is safe to do this mutation since it's modifying in-place. // Make sure that these transformations are all safe for special values. // For example, "!(a < b)" is not the same as "a >= b" if a and/or b are @@ -2974,50 +3268,385 @@ pub const Expr = struct { pub fn isOptionalChain(self: *const @This()) bool { return switch (self.data) { - .e_dot => |dot| dot.optional_chain != null, - .e_index => |dot| dot.optional_chain != null, - .e_call => |dot| dot.optional_chain != null, + .e_dot => self.getDot().optional_chain != null, + .e_index => self.getIndex().optional_chain != null, + .e_call => self.getCall().optional_chain != null, else => false, }; } pub const Data = union(Tag) { - e_array: *E.Array, - e_unary: *E.Unary, - e_binary: *E.Binary, - e_this: *E.This, - e_class: *E.Class, - e_boolean: *E.Boolean, - e_super: *E.Super, - e_null: *E.Null, - e_undefined: *E.Undefined, - e_new: *E.New, - e_new_target: *E.NewTarget, - e_function: *E.Function, - e_import_meta: *E.ImportMeta, - e_call: *E.Call, - e_dot: *E.Dot, - e_index: *E.Index, - e_arrow: *E.Arrow, - e_identifier: *E.Identifier, - e_import_identifier: *E.ImportIdentifier, - e_private_identifier: *E.PrivateIdentifier, - e_jsx_element: *E.JSXElement, + e_array: ListIndex, + e_unary: ListIndex, + e_binary: ListIndex, + + e_class: ListIndex, + e_boolean: ListIndex, + e_missing: E.Missing, - e_number: *E.Number, - e_big_int: *E.BigInt, - e_object: *E.Object, - e_spread: *E.Spread, - e_string: *E.String, - e_template_part: *E.TemplatePart, - e_template: *E.Template, - e_reg_exp: *E.RegExp, - e_await: *E.Await, - e_yield: *E.Yield, - e_if: *E.If, - e_require: *E.Require, - e_require_or_require_resolve: *E.RequireOrRequireResolve, - e_import: *E.Import, + e_this: E.This, + e_super: E.Super, + e_null: E.Null, + e_undefined: E.Undefined, + e_new_target: E.NewTarget, + + e_new: ListIndex, + + e_function: ListIndex, + e_import_meta: E.ImportMeta, + e_call: ListIndex, + e_dot: ListIndex, + e_index: ListIndex, + e_arrow: ListIndex, + e_identifier: ListIndex, + e_import_identifier: ListIndex, + e_private_identifier: ListIndex, + e_jsx_element: ListIndex, + + e_number: ListIndex, + e_big_int: ListIndex, + e_object: ListIndex, + e_spread: ListIndex, + e_string: ListIndex, + e_template_part: ListIndex, + e_template: ListIndex, + e_reg_exp: ListIndex, + e_await: ListIndex, + e_yield: ListIndex, + e_if: ListIndex, + e_require: ListIndex, + e_require_or_require_resolve: ListIndex, + e_import: ListIndex, + + pub const Store = struct { + pub const Array = NewStore(E.Array); + pub const Unary = NewStore(E.Unary); + pub const Binary = NewStore(E.Binary); + pub const This = NewStore(E.This); + pub const Class = NewStore(E.Class); + pub const Boolean = NewStore(E.Boolean); + pub const Super = NewStore(E.Super); + pub const Null = NewStore(E.Null); + pub const Undefined = NewStore(E.Undefined); + pub const New = NewStore(E.New); + pub const Function = NewStore(E.Function); + pub const ImportMeta = NewStore(E.ImportMeta); + pub const Call = NewStore(E.Call); + pub const Dot = NewStore(E.Dot); + pub const Index = NewStore(E.Index); + pub const Arrow = NewStore(E.Arrow); + pub const Identifier = NewStore(E.Identifier); + pub const ImportIdentifier = NewStore(E.ImportIdentifier); + pub const PrivateIdentifier = NewStore(E.PrivateIdentifier); + pub const JSXElement = NewStore(E.JSXElement); + pub const Missing = NewStore(E.Missing); + pub const Number = NewStore(E.Number); + pub const BigInt = NewStore(E.BigInt); + pub const Object = NewStore(E.Object); + pub const Spread = NewStore(E.Spread); + pub const String = NewStore(E.String); + pub const TemplatePart = NewStore(E.TemplatePart); + pub const Template = NewStore(E.Template); + pub const RegExp = NewStore(E.RegExp); + pub const Await = NewStore(E.Await); + pub const Yield = NewStore(E.Yield); + pub const If = NewStore(E.If); + pub const Require = NewStore(E.Require); + pub const RequireOrRequireResolve = NewStore(E.RequireOrRequireResolve); + pub const Import = NewStore(E.Import); + + threadlocal var has_inited = false; + pub fn create(allocator: *std.mem.Allocator) void { + if (has_inited) { + return; + } + + has_inited = true; + _ = Array.init(allocator); + _ = Unary.init(allocator); + _ = Binary.init(allocator); + _ = This.init(allocator); + _ = Class.init(allocator); + _ = Boolean.init(allocator); + _ = New.init(allocator); + _ = Function.init(allocator); + _ = ImportMeta.init(allocator); + _ = Call.init(allocator); + _ = Dot.init(allocator); + _ = Index.init(allocator); + _ = Arrow.init(allocator); + _ = Identifier.init(allocator); + _ = ImportIdentifier.init(allocator); + _ = PrivateIdentifier.init(allocator); + _ = JSXElement.init(allocator); + _ = Missing.init(allocator); + _ = Number.init(allocator); + _ = BigInt.init(allocator); + _ = Object.init(allocator); + _ = Spread.init(allocator); + _ = String.init(allocator); + _ = TemplatePart.init(allocator); + _ = Template.init(allocator); + _ = RegExp.init(allocator); + _ = Await.init(allocator); + _ = Yield.init(allocator); + _ = If.init(allocator); + _ = Require.init(allocator); + _ = RequireOrRequireResolve.init(allocator); + _ = Import.init(allocator); + } + + pub fn reset() void { + Array.reset(); + Unary.reset(); + Binary.reset(); + This.reset(); + Class.reset(); + Boolean.reset(); + New.reset(); + Function.reset(); + ImportMeta.reset(); + Call.reset(); + Dot.reset(); + Index.reset(); + Arrow.reset(); + Identifier.reset(); + ImportIdentifier.reset(); + PrivateIdentifier.reset(); + JSXElement.reset(); + Missing.reset(); + Number.reset(); + BigInt.reset(); + Object.reset(); + Spread.reset(); + String.reset(); + TemplatePart.reset(); + Template.reset(); + RegExp.reset(); + Await.reset(); + Yield.reset(); + If.reset(); + Require.reset(); + RequireOrRequireResolve.reset(); + Import.reset(); + } + + pub fn append(comptime ValueType: type, value: anytype) ListIndex { + switch (comptime ValueType) { + E.Array => { + return Array.append(value); + }, + E.Unary => { + return Unary.append(value); + }, + E.Binary => { + return Binary.append(value); + }, + E.This => { + return This.append(value); + }, + E.Class => { + return Class.append(value); + }, + E.Boolean => { + return Boolean.append(value); + }, + E.Super => { + return Super.append(value); + }, + E.Null => { + return Null.append(value); + }, + E.Undefined => { + return Undefined.append(value); + }, + E.New => { + return New.append(value); + }, + E.NewTarget => { + return @compileError("NewTarget bad"); + }, + E.Function => { + return Function.append(value); + }, + E.ImportMeta => { + return ImportMeta.append(value); + }, + E.Call => { + return Call.append(value); + }, + E.Dot => { + return Dot.append(value); + }, + E.Index => { + return Index.append(value); + }, + E.Arrow => { + return Arrow.append(value); + }, + E.Identifier => { + return Identifier.append(value); + }, + E.ImportIdentifier => { + return ImportIdentifier.append(value); + }, + E.PrivateIdentifier => { + return PrivateIdentifier.append(value); + }, + E.JSXElement => { + return JSXElement.append(value); + }, + E.Missing => { + return Missing.append(value); + }, + E.Number => { + return Number.append(value); + }, + E.BigInt => { + return BigInt.append(value); + }, + E.Object => { + return Object.append(value); + }, + E.Spread => { + return Spread.append(value); + }, + E.String => { + return String.append(value); + }, + E.TemplatePart => { + return TemplatePart.append(value); + }, + E.Template => { + return Template.append(value); + }, + E.RegExp => { + return RegExp.append(value); + }, + E.Await => { + return Await.append(value); + }, + E.Yield => { + return Yield.append(value); + }, + E.If => { + return If.append(value); + }, + E.Require => { + return Require.append(value); + }, + E.RequireOrRequireResolve => { + return RequireOrRequireResolve.append(value); + }, + E.Import => { + return Import.append(value); + }, + else => { + @compileError("Invalid type passed to Stmt.Data.set " ++ @typeName(ValueType)); + }, + } + } + + pub fn NewStore(comptime ValueType: type) type { + const count = 8096; + const max_index = count - 1; + const list_count = count; + return struct { + pub threadlocal var backing_buf: [count]ValueType = undefined; + pub threadlocal var backing_buf_used: u16 = 0; + const Allocator = std.mem.Allocator; + const Self = @This(); + const NotFound = allocators.NotFound; + const Unassigned = allocators.Unassigned; + + overflow_list: std.ArrayListUnmanaged(ValueType), + allocator: *Allocator, + + pub threadlocal var instance: Self = undefined; + pub threadlocal var self: *Self = undefined; + + pub fn reset() void { + backing_buf_used = 0; + self.overflow_list.shrinkRetainingCapacity(0); + } + + pub fn init(allocator: *std.mem.Allocator) *Self { + instance = Self{ + .allocator = allocator, + .overflow_list = std.ArrayListUnmanaged(ValueType){}, + }; + + self = &instance; + return self; + } + + pub fn isOverflowing() bool { + return backing_buf_used >= @as(u16, count); + } + + pub fn at(index: ListIndex) *ValueType { + std.debug.assert(index.index != NotFound.index and index.index != Unassigned.index); + + if (index.is_overflowing) { + return &self.overflow_list.items[index.index]; + } else { + return &backing_buf[index.index]; + } + } + + pub fn exists(value: ValueType) bool { + return isSliceInBuffer(value, backing_buf); + } + + pub fn append(value: ValueType) ListIndex { + var result = ListIndex{ .index = std.math.maxInt(u31), .is_overflowing = backing_buf_used > max_index }; + if (result.is_overflowing) { + result.index = @intCast(u31, self.overflow_list.items.len); + self.overflow_list.append(self.allocator, value) catch unreachable; + } else { + result.index = backing_buf_used; + backing_buf[result.index] = value; + backing_buf_used += 1; + if (backing_buf_used >= max_index) { + self.overflow_list = @TypeOf(self.overflow_list).initCapacity(self.allocator, count) catch unreachable; + } + } + + return result; + } + + pub fn update(result: *ListIndex, value: ValueType) !*ValueType { + if (result.index.index == NotFound.index or result.index.index == Unassigned.index) { + result.index.is_overflowing = backing_buf_used > max_index; + if (result.index.is_overflowing) { + result.index.index = @intCast(u31, self.overflow_list.items.len); + } else { + result.index.index = backing_buf_used; + backing_buf_used += 1; + if (backing_buf_used >= max_index) { + self.overflow_list = try @TypeOf(self.overflow_list).initCapacity(self.allocator, count); + } + } + } + + if (result.index.is_overflowing) { + if (self.overflow_list.items.len == result.index.index) { + const real_index = self.overflow_list.items.len; + try self.overflow_list.append(self.allocator, value); + } else { + self.overflow_list.items[result.index.index] = value; + } + + return &self.overflow_list.items[result.index.index]; + } else { + backing_buf[result.index.index] = value; + + return &backing_buf[result.index.index]; + } + } + }; + } + }; pub fn isBooleanValue(self: *Expr) bool { // TODO: 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); diff --git a/src/js_printer.zig b/src/js_printer.zig index a95d3e43a..de467a105 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -682,7 +682,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { pub fn isUnboundEvalIdentifier(p: *Printer, value: Expr) bool { switch (value.data) { - .e_identifier => |ident| { + .e_identifier => { + const ident = value.getIdentifier(); if (ident.ref.is_source_contents_slice) return false; const symbol = p.symbols.get(p.symbols.follow(ident.ref)) orelse return false; @@ -838,37 +839,41 @@ pub fn NewPrinter(comptime ascii_only: bool) type { defer debugl("</printExpr>"); switch (expr.data) { - .e_missing => |e| {}, - .e_undefined => |e| { + .e_missing => {}, + .e_undefined => { p.printSpaceBeforeIdentifier(); p.printUndefined(level); }, - .e_super => |e| { + .e_super => { p.printSpaceBeforeIdentifier(); p.print("super"); }, - .e_null => |e| { + .e_null => { p.printSpaceBeforeIdentifier(); p.print("null"); }, - .e_this => |e| { + .e_this => { p.printSpaceBeforeIdentifier(); p.print("this"); }, - .e_spread => |e| { + .e_spread => { + const e = expr.getSpread(); + p.print("..."); p.printExpr(e.value, .comma, ExprFlag.None()); }, - .e_new_target => |e| { + .e_new_target => { p.printSpaceBeforeIdentifier(); p.print("new.target"); }, - .e_import_meta => |e| { + .e_import_meta => { p.printSpaceBeforeIdentifier(); p.print("import.meta"); }, - .e_new => |e| { + .e_new => { + const e = expr.getNew(); + const has_pure_comment = e.can_be_unwrapped_if_unused; const wrap = level.gte(.call) or (has_pure_comment and level.gte(.postfix)); @@ -908,7 +913,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_call => |e| { + .e_call => { + const e = expr.getCall(); + var wrap = level.gte(.new) or flags.forbid_call; var target_flags = ExprFlag.None(); if (e.optional_chain == null) { @@ -964,10 +971,14 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_require => |e| { + .e_require => { + const e = expr.getRequire(); + p.printRequireOrImportExpr(e.import_record_index, &([_]G.Comment{}), level, flags); }, - .e_require_or_require_resolve => |e| { + .e_require_or_require_resolve => { + const e = expr.getRequireOrRequireResolve(); + const wrap = level.gte(.new) or flags.forbid_call; if (wrap) { p.print("("); @@ -992,7 +1003,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_import => |e| { + .e_import => { + const e = expr.getImport(); + // Handle non-string expressions if (Ref.isSourceIndexNull(e.import_record_index)) { const wrap = level.gte(.new) or flags.forbid_call; @@ -1024,7 +1037,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printRequireOrImportExpr(e.import_record_index, e.leading_interior_comments, level, flags); } }, - .e_dot => |e| { + .e_dot => { + const e = expr.getDot(); + var wrap = false; if (e.optional_chain == null) { flags.has_non_optional_chain_parent = false; @@ -1063,7 +1078,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_index => |e| { + .e_index => { + const e = expr.getIndex(); + var wrap = false; if (e.optional_chain == null) { flags.has_non_optional_chain_parent = false; @@ -1086,7 +1103,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } switch (e.index.data) { - .e_private_identifier => |priv| { + .e_private_identifier => { + const priv = e.index.getPrivateIdentifier(); if (is_optional_chain_start) { p.print("."); } @@ -1104,7 +1122,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_if => |e| { + .e_if => { + const e = expr.getIf(); + const wrap = level.gte(.conditional); if (wrap) { p.print("("); @@ -1123,7 +1143,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_arrow => |e| { + .e_arrow => { + const e = expr.getArrow(); + const wrap = level.gte(.assign); if (wrap) { @@ -1163,7 +1185,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_function => |e| { + .e_function => { + const e = expr.getFunction(); + const n = p.js.lenI(); var wrap = p.stmt_start == n or p.export_default_start == n; @@ -1190,7 +1214,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_class => |e| { + .e_class => { + const e = expr.getClass(); + const n = p.js.lenI(); var wrap = p.stmt_start == n or p.export_default_start == n; if (wrap) { @@ -1208,7 +1234,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_array => |e| { + .e_array => { + const e = expr.getArray(); + p.print("["); if (e.items.len > 0) { if (!e.is_single_line) { @@ -1249,7 +1277,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print("]"); }, - .e_object => |e| { + .e_object => { + const e = expr.getObject(); + const n = p.js.lenI(); const wrap = p.stmt_start == n or p.arrow_expr_start == n; @@ -1288,11 +1318,15 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_boolean => |e| { + .e_boolean => { + const e = expr.getBoolean(); + p.printSpaceBeforeIdentifier(); p.print(if (e.value) "true" else "false"); }, - .e_string => |e| { + .e_string => { + const e = expr.getString(); + // If this was originally a template literal, print it as one as long as we're not minifying if (e.prefer_template) { p.print("`"); @@ -1306,7 +1340,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printString(e.*, c); p.print(c); }, - .e_template => |e| { + .e_template => { + const e = expr.getTemplate(); + if (e.tag) |tag| { // Optional chains are forbidden in template tags if (expr.isOptionalChain()) { @@ -1341,7 +1377,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } p.print("`"); }, - .e_reg_exp => |e| { + .e_reg_exp => { + const e = expr.getRegExp(); + const n = p.js.len(); // Avoid forming a single-line comment @@ -1354,12 +1392,16 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // Need a space before the next identifier to avoid it turning into flags p.prev_reg_exp_end = p.js.lenI(); }, - .e_big_int => |e| { + .e_big_int => { + const e = expr.getBigInt(); + p.printSpaceBeforeIdentifier(); p.print(e.value); p.print('n'); }, - .e_number => |e| { + .e_number => { + const e = expr.getNumber(); + const value = e.value; const absValue = std.math.fabs(value); @@ -1399,7 +1441,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.prev_num_end = p.js.lenI(); } }, - .e_identifier => |e| { + .e_identifier => { + const e = expr.getIdentifier(); + const name = p.renamer.nameForSymbol(e.ref); const wrap = p.js.lenI() == p.for_of_init_start and strings.eqlComptime(name, "let"); @@ -1414,7 +1458,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_import_identifier => |e| { + .e_import_identifier => { + const e = expr.getImportIdentifier(); + // Potentially use a property access instead of an identifier const ref = p.symbols.follow(e.ref); var didPrint = false; @@ -1427,7 +1473,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { var wrap = false; if (p.call_target) |target| { - wrap = e.was_originally_identifier and target.e_import_identifier == e; + wrap = e.was_originally_identifier and target.e_import_identifier.index == expr.data.e_import_identifier.index; } if (wrap) { @@ -1456,7 +1502,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.printSymbol(e.ref); } }, - .e_await => |e| { + .e_await => { + const e = expr.getAwait(); + const wrap = level.gte(.prefix); if (wrap) { @@ -1472,7 +1520,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_yield => |e| { + .e_yield => { + const e = expr.getYield(); + const wrap = level.gte(.assign); if (wrap) { p.print("("); @@ -1493,7 +1543,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_unary => |e| { + .e_unary => { + const e = expr.getUnary(); + const entry: Op = Op.Table.get(e.op); const wrap = level.gte(entry.level); @@ -1524,7 +1576,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { p.print(")"); } }, - .e_binary => |e| { + .e_binary => { + const e = expr.getBinary(); + const entry: Op = Op.Table.get(e.op); var wrap = level.gte(entry.level) or (e.op == Op.Code.bin_in and flags.forbid_in); @@ -1559,7 +1613,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // "??" can't directly contain "||" or "&&" without being wrapped in parentheses .bin_nullish_coalescing => { switch (e.left.data) { - .e_binary => |left| { + .e_binary => { + const left = e.left.getBinary(); switch (left.op) { .bin_logical_and, .bin_logical_or => { left_level = .prefix; @@ -1571,7 +1626,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } switch (e.right.data) { - .e_binary => |right| { + .e_binary => { + const right = e.right.getBinary(); switch (right.op) { .bin_logical_and, .bin_logical_or => { right_level = .prefix; @@ -1585,7 +1641,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // "**" can't contain certain unary expressions .bin_pow => { switch (e.left.data) { - .e_unary => |left| { + .e_unary => { + const left = e.left.getUnary(); if (left.op.unaryAssignTarget() == .none) { left_level = .call; } @@ -1601,7 +1658,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // Special-case "#foo in bar" if (e.op == .bin_in and @as(Expr.Tag, e.left.data) == .e_private_identifier) { - p.printSymbol(e.left.data.e_private_identifier.ref); + p.printSymbol(e.left.getPrivateIdentifier().ref); } else { flags.forbid_in = true; p.printExpr(e.left, left_level, flags); @@ -1701,7 +1758,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (item.value) |val| { switch (val.data) { - .e_function => |func| { + .e_function => { + const func = val.getFunction(); if (item.flags.is_method) { if (func.func.flags.is_async) { p.printSpaceBeforeIdentifier(); @@ -1728,7 +1786,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (item.value) |val| { switch (val.data) { - .e_function => |func| { + .e_function => { + const func = val.getFunction(); if (item.flags.is_method) { p.printFunc(func.func); return; @@ -1749,10 +1808,11 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } switch (_key.data) { - .e_private_identifier => |key| { - p.printSymbol(key.ref); + .e_private_identifier => { + p.printSymbol(_key.getPrivateIdentifier().ref); }, - .e_string => |key| { + .e_string => { + const key = _key.getString(); p.addSourceMapping(_key.loc); if (key.isUTF8()) { p.printSpaceBeforeIdentifier(); @@ -1761,7 +1821,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // Use a shorthand property if the names are the same if (item.value) |val| { switch (val.data) { - .e_identifier => |e| { + .e_identifier => { + const e = val.getIdentifier(); + // TODO: is needing to check item.flags.was_shorthand a bug? // esbuild doesn't have to do that... // maybe it's a symptom of some other underlying issue @@ -1774,7 +1836,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } // if (strings) {} }, - .e_import_identifier => |e| { + .e_import_identifier => { + const e = val.getImportIdentifier(); + const ref = p.symbols.follow(e.ref); if (p.symbols.get(ref)) |symbol| { if (symbol.namespace_alias == null and strings.eql(key.utf8, p.renamer.nameForSymbol(e.ref))) { @@ -1795,7 +1859,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { // Use a shorthand property if the names are the same if (item.value) |val| { switch (val.data) { - .e_identifier => |e| { + .e_identifier => { + const e = val.getIdentifier(); + // TODO: is needing to check item.flags.was_shorthand a bug? // esbuild doesn't have to do that... // maybe it's a symptom of some other underlying issue @@ -1808,7 +1874,9 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } // if (strings) {} }, - .e_import_identifier => |e| { + .e_import_identifier => { + const e = val.getImportIdentifier(); + const ref = p.symbols.follow(e.ref); if (p.symbols.get(ref)) |symbol| { if (symbol.namespace_alias == null and strings.utf16EqlString(key.value, p.renamer.nameForSymbol(e.ref))) { @@ -1843,8 +1911,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (item.kind != .normal) { switch (item.value.?.data) { - .e_function => |func| { - p.printFunc(func.func); + .e_function => { + p.printFunc(item.value.?.getFunction().func); return; }, else => {}, @@ -1853,7 +1921,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { if (item.value) |val| { switch (val.data) { - .e_function => |f| { + .e_function => { + const f = val.getFunction(); if (item.flags.is_method) { p.printFunc(f.func); @@ -1979,7 +2048,8 @@ pub fn NewPrinter(comptime ascii_only: bool) type { } switch (property.key.data) { - .e_string => |str| { + .e_string => { + const str = property.key.getString(); if (str.isUTF8()) { p.addSourceMapping(property.key.loc); p.printSpaceBeforeIdentifier(); diff --git a/src/resolver/package_json.zig b/src/resolver/package_json.zig index 1594de702..a9952c8b1 100644 --- a/src/resolver/package_json.zig +++ b/src/resolver/package_json.zig @@ -79,8 +79,8 @@ pub const PackageJSON = struct { .main_fields = MainFieldMap.init(r.allocator), }; - if (json.getProperty("type")) |type_json| { - if (type_json.expr.getString(r.allocator)) |type_str| { + if (json.asProperty("type")) |type_json| { + if (type_json.expr.asString(r.allocator)) |type_str| { switch (options.ModuleType.List.get(type_str) orelse options.ModuleType.unknown) { .cjs => { package_json.module_type = .cjs; @@ -105,10 +105,10 @@ pub const PackageJSON = struct { // Read the "main" fields for (r.opts.main_fields) |main| { - if (json.getProperty(main)) |main_json| { + if (json.asProperty(main)) |main_json| { const expr: js_ast.Expr = main_json.expr; - if ((expr.getString(r.allocator))) |str| { + if ((expr.asString(r.allocator))) |str| { if (str.len > 0) { package_json.main_fields.put(main, str) catch unreachable; } @@ -129,14 +129,15 @@ pub const PackageJSON = struct { // "./dist/index.node.esm.js": "./dist/index.browser.esm.js" // }, // - if (json.getProperty("browser")) |browser_prop| { + if (json.asProperty("browser")) |browser_prop| { switch (browser_prop.expr.data) { - .e_object => |obj| { + .e_object => { + const obj = browser_prop.expr.getObject(); // The value is an object // Remap all files in the browser field for (obj.properties) |prop| { - var _key_str = (prop.key orelse continue).getString(r.allocator) orelse continue; + var _key_str = (prop.key orelse continue).asString(r.allocator) orelse continue; const value: js_ast.Expr = prop.value orelse continue; // Normalize the path so we can compare against it without getting @@ -151,11 +152,13 @@ pub const PackageJSON = struct { const key = r.allocator.dupe(u8, r.fs.normalize(_key_str)) catch unreachable; switch (value.data) { - .e_string => |str| { + .e_string => { + const str = value.getString(); // If this is a string, it's a replacement package package_json.browser_map.put(key, str.string(r.allocator) catch unreachable) catch unreachable; }, - .e_boolean => |boolean| { + .e_boolean => { + const boolean = value.getBoolean(); if (!boolean.value) { package_json.browser_map.put(key, "") catch unreachable; } diff --git a/src/resolver/tsconfig_json.zig b/src/resolver/tsconfig_json.zig index 79fb6e677..7b6977bba 100644 --- a/src/resolver/tsconfig_json.zig +++ b/src/resolver/tsconfig_json.zig @@ -74,9 +74,9 @@ pub const TSConfigJSON = struct { var result: TSConfigJSON = TSConfigJSON{ .abs_path = source.key_path.text, .paths = PathsMap.init(allocator) }; errdefer allocator.free(result.paths); - if (json.getProperty("extends")) |extends_value| { + if (json.asProperty("extends")) |extends_value| { log.addWarning(&source, extends_value.loc, "\"extends\" is not implemented yet") catch unreachable; - // if ((extends_value.expr.getString(allocator) catch null)) |str| { + // if ((extends_value.expr.asString(allocator) catch null)) |str| { // if (extends(str, source.rangeOfString(extends_value.loc))) |base| { // result.jsx = base.jsx; // result.base_url_for_paths = base.base_url_for_paths; @@ -90,48 +90,48 @@ pub const TSConfigJSON = struct { var has_base_url = false; // Parse "compilerOptions" - if (json.getProperty("compilerOptions")) |compiler_opts| { + if (json.asProperty("compilerOptions")) |compiler_opts| { // Parse "baseUrl" - if (compiler_opts.expr.getProperty("baseUrl")) |base_url_prop| { - if ((base_url_prop.expr.getString(allocator))) |base_url| { + if (compiler_opts.expr.asProperty("baseUrl")) |base_url_prop| { + if ((base_url_prop.expr.asString(allocator))) |base_url| { result.base_url = base_url; has_base_url = true; } } // Parse "jsxFactory" - if (compiler_opts.expr.getProperty("jsxFactory")) |jsx_prop| { - if (jsx_prop.expr.getString(allocator)) |str| { + if (compiler_opts.expr.asProperty("jsxFactory")) |jsx_prop| { + if (jsx_prop.expr.asString(allocator)) |str| { result.jsx.factory = try parseMemberExpressionForJSX(log, &source, jsx_prop.loc, str, allocator); } } // Parse "jsxFragmentFactory" - if (compiler_opts.expr.getProperty("jsxFactory")) |jsx_prop| { - if (jsx_prop.expr.getString(allocator)) |str| { + if (compiler_opts.expr.asProperty("jsxFactory")) |jsx_prop| { + if (jsx_prop.expr.asString(allocator)) |str| { result.jsx.fragment = try parseMemberExpressionForJSX(log, &source, jsx_prop.loc, str, allocator); } } // Parse "jsxImportSource" - if (compiler_opts.expr.getProperty("jsxImportSource")) |jsx_prop| { - if (jsx_prop.expr.getString(allocator)) |str| { + if (compiler_opts.expr.asProperty("jsxImportSource")) |jsx_prop| { + if (jsx_prop.expr.asString(allocator)) |str| { result.jsx.import_source = str; } } // Parse "useDefineForClassFields" - if (compiler_opts.expr.getProperty("useDefineForClassFields")) |use_define_value_prop| { - if (use_define_value_prop.expr.getBool()) |val| { + if (compiler_opts.expr.asProperty("useDefineForClassFields")) |use_define_value_prop| { + if (use_define_value_prop.expr.asBool()) |val| { result.use_define_for_class_fields = val; } } // Parse "importsNotUsedAsValues" - if (compiler_opts.expr.getProperty("importsNotUsedAsValues")) |jsx_prop| { + if (compiler_opts.expr.asProperty("importsNotUsedAsValues")) |jsx_prop| { // This should never allocate since it will be utf8 - if ((jsx_prop.expr.getString(allocator))) |str| { + if ((jsx_prop.expr.asString(allocator))) |str| { switch (ImportsNotUsedAsValue.List.get(str) orelse ImportsNotUsedAsValue.invalid) { .preserve, .err => { result.preserve_imports_not_used_as_values = true; @@ -145,14 +145,15 @@ pub const TSConfigJSON = struct { } // Parse "paths" - if (compiler_opts.expr.getProperty("paths")) |paths_prop| { + if (compiler_opts.expr.asProperty("paths")) |paths_prop| { switch (paths_prop.expr.data) { - .e_object => |paths| { + .e_object => { + var paths = paths_prop.expr.getObject(); result.base_url_for_paths = result.base_url; result.paths = PathsMap.init(allocator); for (paths.properties) |property| { const key_prop = property.key orelse continue; - const key = (key_prop.getString(allocator)) orelse continue; + const key = (key_prop.asString(allocator)) orelse continue; if (!TSConfigJSON.isValidTSConfigPathNoBaseURLPattern(key, log, &source, allocator, key_prop.loc)) { continue; @@ -182,13 +183,15 @@ pub const TSConfigJSON = struct { // Matching "folder1/file2" should first check "projectRoot/folder1/file2" // and then, if that didn't work, also check "projectRoot/generated/folder1/file2". switch (value_prop.data) { - .e_array => |array| { + .e_array => { + const array = value_prop.getArray(); + if (array.items.len > 0) { var values = allocator.alloc(string, array.items.len) catch unreachable; errdefer allocator.free(values); var count: usize = 0; for (array.items) |expr| { - if ((expr.getString(allocator))) |str| { + if ((expr.asString(allocator))) |str| { if (TSConfigJSON.isValidTSConfigPathPattern( str, log, |