mirror of
https://github.com/Samsung/escargot.git
synced 2026-06-22 10:01:50 +00:00
A `using` declaration in a switch case preceded by another statement aborted with Assertion `isDisposableResourceRecord()' failed / SEGV in finalizeDisposable. The switch releases its discriminant register before generating the case bodies, but pushLexicalBlock had allocated the disposable-record register on top of it. The early giveUpRegister therefore freed the disposable register instead, and a statement in the case body (e.g. `o.k = 1`) reused that register slot, clobbering the record; Initialize/FinalizeDisposable then dereferenced a non-pointer value. When the switch block contains a `using` declaration, defer releasing the discriminant temporaries until after finalizeLexicalBlock has popped the disposable register (preserving LIFO order). Switches without `using` are unchanged. 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
e7221f4211
commit
5e3b91b052
1 changed files with 27 additions and 2 deletions
|
|
@ -53,6 +53,11 @@ public:
|
|||
m_discriminant->generateExpressionByteCode(codeBlock, &newContext, rIndex0);
|
||||
newContext.m_canSkipCopyToRegister = canSkipCopyToRegister;
|
||||
|
||||
// Whether the discriminant occupies its own freshly-allocated register (rather than
|
||||
// reusing an existing variable's register). Captured here, before pushLexicalBlock
|
||||
// may allocate a disposable-record register (for `using`) on top of it. - Issue #1577
|
||||
bool discriminantHasOwnRegister = (rIndex0 == newContext.getLastRegisterIndex());
|
||||
|
||||
if (firstRegister == 0 && context->shouldCareScriptExecutionResult()) {
|
||||
codeBlock->pushCode(LoadLiteral(ByteCodeLOC(m_loc.index), 0, Value()), context, this->m_loc.index);
|
||||
}
|
||||
|
|
@ -65,6 +70,14 @@ public:
|
|||
blockContext = codeBlock->pushLexicalBlock(&newContext, bi, this);
|
||||
}
|
||||
|
||||
// When the switch block declares a `using` variable, pushLexicalBlock allocates a
|
||||
// disposable-record register that lives until finalizeLexicalBlock. It sits on the
|
||||
// register stack above the discriminant temporaries, so those temporaries can only be
|
||||
// released (LIFO) after the disposable register is popped in finalizeLexicalBlock.
|
||||
// Releasing them earlier (the default path below) would free the disposable register
|
||||
// itself, letting a statement in a case body clobber it. - Issue #1577
|
||||
bool hasUsingBlock = (m_lexicalBlockIndex != LEXICAL_BLOCK_INDEX_MAX) && (blockContext.usingBlockTryStartPosition != SIZE_MAX);
|
||||
|
||||
std::vector<size_t> jumpCodePerCaseNodePosition;
|
||||
StatementNode* nd = m_casesB->firstChild();
|
||||
while (nd) {
|
||||
|
|
@ -95,9 +108,11 @@ public:
|
|||
nd = nd->nextSibling();
|
||||
}
|
||||
|
||||
newContext.giveUpRegister();
|
||||
if (registerWasSame) {
|
||||
if (!hasUsingBlock) {
|
||||
newContext.giveUpRegister();
|
||||
if (registerWasSame) {
|
||||
newContext.giveUpRegister();
|
||||
}
|
||||
}
|
||||
|
||||
size_t jmpToDefault = SIZE_MAX;
|
||||
|
|
@ -134,6 +149,16 @@ public:
|
|||
newContext.m_lexicalBlockIndex = lexicalBlockIndexBefore;
|
||||
}
|
||||
|
||||
// Deferred release of the discriminant temporaries for the `using` case (see the
|
||||
// comment where hasUsingBlock is computed). finalizeLexicalBlock above has already
|
||||
// popped the disposable-record register, so these are now back on top. - Issue #1577
|
||||
if (hasUsingBlock) {
|
||||
newContext.giveUpRegister();
|
||||
if (discriminantHasOwnRegister) {
|
||||
newContext.giveUpRegister();
|
||||
}
|
||||
}
|
||||
|
||||
newContext.propagateInformationTo(*context);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue