Assorted bugfixes but the next step really is porting tests and fixing

This commit is contained in:
Jarred Sumner
2021-05-01 01:28:40 -07:00
parent 006ca4f13c
commit f59ec8d6c0
7 changed files with 371 additions and 60 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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
View 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);
}
};
}