Skip to content

Commit 598b759

Browse files
crisbetothePunderWoman
authored andcommitted
fix(compiler): avoid errors with extremely long instruction chains (angular#45574)
Our logic for generating code from an AST uses recursion which limits the number of expressions we can nest before we reach the call stack limit. These changes add a limit in order to avoid errors in some cases where the chains become extremely long. Fixes angular#45564. PR Close angular#45574
1 parent 7bf1cf4 commit 598b759

File tree

1 file changed

+11
-1
lines changed
  • packages/compiler/src/render3/view

1 file changed

+11
-1
lines changed

packages/compiler/src/render3/view/util.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ export const NON_BINDABLE_ATTR = 'ngNonBindable';
5050
/** Name for the variable keeping track of the context returned by `ɵɵrestoreView`. */
5151
export const RESTORED_VIEW_CONTEXT_NAME = 'restoredCtx';
5252

53+
/**
54+
* Maximum length of a single instruction chain. Because our output AST uses recursion, we're
55+
* limited in how many expressions we can nest before we reach the call stack limit. This
56+
* length is set very conservatively in order to reduce the chance of problems.
57+
*/
58+
const MAX_CHAIN_LENGTH = 500;
59+
5360
/** Instructions that support chaining. */
5461
const CHAINABLE_INSTRUCTIONS = new Set([
5562
R3.element,
@@ -309,6 +316,7 @@ export function getInstructionStatements(instructions: Instruction[]): o.Stateme
309316
const statements: o.Statement[] = [];
310317
let pendingExpression: o.Expression|null = null;
311318
let pendingExpressionType: o.ExternalReference|null = null;
319+
let chainLength = 0;
312320

313321
for (const current of instructions) {
314322
const resolvedParams =
@@ -318,16 +326,18 @@ export function getInstructionStatements(instructions: Instruction[]): o.Stateme
318326

319327
// If the current instruction is the same as the previous one
320328
// and it can be chained, add another call to the chain.
321-
if (pendingExpressionType === current.reference &&
329+
if (chainLength < MAX_CHAIN_LENGTH && pendingExpressionType === current.reference &&
322330
CHAINABLE_INSTRUCTIONS.has(pendingExpressionType)) {
323331
// We'll always have a pending expression when there's a pending expression type.
324332
pendingExpression = pendingExpression!.callFn(params, pendingExpression!.sourceSpan);
333+
chainLength++;
325334
} else {
326335
if (pendingExpression !== null) {
327336
statements.push(pendingExpression.toStmt());
328337
}
329338
pendingExpression = invokeInstruction(current.span, current.reference, params);
330339
pendingExpressionType = current.reference;
340+
chainLength = 0;
331341
}
332342
}
333343

0 commit comments

Comments
 (0)