mirror of
https://github.com/oven-sh/bun
synced 2026-02-15 13:22:07 +00:00
Assorted bugfixes but the next step really is porting tests and fixing
This commit is contained in:
@@ -44,6 +44,10 @@ pub const Ref = packed struct {
|
||||
pub fn eql(ref: Ref, b: Ref) bool {
|
||||
return ref.inner_index == b.inner_index and ref.source_index == b.source_index;
|
||||
}
|
||||
|
||||
pub fn jsonStringify(self: *const Ref, options: anytype, writer: anytype) !void {
|
||||
return try std.json.stringify([2]u32{ self.source_index, self.inner_index }, options, writer);
|
||||
}
|
||||
};
|
||||
|
||||
// This is kind of the wrong place, but it's shared between files
|
||||
|
||||
123
src/js_ast.zig
123
src/js_ast.zig
@@ -93,6 +93,17 @@ pub const Binding = struct {
|
||||
loc: logger.Loc,
|
||||
data: B,
|
||||
|
||||
const Serializable = struct {
|
||||
@"type": Tag,
|
||||
object: string,
|
||||
value: B,
|
||||
loc: logger.Loc,
|
||||
};
|
||||
|
||||
pub fn jsonStringify(self: *const @This(), options: anytype, writer: anytype) !void {
|
||||
return try std.json.stringify(Serializable{ .@"type" = std.meta.activeTag(self.data), .object = "binding", .value = self.data, .loc = self.loc }, options, writer);
|
||||
}
|
||||
|
||||
pub fn ToExpr(comptime expr_type: type, comptime func_type: anytype) type {
|
||||
const ExprType = expr_type;
|
||||
return struct {
|
||||
@@ -801,12 +812,25 @@ pub const E = struct {
|
||||
children: ExprNodeList,
|
||||
};
|
||||
|
||||
pub const Missing = struct {};
|
||||
pub const Missing = struct {
|
||||
pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void {
|
||||
return try std.json.stringify(null, opts, o);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Number = struct { value: f64 };
|
||||
pub const Number = struct {
|
||||
value: f64,
|
||||
pub fn jsonStringify(self: *const Number, opts: anytype, o: anytype) !void {
|
||||
return try std.json.stringify(self.value, opts, o);
|
||||
}
|
||||
};
|
||||
|
||||
pub const BigInt = struct {
|
||||
value: string,
|
||||
|
||||
pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void {
|
||||
return try std.json.stringify(self.value, opts, o);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Object = struct {
|
||||
@@ -826,6 +850,20 @@ pub const E = struct {
|
||||
pub fn string(s: *String, allocator: *std.mem.Allocator) !string {
|
||||
return try std.unicode.utf16leToUtf8Alloc(allocator, s.value);
|
||||
}
|
||||
|
||||
pub fn jsonStringify(s: *const String, options: anytype, writer: anytype) !void {
|
||||
var buf = [_]u8{0} ** 4096;
|
||||
var i: usize = 0;
|
||||
for (s.value) |char| {
|
||||
buf[i] = @intCast(u8, char);
|
||||
i += 1;
|
||||
if (i >= 4096) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return try std.json.stringify(buf[0..i], options, writer);
|
||||
}
|
||||
};
|
||||
|
||||
// value is in the Node
|
||||
@@ -846,11 +884,17 @@ pub const E = struct {
|
||||
|
||||
pub const RegExp = struct {
|
||||
value: string,
|
||||
|
||||
pub fn jsonStringify(self: *const RegExp, opts: anytype, o: anytype) !void {
|
||||
return try std.json.stringify(self.value, opts, o);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Class = G.Class;
|
||||
|
||||
pub const Await = struct { value: ExprNodeIndex };
|
||||
pub const Await = struct {
|
||||
value: ExprNodeIndex,
|
||||
};
|
||||
|
||||
pub const Yield = struct {
|
||||
value: ?ExprNodeIndex = null,
|
||||
@@ -891,6 +935,17 @@ pub const Stmt = struct {
|
||||
loc: logger.Loc,
|
||||
data: Data,
|
||||
|
||||
const Serializable = struct {
|
||||
@"type": Tag,
|
||||
object: string,
|
||||
value: Data,
|
||||
loc: logger.Loc,
|
||||
};
|
||||
|
||||
pub fn jsonStringify(self: *const Stmt, options: anytype, writer: anytype) !void {
|
||||
return try std.json.stringify(Serializable{ .@"type" = std.meta.activeTag(self.data), .object = "stmt", .value = self.data, .loc = self.loc }, options, writer);
|
||||
}
|
||||
|
||||
pub fn isTypeScript(self: *Stmt) bool {
|
||||
return @as(Stmt.Tag, self.data) == .s_type_script;
|
||||
}
|
||||
@@ -1223,6 +1278,54 @@ pub const Expr = struct {
|
||||
|
||||
pub const EFlags = enum { none, ts_decorator };
|
||||
|
||||
const Serializable = struct {
|
||||
@"type": Tag,
|
||||
object: string,
|
||||
value: Data,
|
||||
loc: logger.Loc,
|
||||
};
|
||||
|
||||
pub fn isMissing(a: *const Expr) bool {
|
||||
return std.meta.activeTag(a.data) == Expr.Tag.e_missing;
|
||||
}
|
||||
|
||||
pub fn joinWithComma(a: Expr, b: Expr, allocator: *std.mem.Allocator) Expr {
|
||||
if (a.isMissing()) {
|
||||
return b;
|
||||
}
|
||||
|
||||
if (b.isMissing()) {
|
||||
return a;
|
||||
}
|
||||
|
||||
return Expr.alloc(allocator, E.Binary{ .op = .bin_comma, .left = a, .right = b }, a.loc);
|
||||
}
|
||||
|
||||
pub fn joinAllWithComma(all: []Expr, allocator: *std.mem.Allocator) Expr {
|
||||
std.debug.assert(all.len > 0);
|
||||
switch (all.len) {
|
||||
1 => {
|
||||
return all[0];
|
||||
},
|
||||
2 => {
|
||||
return Expr.joinWithComma(all[0], all[1], allocator);
|
||||
},
|
||||
else => {
|
||||
var expr = Expr.joinWithComma(all[0], all[1], allocator);
|
||||
var _all = all[2 .. all.len - 1];
|
||||
for (_all) |right| {
|
||||
expr = Expr.joinWithComma(expr, right, allocator);
|
||||
}
|
||||
|
||||
return expr;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jsonStringify(self: *const @This(), options: anytype, writer: anytype) !void {
|
||||
return try std.json.stringify(Serializable{ .@"type" = std.meta.activeTag(self.data), .object = "expr", .value = self.data, .loc = self.loc }, options, writer);
|
||||
}
|
||||
|
||||
pub fn extractNumericValues(left: Expr.Data, right: Expr.Data) ?[2]f64 {
|
||||
if (!(@as(Expr.Tag, left) == .e_number and @as(Expr.Tag, right) == .e_number)) {
|
||||
return null;
|
||||
@@ -2598,6 +2701,10 @@ pub const Op = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn jsonStringify(self: *const @This(), opts: anytype, o: anytype) !void {
|
||||
return try std.json.stringify(self.text, opts, o);
|
||||
}
|
||||
|
||||
pub const TableType: std.EnumArray(Op.Code, Op);
|
||||
pub const Table = comptime {
|
||||
var table = std.EnumArray(Op.Code, Op).initUndefined();
|
||||
@@ -2722,6 +2829,13 @@ pub const Ast = struct {
|
||||
.parts = parts,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toJSON(self: *Ast, allocator: *std.mem.Allocator, stream: anytype) !void {
|
||||
const opts = std.json.StringifyOptions{ .whitespace = std.json.StringifyOptions.Whitespace{
|
||||
.separator = true,
|
||||
} };
|
||||
try std.json.stringify(self.parts, opts, stream);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Span = struct {
|
||||
@@ -2885,6 +2999,9 @@ pub const Part = struct {
|
||||
// algorithm.
|
||||
is_live: bool = false,
|
||||
pub const SymbolUseMap = std.AutoHashMap(Ref, Symbol.Use);
|
||||
pub fn jsonStringify(self: *const Part, options: std.json.StringifyOptions, writer: anytype) !void {
|
||||
return std.json.stringify(self.stmts, options, writer);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Result = struct {
|
||||
|
||||
@@ -146,9 +146,9 @@ pub const ImportScanner = struct {
|
||||
// user is expecting the output to be as small as possible. So we
|
||||
// should omit unused imports.
|
||||
//
|
||||
const keep_unused_imports = !p.options.trim_unused_imports;
|
||||
// const keep_unused_imports = !p.options.trim_unused_imports;
|
||||
var did_remove_star_loc = false;
|
||||
// const keep_unused_imports = true;
|
||||
const keep_unused_imports = true;
|
||||
|
||||
// TypeScript always trims unused imports. This is important for
|
||||
// correctness since some imports might be fake (only in the type
|
||||
@@ -2319,6 +2319,8 @@ pub const P = struct {
|
||||
// }
|
||||
}
|
||||
|
||||
// This assumes the "function" token has already been parsed
|
||||
|
||||
pub fn parseFnStmt(p: *P, loc: logger.Loc, opts: *ParseStatementOptions, asyncRange: ?logger.Range) !Stmt {
|
||||
const isGenerator = p.lexer.token == T.t_asterisk;
|
||||
const isAsync = asyncRange != null;
|
||||
@@ -2352,9 +2354,11 @@ pub const P = struct {
|
||||
var nameLoc = p.lexer.loc();
|
||||
nameText = p.lexer.identifier;
|
||||
p.lexer.expect(T.t_identifier);
|
||||
// Difference
|
||||
const ref = try p.newSymbol(Symbol.Kind.other, nameText);
|
||||
name = js_ast.LocRef{
|
||||
.loc = nameLoc,
|
||||
.ref = null,
|
||||
.ref = ref,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2397,10 +2401,7 @@ pub const P = struct {
|
||||
return p.s(S.TypeScript{}, loc);
|
||||
}
|
||||
|
||||
// Balance the fake block scope introduced above
|
||||
if (hasIfScope) {
|
||||
p.popScope();
|
||||
}
|
||||
p.popScope();
|
||||
|
||||
// Only declare the function after we know if it had a body or not. Otherwise
|
||||
// TypeScript code such as this will double-declare the symbol:
|
||||
@@ -2418,6 +2419,11 @@ pub const P = struct {
|
||||
|
||||
func.flags.is_export = opts.is_export;
|
||||
|
||||
// Balance the fake block scope introduced above
|
||||
if (hasIfScope) {
|
||||
p.popScope();
|
||||
}
|
||||
|
||||
return p.s(S.Function{
|
||||
.func = func,
|
||||
}, func.open_parens_loc);
|
||||
@@ -2617,7 +2623,26 @@ pub const P = struct {
|
||||
|
||||
// TODO:
|
||||
pub fn parseTypeScriptDecorators(p: *P) []ExprNodeIndex {
|
||||
notimpl();
|
||||
if (!p.options.ts) {
|
||||
return &([_]ExprNodeIndex{});
|
||||
}
|
||||
|
||||
var decorators = List(ExprNodeIndex).init(p.allocator);
|
||||
while (p.lexer.token == T.t_at) {
|
||||
p.lexer.next();
|
||||
|
||||
// Parse a new/call expression with "exprFlagTSDecorator" so we ignore
|
||||
// EIndex expressions, since they may be part of a computed property:
|
||||
//
|
||||
// class Foo {
|
||||
// @foo ['computed']() {}
|
||||
// }
|
||||
//
|
||||
// This matches the behavior of the TypeScript compiler.
|
||||
decorators.append(p.parseExprWithFlags(.new, Expr.EFlags.ts_decorator)) catch unreachable;
|
||||
}
|
||||
|
||||
return decorators.toOwnedSlice();
|
||||
}
|
||||
|
||||
// TODO:
|
||||
@@ -3457,6 +3482,7 @@ pub const P = struct {
|
||||
update = p.parseExpr(.lowest);
|
||||
}
|
||||
|
||||
p.lexer.expect(.t_close_paren);
|
||||
var stmtOpts = ParseStatementOptions{};
|
||||
const body = p.parseStmt(&stmtOpts) catch unreachable;
|
||||
return p.s(
|
||||
@@ -3516,13 +3542,18 @@ pub const P = struct {
|
||||
p.lexer.expectContextualKeyword("from");
|
||||
},
|
||||
.t_open_brace => {
|
||||
// "import * as ns from 'path'"
|
||||
// "import {item1, item2} from 'path'"
|
||||
if (!opts.is_module_scope and (!opts.is_namespace_scope or !opts.is_typescript_declare)) {
|
||||
p.lexer.unexpected();
|
||||
fail();
|
||||
}
|
||||
var importClause = try p.parseImportClause();
|
||||
stmt = S.Import{ .namespace_ref = undefined, .import_record_index = std.math.maxInt(u32), .items = importClause.items, .is_single_line = importClause.is_single_line };
|
||||
stmt = S.Import{
|
||||
.namespace_ref = undefined,
|
||||
.import_record_index = std.math.maxInt(u32),
|
||||
.items = importClause.items,
|
||||
.is_single_line = importClause.is_single_line,
|
||||
};
|
||||
p.lexer.expectContextualKeyword("from");
|
||||
},
|
||||
.t_identifier => {
|
||||
@@ -3733,7 +3764,9 @@ pub const P = struct {
|
||||
p.lexer.expectOrInsertSemicolon();
|
||||
return stmt;
|
||||
},
|
||||
else => {},
|
||||
.expr => |_expr| {
|
||||
expr = _expr;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4484,10 +4517,6 @@ pub const P = struct {
|
||||
}, p.lexer.loc()));
|
||||
}
|
||||
|
||||
if (p.lexer.token == eend) {
|
||||
break :run;
|
||||
}
|
||||
|
||||
var stmt = p.parseStmt(opts) catch break :run;
|
||||
|
||||
// Skip TypeScript types entirely
|
||||
@@ -4562,6 +4591,10 @@ pub const P = struct {
|
||||
returnWithoutSemicolonStart = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (p.lexer.token == eend) {
|
||||
break :run;
|
||||
}
|
||||
}
|
||||
|
||||
return stmts.toOwnedSlice();
|
||||
@@ -4813,12 +4846,12 @@ pub const P = struct {
|
||||
|
||||
pub fn declareBinding(p: *P, kind: Symbol.Kind, binding: BindingNodeIndex, opts: *ParseStatementOptions) !void {
|
||||
switch (binding.data) {
|
||||
.b_missing => {},
|
||||
.b_identifier => |bind| {
|
||||
if (!opts.is_typescript_declare or (opts.is_namespace_scope and opts.is_export)) {
|
||||
bind.ref = try p.declareSymbol(kind, binding.loc, p.loadNameFromRef(bind.ref));
|
||||
}
|
||||
},
|
||||
.b_missing => |*bind| {},
|
||||
|
||||
.b_array => |bind| {
|
||||
for (bind.items) |item| {
|
||||
@@ -5175,7 +5208,7 @@ pub const P = struct {
|
||||
}
|
||||
}
|
||||
|
||||
p.lexer.expect(.t_close_brace);
|
||||
p.lexer.expect(.t_close_bracket);
|
||||
key = expr;
|
||||
},
|
||||
.t_asterisk => {
|
||||
@@ -5231,7 +5264,7 @@ pub const P = struct {
|
||||
}
|
||||
},
|
||||
.p_async => {
|
||||
if (!opts.is_async and strings.eql(raw, name)) {
|
||||
if (!opts.is_async and strings.eql(raw, name) and !p.lexer.has_newline_before) {
|
||||
opts.is_async = true;
|
||||
opts.async_range = name_range;
|
||||
|
||||
@@ -5240,7 +5273,7 @@ pub const P = struct {
|
||||
}
|
||||
},
|
||||
.p_static => {
|
||||
if (!opts.is_static and !opts.is_async and !opts.is_class and strings.eql(raw, name)) {
|
||||
if (!opts.is_static and !opts.is_async and opts.is_class and strings.eql(raw, name)) {
|
||||
opts.is_static = true;
|
||||
return p.parseProperty(kind, opts, null);
|
||||
}
|
||||
@@ -5429,11 +5462,11 @@ pub const P = struct {
|
||||
},
|
||||
.set => {
|
||||
if (func.args.len != 1) {
|
||||
var r = js_lexer.rangeOfIdentifier(&p.source, func.args[0].binding.loc);
|
||||
var r = js_lexer.rangeOfIdentifier(&p.source, if (func.args.len > 0) func.args[0].binding.loc else loc);
|
||||
if (func.args.len > 1) {
|
||||
r = js_lexer.rangeOfIdentifier(&p.source, func.args[1].binding.loc);
|
||||
}
|
||||
p.log.addRangeErrorFmt(p.source, r, p.allocator, "Setter {s} must have exactly 1 argument", .{p.keyNameForError(key)}) catch unreachable;
|
||||
p.log.addRangeErrorFmt(p.source, r, p.allocator, "Setter {s} must have exactly 1 argument (there are {d})", .{ p.keyNameForError(key), func.args.len }) catch unreachable;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
@@ -5516,17 +5549,17 @@ pub const P = struct {
|
||||
if (p.lexer.token == .t_extends) {
|
||||
p.lexer.next();
|
||||
extends = p.parseExpr(.new);
|
||||
}
|
||||
|
||||
// TypeScript's type argument parser inside expressions backtracks if the
|
||||
// first token after the end of the type parameter list is "{", so the
|
||||
// parsed expression above will have backtracked if there are any type
|
||||
// arguments. This means we have to re-parse for any type arguments here.
|
||||
// This seems kind of wasteful to me but it's what the official compiler
|
||||
// does and it probably doesn't have that high of a performance overhead
|
||||
// because "extends" clauses aren't that frequent, so it should be ok.
|
||||
if (p.options.ts) {
|
||||
p.skipTypeScriptTypeArguments(false); // isInsideJSXElement
|
||||
// TypeScript's type argument parser inside expressions backtracks if the
|
||||
// first token after the end of the type parameter list is "{", so the
|
||||
// parsed expression above will have backtracked if there are any type
|
||||
// arguments. This means we have to re-parse for any type arguments here.
|
||||
// This seems kind of wasteful to me but it's what the official compiler
|
||||
// does and it probably doesn't have that high of a performance overhead
|
||||
// because "extends" clauses aren't that frequent, so it should be ok.
|
||||
if (p.options.ts) {
|
||||
p.skipTypeScriptTypeArguments(false); // isInsideJSXElement
|
||||
}
|
||||
}
|
||||
|
||||
if (p.options.ts and p.lexer.isContextualKeyword("implements")) {
|
||||
@@ -5555,8 +5588,7 @@ pub const P = struct {
|
||||
const scopeIndex = p.pushScopeForParsePass(.class_body, body_loc) catch unreachable;
|
||||
|
||||
var opts = PropertyOpts{ .is_class = true, .allow_ts_decorators = class_opts.allow_ts_decorators, .class_has_extends = extends != null };
|
||||
|
||||
while (p.lexer.token != .t_close_brace) {
|
||||
while (p.lexer.token != T.t_close_brace) {
|
||||
if (p.lexer.token == .t_semicolon) {
|
||||
p.lexer.next();
|
||||
continue;
|
||||
@@ -5597,6 +5629,8 @@ pub const P = struct {
|
||||
p.allow_in = old_allow_in;
|
||||
p.allow_private_identifiers = old_allow_private_identifiers;
|
||||
|
||||
p.lexer.expect(.t_close_brace);
|
||||
|
||||
return G.Class{
|
||||
.class_name = name,
|
||||
.extends = extends,
|
||||
@@ -6474,6 +6508,7 @@ pub const P = struct {
|
||||
pub fn _parsePrefix(p: *P, level: Level, errors: *DeferredErrors, flags: Expr.EFlags) Expr {
|
||||
const loc = p.lexer.loc();
|
||||
const l = @enumToInt(level);
|
||||
std.debug.print("Parse Prefix {s}:{s} @{s} ", .{ p.lexer.token, p.lexer.raw(), @tagName(level) });
|
||||
|
||||
switch (p.lexer.token) {
|
||||
.t_super => {
|
||||
@@ -6499,7 +6534,7 @@ pub const P = struct {
|
||||
p.lexer.next();
|
||||
|
||||
// Arrow functions aren't allowed in the middle of expressions
|
||||
if (l > @enumToInt(Level.assign)) {
|
||||
if (level.gt(.assign)) {
|
||||
const oldAllowIn = p.allow_in;
|
||||
p.allow_in = true;
|
||||
|
||||
@@ -6885,7 +6920,7 @@ pub const P = struct {
|
||||
const old_allow_in = p.allow_in;
|
||||
p.allow_in = true;
|
||||
|
||||
while (p.lexer.token != .t_close_brace and p.lexer.token != .t_end_of_file) {
|
||||
while (p.lexer.token != .t_close_brace) {
|
||||
if (p.lexer.token == .t_dot_dot_dot) {
|
||||
p.lexer.next();
|
||||
properties.append(G.Property{ .kind = .spread, .value = p.parseExpr(.comma) }) catch unreachable;
|
||||
@@ -7312,6 +7347,7 @@ pub const P = struct {
|
||||
}
|
||||
|
||||
p.pushScopeForVisitPass(.function_args, open_parens_loc) catch unreachable;
|
||||
defer p.popScope();
|
||||
p.visitArgs(
|
||||
func.args,
|
||||
VisitArgsOpts{
|
||||
@@ -7320,13 +7356,17 @@ pub const P = struct {
|
||||
.is_unique_formal_parameters = true,
|
||||
},
|
||||
);
|
||||
defer p.popScope();
|
||||
const body = func.body orelse p.panic("Expected visitFunc to have body {s}", .{func});
|
||||
|
||||
var body = func.body orelse p.panic("Expected visitFunc to have body {s}", .{func});
|
||||
p.pushScopeForVisitPass(.function_body, body.loc) catch unreachable;
|
||||
defer p.popScope();
|
||||
var stmts = List(Stmt).fromOwnedSlice(p.allocator, body.stmts);
|
||||
var temp_opts = PrependTempRefsOpts{ .kind = StmtsKind.fn_body, .fn_body_loc = body.loc };
|
||||
p.visitStmtsAndPrependTempRefs(&stmts, &temp_opts) catch unreachable;
|
||||
func.body.?.stmts = stmts.toOwnedSlice();
|
||||
|
||||
body.stmts = stmts.toOwnedSlice();
|
||||
|
||||
func.body = body;
|
||||
}
|
||||
|
||||
pub fn maybeKeepExprSymbolName(p: *P, expr: Expr, original_name: string, was_anonymous_named_expr: bool) Expr {
|
||||
@@ -7382,7 +7422,7 @@ pub const P = struct {
|
||||
},
|
||||
|
||||
.e_import_meta => |exp| {
|
||||
const is_delete_target = exp == p.delete_target.e_import_meta;
|
||||
const is_delete_target = std.meta.activeTag(p.delete_target) == .e_import_meta and exp == p.delete_target.e_import_meta;
|
||||
|
||||
if (p.define.dots.get("meta")) |meta| {
|
||||
for (meta) |define| {
|
||||
@@ -7540,8 +7580,8 @@ pub const P = struct {
|
||||
else => {},
|
||||
}
|
||||
|
||||
const is_call_target = e_ == p.call_target.e_binary;
|
||||
const is_stmt_expr = e_ == p.stmt_expr_value.e_binary;
|
||||
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 was_anonymous_named_expr = p.isAnonymousNamedExpr(e_.right);
|
||||
|
||||
e_.left = p.visitExprInOut(e_.left, ExprIn{
|
||||
@@ -7841,8 +7881,8 @@ pub const P = struct {
|
||||
}
|
||||
},
|
||||
.e_index => |e_| {
|
||||
const is_call_target = e_ == p.call_target.e_index;
|
||||
const is_delete_target = e_ == p.delete_target.e_index;
|
||||
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;
|
||||
|
||||
const target = p.visitExprInOut(e_.target, ExprIn{
|
||||
// this is awkward due to a zig compiler bug
|
||||
@@ -8003,7 +8043,7 @@ pub const P = struct {
|
||||
}
|
||||
},
|
||||
.e_if => |e_| {
|
||||
const is_call_target = e_ == p.call_target.e_if;
|
||||
const is_call_target = (p.call_target) == .e_if and e_ == p.call_target.e_if;
|
||||
|
||||
e_.test_ = p.visitExpr(e_.test_);
|
||||
|
||||
@@ -9331,7 +9371,18 @@ pub const P = struct {
|
||||
}
|
||||
|
||||
pub fn lowerClass(p: *P, stmtorexpr: js_ast.StmtOrExpr, ref: Ref) []Stmt {
|
||||
notimpl();
|
||||
switch (stmtorexpr) {
|
||||
.stmt => |stmt| {
|
||||
var stmts = p.allocator.alloc(Stmt, 1) catch unreachable;
|
||||
stmts[0] = stmt;
|
||||
return stmts;
|
||||
},
|
||||
.expr => |expr| {
|
||||
var stmts = p.allocator.alloc(Stmt, 1) catch unreachable;
|
||||
stmts[0] = p.s(S.SExpr{ .value = expr }, expr.loc);
|
||||
return stmts;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visitForLoopInit(p: *P, stmt: Stmt, is_in_or_of: bool) Stmt {
|
||||
@@ -9651,7 +9702,7 @@ pub const P = struct {
|
||||
// are not allowed to assign to this symbol (it throws a TypeError).
|
||||
const name = p.symbols.items[class_name_ref.inner_index].original_name;
|
||||
var identifier = p.allocator.alloc(u8, name.len + 1) catch unreachable;
|
||||
std.mem.copy(u8, identifier[1 .. identifier.len - 1], name);
|
||||
std.mem.copy(u8, identifier[1..identifier.len], name);
|
||||
identifier[0] = '_';
|
||||
shadow_ref = p.newSymbol(Symbol.Kind.cconst, identifier) catch unreachable;
|
||||
p.recordDeclaredSymbol(shadow_ref) catch unreachable;
|
||||
@@ -9844,7 +9895,7 @@ pub const P = struct {
|
||||
// values that introduce new scopes and declare new symbols. If this is an
|
||||
// arrow function, then those new scopes will need to be parented under the
|
||||
// scope of the arrow function itself.
|
||||
const scopeIndex = p.pushScopeForParsePass(.function_args, loc);
|
||||
const scopeIndex = try p.pushScopeForParsePass(.function_args, loc);
|
||||
|
||||
// Allow "in" inside parentheses
|
||||
var oldAllowIn = p.allow_in;
|
||||
@@ -9952,7 +10003,67 @@ pub const P = struct {
|
||||
}
|
||||
}
|
||||
|
||||
return p.e(E.Missing{}, loc);
|
||||
// If we get here, it's not an arrow function so undo the pushing of the
|
||||
// scope we did earlier. This needs to flatten any child scopes into the
|
||||
// parent scope as if the scope was never pushed in the first place.
|
||||
p.popAndFlattenScope(scopeIndex);
|
||||
|
||||
// If this isn't an arrow function, then types aren't allowed
|
||||
if (type_colon_range.len > 0) {
|
||||
try p.log.addRangeError(p.source, type_colon_range, "Unexpected \":\"");
|
||||
p.panic("", .{});
|
||||
}
|
||||
|
||||
// Are these arguments for a call to a function named "async"?
|
||||
if (opts.is_async) {
|
||||
p.logExprErrors(&errors);
|
||||
const async_expr = p.e(E.Identifier{ .ref = try p.storeNameInRef("async") }, loc);
|
||||
return p.e(E.Call{ .target = async_expr, .args = items }, loc);
|
||||
}
|
||||
|
||||
// Is this a chain of expressions and comma operators?
|
||||
|
||||
if (items.len > 0) {
|
||||
p.logExprErrors(&errors);
|
||||
if (spread_range.len > 0) {
|
||||
try p.log.addRangeError(p.source, type_colon_range, "Unexpected \"...\"");
|
||||
p.panic("", .{});
|
||||
}
|
||||
|
||||
var value = Expr.joinAllWithComma(items, p.allocator);
|
||||
p.markExprAsParenthesized(&value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Indicate that we expected an arrow function
|
||||
p.lexer.expected(.t_equals_greater_than);
|
||||
p.panic("", .{});
|
||||
}
|
||||
|
||||
pub fn popAndFlattenScope(p: *P, scope_index: usize) void {
|
||||
// Move up to the parent scope
|
||||
var to_flatten = p.current_scope;
|
||||
var parent = to_flatten.parent.?;
|
||||
p.current_scope = parent;
|
||||
var scopes_in_order = p.scopes_in_order.toOwnedSlice();
|
||||
// Erase this scope from the order. This will shift over the indices of all
|
||||
// the scopes that were created after us. However, we shouldn't have to
|
||||
// worry about other code with outstanding scope indices for these scopes.
|
||||
// These scopes were all created in between this scope's push and pop
|
||||
// operations, so they should all be child scopes and should all be popped
|
||||
// by the time we get here.
|
||||
std.mem.copy(ScopeOrder, scopes_in_order[scope_index..scopes_in_order.len], scopes_in_order[scope_index + 1 .. scopes_in_order.len]);
|
||||
p.scopes_in_order = @TypeOf(p.scopes_in_order).fromOwnedSlice(p.allocator, scopes_in_order);
|
||||
|
||||
// Remove the last child from the parent scope
|
||||
const last = parent.children.items.len - 1;
|
||||
assert(parent.children.items[last] == to_flatten);
|
||||
_ = parent.children.popOrNull();
|
||||
|
||||
for (to_flatten.children.items) |item| {
|
||||
item.parent = parent;
|
||||
parent.children.append(item) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybeCommaSpreadError(p: *P, _comma_after_spread: ?logger.Loc) void {
|
||||
|
||||
@@ -1941,6 +1941,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type {
|
||||
}
|
||||
const name = s.func.name orelse std.debug.panic("Internal error: expected func to have a name ref\n{s}", .{s});
|
||||
const nameRef = name.ref orelse std.debug.panic("Internal error: expected func to have a name\n{s}", .{s});
|
||||
p.printSpace();
|
||||
p.printSymbol(nameRef);
|
||||
p.printFunc(s.func);
|
||||
p.printNewline();
|
||||
@@ -1951,7 +1952,7 @@ pub fn NewPrinter(comptime ascii_only: bool) type {
|
||||
if (s.is_export) {
|
||||
p.print("export ");
|
||||
}
|
||||
p.print("class");
|
||||
p.print("class ");
|
||||
p.printSymbol(s.class.class_name.?.ref.?);
|
||||
p.printClass(s.class);
|
||||
p.printNewline();
|
||||
|
||||
@@ -43,6 +43,10 @@ pub const Loc = packed struct {
|
||||
pub fn eql(loc: *Loc, other: Loc) bool {
|
||||
return loc.start == other.start;
|
||||
}
|
||||
|
||||
pub fn jsonStringify(self: *const Loc, options: anytype, writer: anytype) !void {
|
||||
return try std.json.stringify(self.start, options, writer);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Location = struct {
|
||||
@@ -142,6 +146,10 @@ pub const Range = packed struct {
|
||||
pub fn endI(self: *const Range) usize {
|
||||
return std.math.lossyCast(usize, self.loc.start + self.len);
|
||||
}
|
||||
|
||||
pub fn jsonStringify(self: *const Range, options: anytype, writer: anytype) !void {
|
||||
return try std.json.stringify([2]i32{ self.loc.start, self.len + self.loc.start }, options, writer);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Log = struct {
|
||||
|
||||
28
src/main.zig
28
src/main.zig
@@ -10,9 +10,24 @@ const js_ast = @import("js_ast.zig");
|
||||
const linker = @import("linker.zig");
|
||||
usingnamespace @import("ast/base.zig");
|
||||
usingnamespace @import("defines.zig");
|
||||
const panicky = @import("panic_handler.zig");
|
||||
|
||||
const MainPanicHandler = panicky.NewPanicHandler(panicky.default_panic);
|
||||
|
||||
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
|
||||
if (MainPanicHandler.Singleton) |singleton| {
|
||||
MainPanicHandler.handle_panic(msg, error_return_trace);
|
||||
} else {
|
||||
panicky.default_panic(msg, error_return_trace);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
try alloc.setup(std.heap.page_allocator);
|
||||
var log = logger.Log.init(alloc.dynamic);
|
||||
var panicker = MainPanicHandler.init(&log);
|
||||
panicker.skip_next_panic = true;
|
||||
MainPanicHandler.Singleton = &panicker;
|
||||
|
||||
const args = try std.process.argsAlloc(alloc.dynamic);
|
||||
const stdout = std.io.getStdOut();
|
||||
@@ -30,7 +45,7 @@ pub fn main() anyerror!void {
|
||||
const code = try file.readToEndAlloc(alloc.dynamic, stat.size);
|
||||
|
||||
const opts = try options.TransformOptions.initUncached(alloc.dynamic, entryPointName, code);
|
||||
var log = logger.Log.init(alloc.dynamic);
|
||||
|
||||
var source = logger.Source.initFile(opts.entry_point, alloc.dynamic);
|
||||
var ast: js_ast.Ast = undefined;
|
||||
var raw_defines = RawDefines.init(alloc.static);
|
||||
@@ -79,13 +94,10 @@ pub fn main() anyerror!void {
|
||||
);
|
||||
|
||||
if (std.builtin.mode == std.builtin.Mode.Debug) {
|
||||
std.debug.print("\n--AST DEBUG--:\n", .{});
|
||||
std.debug.print("Lines: {d}\n", .{ast.approximate_line_count});
|
||||
std.debug.print("Parts: {d}\n{s}\n", .{ ast.parts.len, ast.parts });
|
||||
std.debug.print("Symbols: {d}\n{s}\n", .{ ast.symbols.len, ast.symbols });
|
||||
std.debug.print("Imports: {d}\n{s}\n", .{ ast.named_exports.count(), ast.named_imports });
|
||||
std.debug.print("Exports: {d}\n{s}\n", .{ ast.named_imports.count(), ast.named_exports });
|
||||
std.debug.print("\n--AST DEBUG--:\n", .{});
|
||||
var fixed_buffer = [_]u8{0} ** 512000;
|
||||
var buf_stream = std.io.fixedBufferStream(&fixed_buffer);
|
||||
|
||||
try ast.toJSON(alloc.dynamic, stderr.writer());
|
||||
}
|
||||
|
||||
_ = try stdout.write(printed.js);
|
||||
|
||||
58
src/panic_handler.zig
Normal file
58
src/panic_handler.zig
Normal file
@@ -0,0 +1,58 @@
|
||||
const std = @import("std");
|
||||
const logger = @import("logger.zig");
|
||||
const root = @import("root");
|
||||
|
||||
const USERLAND_PANIC_MESSAGE = "iNtErNaL sErVeR eRrOr";
|
||||
|
||||
/// This function is used by the Zig language code generation and
|
||||
/// therefore must be kept in sync with the compiler implementation.
|
||||
pub fn default_panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn {
|
||||
@setCold(true);
|
||||
if (@hasDecl(root, "os") and @hasDecl(root.os, "panic")) {
|
||||
root.os.panic(msg, error_return_trace);
|
||||
unreachable;
|
||||
}
|
||||
switch (std.builtin.os.tag) {
|
||||
.freestanding => {
|
||||
while (true) {
|
||||
@breakpoint();
|
||||
}
|
||||
},
|
||||
.wasi => {
|
||||
std.debug.warn("{s}", .{msg});
|
||||
std.os.abort();
|
||||
},
|
||||
.uefi => {
|
||||
// TODO look into using the debug info and logging helpful messages
|
||||
std.os.abort();
|
||||
},
|
||||
else => {
|
||||
const first_trace_addr = @returnAddress();
|
||||
std.debug.panicExtra(error_return_trace, first_trace_addr, "{s}", .{msg});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn NewPanicHandler(panic_func: fn handle_panic(msg: []const u8, error_return_type: ?*std.builtin.StackTrace) noreturn) type {
|
||||
return struct {
|
||||
panic_count: usize = 0,
|
||||
skip_next_panic: bool = false,
|
||||
log: *logger.Log,
|
||||
|
||||
pub var Singleton: ?*Handler = null;
|
||||
const Handler = @This();
|
||||
|
||||
pub fn init(log: *logger.Log) Handler {
|
||||
return Handler{
|
||||
.log = log,
|
||||
};
|
||||
}
|
||||
pub fn handle_panic(msg: []const u8, error_return_type: ?*std.builtin.StackTrace) callconv(.Inline) noreturn {
|
||||
if (@This().Singleton) |singleton| {
|
||||
singleton.panic_count += 1;
|
||||
}
|
||||
|
||||
panic_func(msg, error_return_type);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user