mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
`L: for (const v of [...]) { continue L; }` aborted with
Assertion `!v.isEmpty()' failed, and `continue OUTER` from a nested
loop silently terminated the script.
A `continue <label>` whose label targets a for-of loop was left to be
resolved by LabelledStatementNode after the loop body, by which point
the for-of iterator-cleanup try block had registered the jump as a
complex case. It was then morphed into a JumpComplexCase that unwound
the try block, wrongly closing the iterator and leaving an empty Value
in the result register.
A previous per-loop attempt (8fd141b2) was reverted (60b1202a) because
a single m_currentLoopLabel leaked into nested loops and broke test262.
Track all labels directly targeting a loop (m_currentLoopLabels), clear
the list when entering each loop body so nested loops never inherit it,
and let for-of/for-in resolve continues for its own labels to
continuePosition (a plain jump, identical to an unlabeled continue)
before the try block is registered as a complex case.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
parent
b30b63fc63
commit
e7221f4211
7 changed files with 30 additions and 6 deletions
|
|
@ -64,7 +64,6 @@ ByteCodeGenerateContext::ByteCodeGenerateContext(InterpretedCodeBlock* codeBlock
|
|||
, m_lexicalBlockIndex(0)
|
||||
, m_classInfo()
|
||||
, m_numeralLiteralData(numeralLiteralData) // should be NumeralLiteralVector
|
||||
, m_currentLoopLabel(nullptr)
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_returnRegister(SIZE_MAX)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ struct ByteCodeGenerateContext {
|
|||
, m_lexicalBlockIndex(contextBefore.m_lexicalBlockIndex)
|
||||
, m_classInfo(contextBefore.m_classInfo)
|
||||
, m_numeralLiteralData(contextBefore.m_numeralLiteralData) // should be NumeralLiteralVector
|
||||
, m_currentLoopLabel(contextBefore.m_currentLoopLabel)
|
||||
, m_currentLoopLabels(contextBefore.m_currentLoopLabels)
|
||||
#if defined(ENABLE_TCO)
|
||||
, m_returnRegister(contextBefore.m_returnRegister)
|
||||
#endif
|
||||
|
|
@ -398,7 +398,10 @@ struct ByteCodeGenerateContext {
|
|||
ClassContextInformation m_classInfo;
|
||||
std::map<size_t, size_t> m_complexCaseStatementPositions;
|
||||
void* m_numeralLiteralData; // should be NumeralLiteralVector
|
||||
String* m_currentLoopLabel; // Label of the immediately enclosing loop (if in a labeled loop) - Issue #1571
|
||||
// Labels directly targeting the loop that is about to be generated (a loop may
|
||||
// carry more than one label, e.g. `L1: L2: for (...)`). A loop clears this list
|
||||
// before generating its body so that nested loops do not inherit it. - Issue #1571/#1577
|
||||
std::vector<String*> m_currentLoopLabels;
|
||||
|
||||
#if defined(ENABLE_TCO)
|
||||
size_t m_returnRegister; // for tail call optimizaiton (TCO)
|
||||
|
|
|
|||
|
|
@ -42,6 +42,8 @@ public:
|
|||
#endif /* ESCARGOT_DEBUGGER */
|
||||
|
||||
ByteCodeGenerateContext newContext(*context);
|
||||
// Do not let nested loops inherit this loop's labels. - Issue #1571/#1577
|
||||
newContext.m_currentLoopLabels.clear();
|
||||
|
||||
size_t doStart = codeBlock->currentCodeSize();
|
||||
if (context->shouldCareScriptExecutionResult()) {
|
||||
|
|
|
|||
|
|
@ -162,6 +162,10 @@ public:
|
|||
}
|
||||
|
||||
ByteCodeGenerateContext newContext(*context);
|
||||
// The labels of this loop (collected in `context`) must not be inherited by
|
||||
// nested loops generated while emitting the head/body, otherwise an inner loop
|
||||
// would wrongly consume a `continue <label>` that targets this loop. - Issue #1571/#1577
|
||||
newContext.m_currentLoopLabels.clear();
|
||||
|
||||
newContext.getRegister(); // ExecutionResult of m_right should not overwrite any reserved value
|
||||
if (m_left->type() == ASTNodeType::VariableDeclaration) {
|
||||
|
|
@ -373,6 +377,16 @@ public:
|
|||
newContext.consumeContinuePositions(codeBlock, continuePosition, newContext.tryCatchWithBlockStatementCount());
|
||||
newContext.m_positionToContinue = continuePosition;
|
||||
|
||||
// A `continue <label>` whose label targets THIS loop is semantically identical
|
||||
// to an unlabeled `continue`, so resolve it to `continuePosition` here, before the
|
||||
// iterator-cleanup try block of for-of is registered as a complex case. Otherwise
|
||||
// the labeled continue would be morphed into a JumpComplexCase that unwinds the
|
||||
// for-of try (wrongly closing the iterator and corrupting the result register),
|
||||
// which crashed `L: for (const v of [...]) { continue L; }`. - Issue #1571/#1577
|
||||
for (size_t i = 0; i < context->m_currentLoopLabels.size(); i++) {
|
||||
newContext.consumeLabelledContinuePositions(codeBlock, continuePosition, context->m_currentLoopLabels[i], newContext.tryCatchWithBlockStatementCount());
|
||||
}
|
||||
|
||||
if (!m_forIn) {
|
||||
TryStatementNode::generateTryStatementBodyEndByteCode(codeBlock, &newContext, this, forOfTryStatementContext);
|
||||
TryStatementNode::generateTryFinalizerStatementStartByteCode(codeBlock, &newContext, this, forOfTryStatementContext, true);
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ public:
|
|||
}
|
||||
|
||||
ByteCodeGenerateContext newContext(*context);
|
||||
// Do not let nested loops inherit this loop's labels. - Issue #1571/#1577
|
||||
newContext.m_currentLoopLabels.clear();
|
||||
|
||||
newContext.getRegister(); // ExeuctionResult of m_body should not be overwritten by m_test
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,12 @@ public:
|
|||
{
|
||||
size_t start = codeBlock->currentCodeSize();
|
||||
context->m_positionToContinue = start;
|
||||
String* previousLoopLabel = context->m_currentLoopLabel;
|
||||
context->m_currentLoopLabel = m_label;
|
||||
// Make this label visible to the loop that this labeled statement (possibly
|
||||
// through a chain of labels) directly wraps, so the loop can resolve a
|
||||
// `continue <label>` targeting itself just like an unlabeled continue. - Issue #1571/#1577
|
||||
context->m_currentLoopLabels.push_back(m_label);
|
||||
m_statementNode->generateStatementByteCode(codeBlock, context);
|
||||
context->m_currentLoopLabel = previousLoopLabel;
|
||||
context->m_currentLoopLabels.pop_back();
|
||||
size_t end = codeBlock->currentCodeSize();
|
||||
context->consumeLabelledBreakPositions(codeBlock, end, m_label, context->tryCatchWithBlockStatementCount());
|
||||
context->consumeLabelledContinuePositions(codeBlock, context->m_positionToContinue, m_label, context->tryCatchWithBlockStatementCount());
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ public:
|
|||
}
|
||||
|
||||
ByteCodeGenerateContext newContext(*context);
|
||||
// Do not let nested loops inherit this loop's labels. - Issue #1571/#1577
|
||||
newContext.m_currentLoopLabels.clear();
|
||||
|
||||
newContext.getRegister(); // ExeuctionResult of m_body should not be overwritten by m_test
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue