Reduce stack space usage of parseSuffix (#21662)

### What does this PR do?

Reduce stack space usage of parseSuffix

### How did you verify your code works?

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
Jarred Sumner
2025-08-09 00:20:17 -07:00
committed by GitHub
parent 2f84949fe0
commit 19fac68e81
11 changed files with 6472 additions and 3120 deletions

View File

@@ -26,25 +26,27 @@ pub fn Parse(
pub const parseTypeScriptImportEqualsStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypeScriptImportEqualsStmt;
pub const parseTypescriptEnumStmt = @import("./parseTypescript.zig").ParseTypescript(parser_feature__typescript, parser_feature__jsx, parser_feature__scan_only).parseTypescriptEnumStmt;
pub inline fn parseExprOrBindings(p: *P, level: Level, errors: ?*DeferredErrors) anyerror!Expr {
return try p.parseExprCommon(level, errors, Expr.EFlags.none);
pub inline fn parseExprOrBindings(p: *P, level: Level, errors: ?*DeferredErrors, expr: *Expr) anyerror!void {
return p.parseExprCommon(level, errors, Expr.EFlags.none, expr);
}
pub inline fn parseExpr(p: *P, level: Level) anyerror!Expr {
return try p.parseExprCommon(level, null, Expr.EFlags.none);
var expr: Expr = undefined;
try p.parseExprCommon(level, null, Expr.EFlags.none, &expr);
return expr;
}
pub inline fn parseExprWithFlags(p: *P, level: Level, flags: Expr.EFlags) anyerror!Expr {
return try p.parseExprCommon(level, null, flags);
pub inline fn parseExprWithFlags(p: *P, level: Level, flags: Expr.EFlags, expr: *Expr) anyerror!void {
return p.parseExprCommon(level, null, flags, expr);
}
pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags) anyerror!Expr {
pub fn parseExprCommon(p: *P, level: Level, errors: ?*DeferredErrors, flags: Expr.EFlags, expr: *Expr) anyerror!void {
if (!p.stack_check.isSafeToRecurse()) {
try bun.throwStackOverflow();
}
const had_pure_comment_before = p.lexer.has_pure_comment_before and !p.options.ignore_dce_annotations;
var expr = try p.parsePrefix(level, errors, flags);
expr.* = try p.parsePrefix(level, errors, flags);
// There is no formal spec for "__PURE__" comments but from reverse-
// engineering, it looks like they apply to the next CallExpression or
@@ -52,7 +54,7 @@ pub fn Parse(
// to the expression "a().b()".
if (had_pure_comment_before and level.lt(.call)) {
expr = try p.parseSuffix(expr, @as(Level, @enumFromInt(@intFromEnum(Level.call) - 1)), errors, flags);
try p.parseSuffix(expr, @as(Level, @enumFromInt(@intFromEnum(Level.call) - 1)), errors, flags);
switch (expr.data) {
.e_call => |ex| {
ex.can_be_unwrapped_if_unused = .if_unused;
@@ -64,7 +66,7 @@ pub fn Parse(
}
}
return try p.parseSuffix(expr, level, errors, flags);
try p.parseSuffix(expr, level, errors, flags);
}
pub fn parseYieldExpr(p: *P, loc: logger.Loc) !ExprNodeIndex {
@@ -343,10 +345,13 @@ pub fn Parse(
// We don't know yet whether these are arguments or expressions, so parse
p.latest_arrow_arg_loc = p.lexer.loc();
var item = try p.parseExprOrBindings(.comma, &errors);
try items_list.ensureUnusedCapacity(1);
const item: *Expr = &items_list.unusedCapacitySlice()[0];
try p.parseExprOrBindings(.comma, &errors, item);
items_list.items.len += 1;
if (is_spread) {
item = p.newExpr(E.Spread{ .value = item }, loc);
item.* = p.newExpr(E.Spread{ .value = item.* }, loc);
}
// Skip over types
@@ -359,11 +364,9 @@ pub fn Parse(
// There may be a "=" after the type (but not after an "as" cast)
if (is_typescript_enabled and p.lexer.token == .t_equals and !p.forbid_suffix_after_as_loc.eql(p.lexer.loc())) {
try p.lexer.next();
item = Expr.assign(item, try p.parseExpr(.comma));
item.* = Expr.assign(item.*, try p.parseExpr(.comma));
}
items_list.append(item) catch unreachable;
if (p.lexer.token != .t_comma) {
break;
}
@@ -675,7 +678,7 @@ pub fn Parse(
try p.lexer.next();
const raw2 = p.lexer.raw();
const value = if (p.lexer.token == .t_identifier and strings.eqlComptime(raw2, "using")) value: {
var value = if (p.lexer.token == .t_identifier and strings.eqlComptime(raw2, "using")) value: {
// const using_loc = p.saveExprCommentsHere();
const using_range = p.lexer.range();
try p.lexer.next();
@@ -711,13 +714,15 @@ pub fn Parse(
if (p.lexer.token == .t_asterisk_asterisk) {
try p.lexer.unexpected();
}
const expr = p.newExpr(
E.Await{ .value = try p.parseSuffix(value, .prefix, null, .none) },
try p.parseSuffix(&value, .prefix, null, .none);
var expr = p.newExpr(
E.Await{ .value = value },
token_range.loc,
);
try p.parseSuffix(&expr, .lowest, null, .none);
return ExprOrLetStmt{
.stmt_or_expr = js_ast.StmtOrExpr{
.expr = try p.parseSuffix(expr, .lowest, null, .none),
.expr = expr,
},
};
} else {
@@ -730,12 +735,13 @@ pub fn Parse(
// Parse the remainder of this expression that starts with an identifier
const ref = try p.storeNameInRef(raw);
const expr = p.newExpr(E.Identifier{ .ref = ref }, token_range.loc);
return ExprOrLetStmt{
var result = ExprOrLetStmt{
.stmt_or_expr = js_ast.StmtOrExpr{
.expr = try p.parseSuffix(expr, .lowest, null, .none),
.expr = p.newExpr(E.Identifier{ .ref = ref }, token_range.loc),
},
};
try p.parseSuffix(&result.stmt_or_expr.expr, .lowest, null, .none);
return result;
}
pub fn parseBinding(p: *P, comptime opts: ParseBindingOptions) anyerror!Binding {