Title: [186996] trunk/Source/_javascript_Core
Revision
186996
Author
[email protected]
Date
2015-07-18 13:12:14 -0700 (Sat, 18 Jul 2015)

Log Message

lexical scoping is broken with respect to "break" and "continue"
https://bugs.webkit.org/show_bug.cgi?id=147063

Reviewed by Filip Pizlo.

Bug #142944 which introduced "let" and lexical scoping
didn't properly hook into the bytecode generator's machinery
for calculating scope depth deltas for "break" and "continue". This
resulted in the bytecode generator popping an incorrect number
of scopes when lexical scopes were involved.

This patch fixes this problem and generalizes this machinery a bit.
This patch also renames old functions in a sensible way that is more
coherent in a world with lexical scoping.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::newLabelScope):
(JSC::BytecodeGenerator::emitProfileType):
(JSC::BytecodeGenerator::pushLexicalScope):
(JSC::BytecodeGenerator::popLexicalScope):
(JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
(JSC::BytecodeGenerator::resolveType):
(JSC::BytecodeGenerator::emitResolveScope):
(JSC::BytecodeGenerator::emitGetFromScope):
(JSC::BytecodeGenerator::emitPutToScope):
(JSC::BytecodeGenerator::emitPushWithScope):
(JSC::BytecodeGenerator::emitGetParentScope):
(JSC::BytecodeGenerator::emitPopScope):
(JSC::BytecodeGenerator::emitPopWithOrCatchScope):
(JSC::BytecodeGenerator::emitPopScopes):
(JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler):
(JSC::BytecodeGenerator::localScopeDepth):
(JSC::BytecodeGenerator::labelScopeDepth):
(JSC::BytecodeGenerator::emitThrowReferenceError):
(JSC::BytecodeGenerator::emitPushFunctionNameScope):
(JSC::BytecodeGenerator::pushScopedControlFlowContext):
(JSC::BytecodeGenerator::popScopedControlFlowContext):
(JSC::BytecodeGenerator::emitPushCatchScope):
(JSC::BytecodeGenerator::currentScopeDepth): Deleted.
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::hasFinaliser):
(JSC::BytecodeGenerator::scopeDepth): Deleted.
* bytecompiler/NodesCodegen.cpp:
(JSC::ContinueNode::trivialTarget):
(JSC::BreakNode::trivialTarget):
(JSC::ReturnNode::emitBytecode):
(JSC::WithNode::emitBytecode):
(JSC::TryNode::emitBytecode):
* tests/stress/lexical-scoping-break-continue.js: Added.
(assert):
(.):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (186995 => 186996)


--- trunk/Source/_javascript_Core/ChangeLog	2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-07-18 20:12:14 UTC (rev 186996)
@@ -1,3 +1,58 @@
+2015-07-18  Saam barati  <[email protected]>
+
+        lexical scoping is broken with respect to "break" and "continue"
+        https://bugs.webkit.org/show_bug.cgi?id=147063
+
+        Reviewed by Filip Pizlo.
+
+        Bug #142944 which introduced "let" and lexical scoping
+        didn't properly hook into the bytecode generator's machinery
+        for calculating scope depth deltas for "break" and "continue". This
+        resulted in the bytecode generator popping an incorrect number
+        of scopes when lexical scopes were involved.
+
+        This patch fixes this problem and generalizes this machinery a bit.
+        This patch also renames old functions in a sensible way that is more
+        coherent in a world with lexical scoping.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::newLabelScope):
+        (JSC::BytecodeGenerator::emitProfileType):
+        (JSC::BytecodeGenerator::pushLexicalScope):
+        (JSC::BytecodeGenerator::popLexicalScope):
+        (JSC::BytecodeGenerator::prepareLexicalScopeForNextForLoopIteration):
+        (JSC::BytecodeGenerator::resolveType):
+        (JSC::BytecodeGenerator::emitResolveScope):
+        (JSC::BytecodeGenerator::emitGetFromScope):
+        (JSC::BytecodeGenerator::emitPutToScope):
+        (JSC::BytecodeGenerator::emitPushWithScope):
+        (JSC::BytecodeGenerator::emitGetParentScope):
+        (JSC::BytecodeGenerator::emitPopScope):
+        (JSC::BytecodeGenerator::emitPopWithOrCatchScope):
+        (JSC::BytecodeGenerator::emitPopScopes):
+        (JSC::BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler):
+        (JSC::BytecodeGenerator::localScopeDepth):
+        (JSC::BytecodeGenerator::labelScopeDepth):
+        (JSC::BytecodeGenerator::emitThrowReferenceError):
+        (JSC::BytecodeGenerator::emitPushFunctionNameScope):
+        (JSC::BytecodeGenerator::pushScopedControlFlowContext):
+        (JSC::BytecodeGenerator::popScopedControlFlowContext):
+        (JSC::BytecodeGenerator::emitPushCatchScope):
+        (JSC::BytecodeGenerator::currentScopeDepth): Deleted.
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::hasFinaliser):
+        (JSC::BytecodeGenerator::scopeDepth): Deleted.
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ContinueNode::trivialTarget):
+        (JSC::BreakNode::trivialTarget):
+        (JSC::ReturnNode::emitBytecode):
+        (JSC::WithNode::emitBytecode):
+        (JSC::TryNode::emitBytecode):
+        * tests/stress/lexical-scoping-break-continue.js: Added.
+        (assert):
+        (.):
+
 2015-07-17  Filip Pizlo  <[email protected]>
 
         DFG should have some obvious mitigations against watching structures that are unprofitable to watch

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (186995 => 186996)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2015-07-18 20:12:14 UTC (rev 186996)
@@ -522,6 +522,8 @@
         instructions().append(0);
     }
 
+    if (m_lexicalEnvironmentRegister)
+        pushScopedControlFlowContext();
     m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(*m_vm, m_symbolTable), m_lexicalEnvironmentRegister, false, symbolTableConstantIndex });
     m_TDZStack.append(std::make_pair(*parentScopeTDZVariables, false));
 }
@@ -627,7 +629,7 @@
         m_labelScopes.removeLast();
 
     // Allocate new label scope.
-    LabelScope scope(type, name, scopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr<Label>()); // Only loops have continue targets.
+    LabelScope scope(type, name, labelScopeDepth(), newLabel(), type == LabelScope::Loop ? newLabel() : PassRefPtr<Label>()); // Only loops have continue targets.
     m_labelScopes.append(scope);
     return LabelScopePtr(m_labelScopes, m_labelScopes.size() - 1);
 }
@@ -1210,7 +1212,7 @@
     // The format of this instruction is: op_profile_type regToProfile, TypeLocation*, flag, identifier?, resolveType?
     emitOpcode(op_profile_type);
     instructions().append(registerToProfile->index());
-    instructions().append(currentScopeDepth());
+    instructions().append(localScopeDepth());
     instructions().append(flag);
     instructions().append(identifier ? addConstant(*identifier) : 0);
     instructions().append(resolveType());
@@ -1318,6 +1320,8 @@
         instructions().append(addConstantValue(jsTDZValue())->index());
 
         emitMove(scopeRegister(), newScope);
+
+        pushScopedControlFlowContext();
     }
 
     m_symbolTableStack.append(SymbolTableStackEntry{ symbolTable, newScope, false, symbolTableConstantIndex });
@@ -1365,8 +1369,8 @@
 
     if (hasCapturedVariables) {
         RELEASE_ASSERT(stackEntry.m_scope);
-        RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scopeRegister());
-        emitMove(scopeRegister(), parentScope.get());
+        emitPopScope(scopeRegister(), stackEntry.m_scope);
+        popScopedControlFlowContext();
         stackEntry.m_scope->deref();
     }
 
@@ -1423,7 +1427,7 @@
     // as the previous scope because the loop body is compiled under
     // the assumption that the scope's register index is constant even
     // though the value in that register will change on each loop iteration.
-    RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scopeRegister());
+    RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), loopScope);
     emitMove(scopeRegister(), parentScope.get());
 
     emitOpcode(op_create_lexical_environment);
@@ -1572,8 +1576,11 @@
 // will start with this ResolveType and compute the least upper bound including intercepting scopes.
 ResolveType BytecodeGenerator::resolveType()
 {
-    if (m_localScopeDepth)
-        return Dynamic;
+    for (unsigned i = m_symbolTableStack.size(); i--; ) {
+        if (m_symbolTableStack[i].m_isWithOrCatch)
+            return Dynamic;
+    }
+
     if (m_symbolTable && m_symbolTable->usesNonStrictEval())
         return GlobalPropertyWithVarInjectionChecks;
     return GlobalProperty;
@@ -1632,7 +1639,7 @@
         instructions().append(scopeRegister()->index());
         instructions().append(addConstant(variable.ident()));
         instructions().append(resolveType());
-        instructions().append(currentScopeDepth());
+        instructions().append(localScopeDepth());
         instructions().append(0);
         return dst;
     }
@@ -1666,7 +1673,7 @@
         instructions().append(scope->index());
         instructions().append(addConstant(variable.ident()));
         instructions().append(ResolveModeAndType(resolveMode, variable.offset().isScope() ? LocalClosureVar : resolveType()).operand());
-        instructions().append(currentScopeDepth());
+        instructions().append(localScopeDepth());
         instructions().append(variable.offset().isScope() ? variable.offset().scopeOffset().offset() : 0);
         instructions().append(profile);
         return dst;
@@ -1706,7 +1713,7 @@
         } else {
             ASSERT(resolveType() != LocalClosureVar);
             instructions().append(ResolveModeAndType(resolveMode, resolveType()).operand());
-            instructions().append(currentScopeDepth());
+            instructions().append(localScopeDepth());
         }
         instructions().append(!!offset ? offset.offset() : 0);
         return value;
@@ -2474,10 +2481,7 @@
 
 RegisterID* BytecodeGenerator::emitPushWithScope(RegisterID* dst, RegisterID* scope)
 {
-    ControlFlowContext context;
-    context.isFinallyBlock = false;
-    m_scopeContextStack.append(context);
-    m_localScopeDepth++;
+    pushScopedControlFlowContext();
 
     RegisterID* result = emitUnaryOp(op_push_with_scope, dst, scope);
     m_symbolTableStack.append(SymbolTableStackEntry{ Strong<SymbolTable>(), nullptr, true, 0 });
@@ -2492,16 +2496,16 @@
     return dst;
 }
 
-void BytecodeGenerator::emitPopScope(RegisterID* srcDst)
+void BytecodeGenerator::emitPopScope(RegisterID* dst, RegisterID* scope)
 {
-    ASSERT(m_scopeContextStack.size());
-    ASSERT(!m_scopeContextStack.last().isFinallyBlock);
+    RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), scope);
+    emitMove(dst, parentScope.get());
+}
 
-    RefPtr<RegisterID> parentScope = emitGetParentScope(newTemporary(), srcDst);
-    emitMove(srcDst, parentScope.get());
-
-    m_scopeContextStack.removeLast();
-    m_localScopeDepth--;
+void BytecodeGenerator::emitPopWithOrCatchScope(RegisterID* srcDst)
+{
+    emitPopScope(srcDst, srcDst);
+    popScopedControlFlowContext();
     SymbolTableStackEntry stackEntry = m_symbolTableStack.takeLast();
     RELEASE_ASSERT(stackEntry.m_isWithOrCatch);
 }
@@ -2815,9 +2819,9 @@
 
 void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth)
 {
-    ASSERT(scopeDepth() - targetScopeDepth >= 0);
+    ASSERT(labelScopeDepth() - targetScopeDepth >= 0);
 
-    size_t scopeDelta = scopeDepth() - targetScopeDepth;
+    size_t scopeDelta = labelScopeDepth() - targetScopeDepth;
     ASSERT(scopeDelta <= m_scopeContextStack.size());
     if (!scopeDelta)
         return;
@@ -2876,16 +2880,10 @@
 
 int BytecodeGenerator::calculateTargetScopeDepthForExceptionHandler() const
 {
-    int depth = m_localScopeDepth;
+    int depth = localScopeDepth();
 
-    for (unsigned i = m_symbolTableStack.size(); i--; ) {
-        RegisterID* scope = m_symbolTableStack[i].m_scope;
-        if (scope)
-            depth++;
-    }
-
     // Currently, we're maintaing compatibility with how things are done and letting the exception handling
-    // code take into consideration the base activation of the function. There is no reason we shouldn't 
+    // code take into consideration the base activation of the function. There is no reason we shouldn't
     // be able to calculate the exact depth here and let the exception handler not worry if there is a base
     // activation or not.
     if (m_lexicalEnvironmentRegister)
@@ -2895,18 +2893,16 @@
     return depth;
 }
 
-int BytecodeGenerator::currentScopeDepth() const
+int BytecodeGenerator::localScopeDepth() const
 {
-    // This is the current number of JSScope descendents that would be allocated
-    // in this function/program if this code were running.
-    int depth = 0;
-    for (unsigned i = m_symbolTableStack.size(); i--; ) {
-        if (m_symbolTableStack[i].m_scope || m_symbolTableStack[i].m_isWithOrCatch)
-            depth++;
-    }
-    return depth;
+    return m_localScopeDepth;
 }
 
+int BytecodeGenerator::labelScopeDepth() const
+{ 
+    return localScopeDepth() + m_finallyDepth;
+}
+
 void BytecodeGenerator::emitThrowReferenceError(const String& message)
 {
     emitOpcode(op_throw_static_error);
@@ -2930,13 +2926,26 @@
     instructions().append(JSNameScope::FunctionNameScope);
 }
 
-void BytecodeGenerator::emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes)
+void BytecodeGenerator::pushScopedControlFlowContext()
 {
     ControlFlowContext context;
     context.isFinallyBlock = false;
     m_scopeContextStack.append(context);
     m_localScopeDepth++;
+}
 
+void BytecodeGenerator::popScopedControlFlowContext()
+{
+    ASSERT(m_scopeContextStack.size());
+    ASSERT(!m_scopeContextStack.last().isFinallyBlock);
+    m_scopeContextStack.removeLast();
+    m_localScopeDepth--;
+}
+
+void BytecodeGenerator::emitPushCatchScope(RegisterID* dst, const Identifier& property, RegisterID* value, unsigned attributes)
+{
+    pushScopedControlFlowContext();
+
     emitOpcode(op_push_name_scope);
     instructions().append(dst->index());
     instructions().append(value->index());

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (186995 => 186996)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2015-07-18 20:12:14 UTC (rev 186996)
@@ -585,12 +585,12 @@
 
         void emitGetScope();
         RegisterID* emitPushWithScope(RegisterID* dst, RegisterID* scope);
-        void emitPopScope(RegisterID* srcDst);
+        void emitPopScope(RegisterID* dst, RegisterID* scope);
+        void emitPopWithOrCatchScope(RegisterID* srcDst);
         RegisterID* emitGetParentScope(RegisterID* dst, RegisterID* scope);
 
         void emitDebugHook(DebugHookID, unsigned line, unsigned charOffset, unsigned lineStart);
 
-        int scopeDepth() { return m_localScopeDepth + m_finallyDepth; }
         bool hasFinaliser() { return m_finallyDepth != 0; }
 
         void pushFinallyContext(StatementNode* finallyBlock);
@@ -624,6 +624,7 @@
         void pushLexicalScope(VariableEnvironmentNode*, bool canOptimizeTDZChecks, RegisterID** constantSymbolTableResult = nullptr);
         void popLexicalScope(VariableEnvironmentNode*);
         void prepareLexicalScopeForNextForLoopIteration(VariableEnvironmentNode*, RegisterID* loopSymbolTable);
+        int labelScopeDepth() const;
 
     private:
         void reclaimFreeRegisters();
@@ -761,7 +762,9 @@
         const CodeType m_codeType;
 
         int calculateTargetScopeDepthForExceptionHandler() const;
-        int currentScopeDepth() const;
+        int localScopeDepth() const;
+        void pushScopedControlFlowContext();
+        void popScopedControlFlowContext();
 
         Vector<ControlFlowContext, 0, UnsafeVectorOverflow> m_scopeContextStack;
         Vector<SwitchInfo> m_switchContextStack;

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (186995 => 186996)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2015-07-18 19:48:59 UTC (rev 186995)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2015-07-18 20:12:14 UTC (rev 186996)
@@ -2627,7 +2627,7 @@
     LabelScopePtr scope = generator.continueTarget(m_ident);
     ASSERT(scope);
 
-    if (generator.scopeDepth() != scope->scopeDepth())
+    if (generator.labelScopeDepth() != scope->scopeDepth())
         return 0;
 
     return scope->continueTarget();
@@ -2656,7 +2656,7 @@
     LabelScopePtr scope = generator.breakTarget(m_ident);
     ASSERT(scope);
 
-    if (generator.scopeDepth() != scope->scopeDepth())
+    if (generator.labelScopeDepth() != scope->scopeDepth())
         return 0;
 
     return scope->breakTarget();
@@ -2690,7 +2690,7 @@
         generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, nullptr);
         generator.emitTypeProfilerExpressionInfo(divotStart(), divotEnd());
     }
-    if (generator.scopeDepth()) {
+    if (generator.labelScopeDepth()) {
         returnRegister = generator.emitMove(generator.newTemporary(), returnRegister.get());
         generator.emitPopScopes(generator.scopeRegister(), 0);
     }
@@ -2714,7 +2714,7 @@
     generator.emitExpressionInfo(m_divot, m_divot - m_expressionLength, m_divot);
     generator.emitPushWithScope(generator.scopeRegister(), scope.get());
     generator.emitNode(dst, m_statement);
-    generator.emitPopScope(generator.scopeRegister());
+    generator.emitPopWithOrCatchScope(generator.scopeRegister());
 }
 
 // ------------------------------ CaseClauseNode --------------------------------
@@ -2967,7 +2967,7 @@
         generator.emitPushCatchScope(generator.scopeRegister(), m_thrownValueIdent, thrownValueRegister.get(), DontDelete);
         generator.emitProfileControlFlow(m_tryBlock->endOffset() + 1);
         generator.emitNode(dst, m_catchBlock);
-        generator.emitPopScope(generator.scopeRegister());
+        generator.emitPopWithOrCatchScope(generator.scopeRegister());
         generator.emitLabel(catchEndLabel.get());
     }
 

Added: trunk/Source/_javascript_Core/tests/stress/lexical-scoping-break-continue.js (0 => 186996)


--- trunk/Source/_javascript_Core/tests/stress/lexical-scoping-break-continue.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/lexical-scoping-break-continue.js	2015-07-18 20:12:14 UTC (rev 186996)
@@ -0,0 +1,216 @@
+function assert(b) {
+    if (!b)
+        throw new Error("bad assertion");
+}
+noInline(assert);
+
+;(function() {
+    function test1() {
+        let x = 20;
+        function foo() {
+            label: {
+                let y = 21;
+                let capY = function () { return y; }
+                assert(x === 20);
+                break label;
+            }
+            assert(x === 20);
+        }
+        foo();
+    }
+
+    function test2() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            label1: {
+                label2: {
+                    let y = 21;
+                    let capY = function () { return y; }
+                    break label2;
+                }
+                assert(x === 20);
+            }
+            assert(x === 20);
+
+            label1: {
+                label2: {
+                    let y = 21;
+                    let capY = function () { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    break label1;
+                }
+            }
+            assert(x === 20);
+
+            label1: {
+                let y = 21;
+                let capY = function () { return y; }
+                label2: {
+                    let y = 21;
+                    let capY = function () { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    break label1;
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    function test3() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            loop1: for (var i = 0; i++ < 1000; ) {
+                //assert(x === 20);
+                loop2: for (var j = 0; j++ < 1000; ) {
+                    let y = 21;
+                    let capY = function() { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    continue loop1;
+                    //break loop1;
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    function test4() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            loop1: for (var i = 0; i++ < 1000; ) {
+                loop2: for (var j = 0; j++ < 1000; ) {
+                    let y = 21;
+                    let capY = function() { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    break loop1;
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    function test5() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            loop1: for (var i = 0; i++ < 1000; ) {
+                let y = 21;
+                let capY = function() { return y; }
+                loop2: for (var j = 0; j++ < 1000; ) {
+                    let y = 21;
+                    let capY = function() { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    break loop1;
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    function test6() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            loop1: for (var i = 0; i++ < 1000; ) {
+                assert(x === 20);
+                let y = 21;
+                let capY = function() { return y; }
+                loop2: for (var j = 0; j++ < 1000; ) {
+                    let y = 21;
+                    let capY = function() { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    try {
+                        throw new Error();            
+                    } catch(e) {
+                    } finally {
+                        assert(x === 20);
+                        continue loop1;    
+                    }
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    function test7() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            loop1: for (var i = 0; i++ < 1000; ) {
+                assert(x === 20);
+                let y = 21;
+                let capY = function() { return y; }
+                loop2: for (var j = 0; j++ < 1000; ) {
+                    let y = 21;
+                    let capY = function() { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    try {
+                        throw new Error();            
+                    } catch(e) {
+                        continue loop1;
+                    } finally {
+                        let x = 40;
+                        let capX = function() { return x; }
+                        assert(x === 40);
+                    }
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    function test8() {
+        let x = 20;
+        function capX() { return x; }
+        function foo() {
+            loop1: for (var i = 0; i++ < 1000; ) {
+                assert(x === 20);
+                let y = 21;
+                let capY = function() { return y; }
+                loop2: for (var j = 0; j++ < 1000; ) {
+                    let y = 21;
+                    let capY = function() { return y; }
+                    assert(x === 20);
+                    assert(y === 21);
+                    try {
+                        throw new Error();            
+                    } catch(e) {
+                        break loop1;
+                    } finally {
+                        let x = 40;
+                        let capX = function() { return x; }
+                        assert(x === 40);
+                    }
+                }
+            }
+            assert(x === 20);
+        }
+        foo()
+    }
+
+    for (var i = 0; i < 1000; i++) {
+        test1();
+        test2();
+        test3();
+        test4();
+        test5();
+        test6();
+        test7();
+        test8();
+    }
+})();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to