Skip to content

Commit 11406eb

Browse files
ksh8281claude
andcommitted
Fix continue/break at first instruction of env-allocating block (Issue #1571)
A continue/break that is the first instruction inside a lexical block which allocates an environment (e.g. `for(;c;){ continue; eval(); const x=1; }`) was emitted as a plain Jump, skipping the block's CloseLexicalEnvironment. The leaked environment then caused a subsequent outer-scope `const` to initialize in the wrong environment, producing a spurious `ReferenceError: Cannot access '...' before initialization`. registerJumpPositionsToComplexCase compared jump positions against frontlimit (= lexicalBlockStartPosition, the first body instruction) with strict `>`, so a jump located exactly at the first body instruction was never morphed into a JumpComplexCase and the block environment was left un-popped. Use `>=` for break/continue/labelledBreak/labelledContinue. With the environment now unwound correctly, the hasBinding guard band-aid in InterpreterSlowPath::initializeByName is no longer needed and is removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
1 parent df9078d commit 11406eb

2 files changed

Lines changed: 12 additions & 8 deletions

File tree

src/interpreter/ByteCodeGenerator.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,26 +183,33 @@ struct ByteCodeGenerateContext {
183183
void registerJumpPositionsToComplexCase(size_t frontlimit)
184184
{
185185
ASSERT(tryCatchWithBlockStatementCount());
186+
// `frontlimit` is the byte position of the first instruction of the block body
187+
// (i.e. lexicalBlockStartPosition, set right after the BlockOperation opcode).
188+
// A break/continue jump located at exactly that position is the very first
189+
// statement of the block body and therefore IS inside the block, so it must be
190+
// morphed into a JumpComplexCase to unwind the block's lexical environment.
191+
// Using strictly greater-than (`>`) here would skip such a jump and leave the
192+
// block environment un-popped (Issue #1571), so the comparison must be `>=`.
186193
for (unsigned i = 0; i < m_breakStatementPositions.size(); i++) {
187-
if (m_breakStatementPositions[i] > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_breakStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
194+
if (m_breakStatementPositions[i] >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_breakStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
188195
m_complexCaseStatementPositions.insert(std::make_pair(m_breakStatementPositions[i], tryCatchWithBlockStatementCount()));
189196
}
190197
}
191198

192199
for (unsigned i = 0; i < m_continueStatementPositions.size(); i++) {
193-
if (m_continueStatementPositions[i] > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_continueStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
200+
if (m_continueStatementPositions[i] >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_continueStatementPositions[i]) == m_complexCaseStatementPositions.end()) {
194201
m_complexCaseStatementPositions.insert(std::make_pair(m_continueStatementPositions[i], tryCatchWithBlockStatementCount()));
195202
}
196203
}
197204

198205
for (unsigned i = 0; i < m_labelledBreakStatmentPositions.size(); i++) {
199-
if (m_labelledBreakStatmentPositions[i].second > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledBreakStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
206+
if (m_labelledBreakStatmentPositions[i].second >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledBreakStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
200207
m_complexCaseStatementPositions.insert(std::make_pair(m_labelledBreakStatmentPositions[i].second, tryCatchWithBlockStatementCount()));
201208
}
202209
}
203210

204211
for (unsigned i = 0; i < m_labelledContinueStatmentPositions.size(); i++) {
205-
if (m_labelledContinueStatmentPositions[i].second > (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledContinueStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
212+
if (m_labelledContinueStatmentPositions[i].second >= (unsigned long)frontlimit && m_complexCaseStatementPositions.find(m_labelledContinueStatmentPositions[i].second) == m_complexCaseStatementPositions.end()) {
206213
m_complexCaseStatementPositions.insert(std::make_pair(m_labelledContinueStatmentPositions[i].second, tryCatchWithBlockStatementCount()));
207214
}
208215
}

src/interpreter/ByteCodeInterpreter.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,10 +1840,7 @@ NEVER_INLINE void InterpreterSlowPath::storeByName(ExecutionState& state, Lexica
18401840
NEVER_INLINE void InterpreterSlowPath::initializeByName(ExecutionState& state, LexicalEnvironment* env, const AtomicString& name, bool isLexicallyDeclaredName, const Value& value)
18411841
{
18421842
if (isLexicallyDeclaredName) {
1843-
auto result = state.lexicalEnvironment()->record()->hasBinding(state, name);
1844-
if (result.m_index != SIZE_MAX) {
1845-
state.lexicalEnvironment()->record()->initializeBinding(state, name, value);
1846-
}
1843+
state.lexicalEnvironment()->record()->initializeBinding(state, name, value);
18471844
} else {
18481845
while (env) {
18491846
if (env->record()->isVarDeclarationTarget()) {

0 commit comments

Comments
 (0)