aboutsummaryrefslogtreecommitdiff
path: root/src/js_ast.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/js_ast.zig')
-rw-r--r--src/js_ast.zig448
1 files changed, 52 insertions, 396 deletions
diff --git a/src/js_ast.zig b/src/js_ast.zig
index 774c8247f..d1c136847 100644
--- a/src/js_ast.zig
+++ b/src/js_ast.zig
@@ -24,6 +24,7 @@ pub fn NewBaseStore(comptime Union: anytype, comptime count: usize) type {
return struct {
const Allocator = std.mem.Allocator;
const Self = @This();
+
const Block = struct {
items: [count]UnionValueType align(max_align) = undefined,
used: usize = 0,
@@ -288,7 +289,10 @@ pub const Binding = struct {
b_missing,
};
+ pub var icount: usize = 0;
+
pub fn init(t: anytype, loc: logger.Loc) Binding {
+ icount += 1;
switch (@TypeOf(t)) {
*B.Identifier => {
return Binding{ .loc = loc, .data = B{ .b_identifier = t } };
@@ -312,6 +316,7 @@ pub const Binding = struct {
}
pub fn alloc(allocator: *std.mem.Allocator, t: anytype, loc: logger.Loc) Binding {
+ icount += 1;
switch (@TypeOf(t)) {
B.Identifier => {
var data = allocator.create(B.Identifier) catch unreachable;
@@ -334,9 +339,7 @@ pub const Binding = struct {
return Binding{ .loc = loc, .data = B{ .b_object = data } };
},
B.Missing => {
- var data = allocator.create(B.Missing) catch unreachable;
- data.* = t;
- return Binding{ .loc = loc, .data = B{ .b_missing = data } };
+ return Binding{ .loc = loc, .data = B{ .b_missing = .{} } };
},
else => {
@compileError("Invalid type passed to Binding.alloc");
@@ -350,7 +353,7 @@ pub const B = union(Binding.Tag) {
b_array: *B.Array,
b_property: *B.Property,
b_object: *B.Object,
- b_missing: *B.Missing,
+ b_missing: B.Missing,
pub const Identifier = struct {
ref: Ref,
@@ -1843,10 +1846,10 @@ pub const Expr = struct {
pub fn getMissing(exp: *const Expr) *E.Missing {
return exp.data.e_missing;
}
- pub fn getNumber(exp: *const Expr) *E.Number {
+ pub fn getNumber(exp: *const Expr) E.Number {
return exp.data.e_number;
}
- pub fn getBigInt(exp: *const Expr) *E.BigInt {
+ pub fn getBigInt(exp: *const Expr) E.BigInt {
return exp.data.e_big_int;
}
pub fn getObject(exp: *const Expr) *E.Object {
@@ -1855,7 +1858,7 @@ pub const Expr = struct {
pub fn getSpread(exp: *const Expr) *E.Spread {
return exp.data.e_spread;
}
- pub fn getString(exp: *const Expr) *E.String {
+ pub fn getString(exp: *const Expr) E.String {
return exp.data.e_string;
}
pub fn getTemplatePart(exp: *const Expr) *E.TemplatePart {
@@ -1890,11 +1893,11 @@ pub const Expr = struct {
if (std.meta.activeTag(expr.data) != .e_object) return null;
const obj = expr.getObject();
- for (obj.properties) |prop| {
+ 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.getString();
+ const key_str = key.data.e_string;
if (key_str.eql(string, name)) {
return Query{ .expr = value, .loc = key.loc };
}
@@ -1906,7 +1909,7 @@ pub const Expr = struct {
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.getString();
+ const key_str = expr.data.e_string;
return if (key_str.isUTF8()) key_str.utf8 else key_str.string(allocator) catch null;
}
@@ -1916,9 +1919,7 @@ pub const Expr = struct {
) ?bool {
if (std.meta.activeTag(expr.data) != .e_boolean) return null;
- const obj = expr.getBoolean();
-
- return obj.value;
+ return expr.data.e_boolean.value;
}
pub const EFlags = enum { none, ts_decorator };
@@ -1997,6 +1998,12 @@ pub const Expr = struct {
}
pub var icount: usize = 0;
+
+ // We don't need to dynamically allocate booleans
+ var true_bool = E.Boolean{ .value = true };
+ var false_bool = E.Boolean{ .value = false };
+ var bool_values = [_]*E.Boolean{ &false_bool, &true_bool };
+
pub fn init(exp: anytype, loc: logger.Loc) Expr {
icount += 1;
const st = exp.*;
@@ -2045,7 +2052,7 @@ pub const Expr = struct {
return Expr{
.loc = loc,
.data = Data{
- .e_boolean = Data.Store.All.append(@TypeOf(st), st),
+ .e_boolean = bool_values[@boolToInt(st.value)],
},
};
},
@@ -2101,7 +2108,7 @@ pub const Expr = struct {
return Expr{
.loc = loc,
.data = Data{
- .e_import_meta = Data.Store.All.append(@TypeOf(st), st),
+ .e_import_meta = st,
},
};
},
@@ -2284,304 +2291,26 @@ pub const Expr = struct {
},
};
},
-
- else => {
- @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 => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_array = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Class => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_class = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Unary => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_unary = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Binary => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_binary = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.This => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_this = st,
- },
- };
- },
- E.Boolean => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_boolean = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Super => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_super = st,
- },
- };
- },
- E.Null => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_null = st,
- },
- };
- },
- E.Undefined => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_undefined = st,
- },
- };
- },
- E.New => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_new = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.NewTarget => {
- return Expr{
- .loc = loc,
- .data = Data{ .e_new_target = st },
- };
- },
- E.Function => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_function = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.ImportMeta => {
+ *E.String => {
return Expr{
.loc = loc,
.data = Data{
- .e_import_meta = st,
- },
- };
- },
- E.Call => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_call = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Dot => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_dot = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Index => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_index = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Arrow => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_arrow = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Identifier => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_identifier = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.ImportIdentifier => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_import_identifier = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.PrivateIdentifier => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_private_identifier = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.JSXElement => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_jsx_element = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Missing => {
- return Expr{ .loc = loc, .data = Data{ .e_missing = E.Missing{} } };
- },
- E.Number => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_number = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.BigInt => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_big_int = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Object => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_object = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Spread => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_spread = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.String => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_string = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.TemplatePart => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_template_part = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Template => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_template = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.RegExp => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_reg_exp = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Await => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_await = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Yield => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_yield = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.If => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_if = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.RequireOrRequireResolve => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_require_or_require_resolve = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Import => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_import = Data.Store.All.append(@TypeOf(st), st),
- },
- };
- },
- E.Require => {
- return Expr{
- .loc = loc,
- .data = Data{
- .e_require = Data.Store.All.append(@TypeOf(st), st),
+ .e_string = st,
},
};
},
else => {
- @compileError("Invalid type passed to Expr.init");
+ @compileError("Invalid type passed to Expr.init: " ++ @typeName(@TypeOf(st)));
},
}
}
+ pub fn alloc(allocator: *std.mem.Allocator, st: anytype, loc: logger.Loc) Expr {
+ icount += 1;
+ return init(&st, loc);
+ }
+
pub const Tag = enum(u6) {
e_array,
e_unary,
@@ -2978,16 +2707,13 @@ pub const Expr = struct {
return true;
},
- .e_if => {
- const ex = a.getIf();
+ .e_if => |ex| {
return isBoolean(ex.yes) and isBoolean(ex.no);
},
- .e_unary => {
- const ex = a.getUnary();
+ .e_unary => |ex| {
return ex.op == .un_not or ex.op == .un_delete;
},
- .e_binary => {
- const ex = a.getBinary();
+ .e_binary => |ex| {
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;
@@ -3036,17 +2762,14 @@ pub const Expr = struct {
.e_null, .e_undefined => {
return expr.at(E.Boolean{ .value = true }, allocator);
},
- .e_boolean => {
- const b = expr.getBoolean();
+ .e_boolean => |b| {
return expr.at(E.Boolean{ .value = b.value }, allocator);
},
- .e_number => {
- const n = expr.getNumber();
+ .e_number => |n| {
return expr.at(E.Boolean{ .value = (n.value == 0 or std.math.isNan(n.value)) }, allocator);
},
- .e_big_int => {
- const b = expr.getBigInt();
- return expr.at(E.Boolean{ .value = strings.eql(b.value, "0") }, allocator);
+ .e_big_int => |b| {
+ return expr.at(E.Boolean{ .value = strings.eqlComptime(b.value, "0") }, allocator);
},
.e_function,
.e_arrow,
@@ -3055,14 +2778,12 @@ pub const Expr = struct {
return expr.at(E.Boolean{ .value = false }, allocator);
},
// "!!!a" => "!a"
- .e_unary => {
- const un = expr.getUnary();
+ .e_unary => |un| {
if (un.op == Op.Code.un_not and isBoolean(un.value)) {
return un.value;
}
},
- .e_binary => {
- const ex = expr.getBinary();
+ .e_binary => |ex| {
// 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
@@ -3122,7 +2843,7 @@ pub const Expr = struct {
e_unary: *E.Unary,
e_binary: *E.Binary,
e_class: *E.Class,
- e_boolean: *E.Boolean,
+
e_new: *E.New,
e_function: *E.Function,
e_call: *E.Call,
@@ -3133,11 +2854,10 @@ pub const Expr = struct {
e_import_identifier: *E.ImportIdentifier,
e_private_identifier: *E.PrivateIdentifier,
e_jsx_element: *E.JSXElement,
- 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,
@@ -3148,6 +2868,11 @@ pub const Expr = struct {
e_require_or_require_resolve: *E.RequireOrRequireResolve,
e_import: *E.Import,
+ e_boolean: *E.Boolean,
+ e_number: *E.Number,
+ e_big_int: *E.BigInt,
+ e_string: *E.String,
+
e_missing: E.Missing,
e_this: E.This,
e_super: E.Super,
@@ -3702,11 +3427,14 @@ pub const Ast = struct {
// These are used when bundling. They are filled in during the parser pass
// since we already have to traverse the AST then anyway and the parser pass
// is conveniently fully parallelized.
- named_imports: AutoHashMap(Ref, NamedImport) = undefined,
- named_exports: StringHashMap(NamedExport) = undefined,
+ named_imports: NamedImports = undefined,
+ named_exports: NamedExports = undefined,
top_level_symbol_to_parts: AutoHashMap(Ref, std.ArrayList(u32)) = undefined,
export_star_import_records: []u32 = &([_]u32{}),
+ pub const NamedImports = std.ArrayHashMap(Ref, NamedImport, Ref.hash, Ref.eql, true);
+ pub const NamedExports = StringHashMap(NamedExport);
+
pub fn initTest(parts: []Part) Ast {
return Ast{
.parts = parts,
@@ -3770,78 +3498,6 @@ pub const Dependency = packed struct {
pub const ExprList = std.ArrayList(Expr);
pub const StmtList = std.ArrayList(Stmt);
pub const BindingList = std.ArrayList(Binding);
-pub const AstData = struct {
- expr_list: ExprList,
- stmt_list: StmtList,
- binding_list: BindingList,
-
- pub fn init(allocator: *std.mem.Allocator) AstData {
- return AstData{
- .expr_list = ExprList.init(allocator),
- .stmt_list = StmtList.init(allocator),
- .binding_list = BindingList.init(allocator),
- };
- }
-
- pub fn deinit(self: *AstData) void {
- self.expr_list.deinit();
- self.stmt_list.deinit();
- self.binding_list.deinit();
- }
-
- pub fn expr(self: *AstData, index: ExprNodeIndex) Expr {
- return self.expr_list.items[index];
- }
-
- pub fn stmt(self: *AstData, index: StmtNodeIndex) Stmt {
- return self.stmt_list.items[index];
- }
-
- pub fn binding(self: *AstData, index: BindingNodeIndex) Binding {
- return self.binding_list.items[index];
- }
-
- pub fn add_(self: *AstData, t: anytype) !void {
- return switch (@TypeOf(t)) {
- Stmt => {
- try self.stmt_list.append(t);
- },
- Expr => {
- try self.expr_list.append(t);
- },
- Binding => {
- try self.binding_list.append(t);
- },
- else => {
- @compileError("Invalid type passed to AstData.add. Expected Stmt, Expr, or Binding.");
- },
- };
- }
-
- pub fn add(self: *AstData, t: anytype) !NodeIndex {
- return &t;
- // return switch (@TypeOf(t)) {
- // Stmt => {
- // var len = self.stmt_list.items.len;
- // try self.stmt_list.append(t);
- // return @intCast(StmtNodeIndex, len);
- // },
- // Expr => {
- // var len = self.expr_list.items.len;
- // try self.expr_list.append(t);
- // return @intCast(ExprNodeIndex, len);
- // },
- // Binding => {
- // var len = self.binding_list.items.len;
- // try self.binding_list.append(t);
- // return @intCast(BindingNodeIndex, len);
- // },
- // else => {
- // @compileError("Invalid type passed to AstData.add. Expected Stmt, Expr, or Binding.");
- // },
- // };
- }
-};
// Each file is made up of multiple parts, and each part consists of one or
// more top-level statements. Parts are used for tree shaking and code