Title: [126718] trunk
Revision
126718
Author
[email protected]
Date
2012-08-26 17:49:25 -0700 (Sun, 26 Aug 2012)

Log Message

Finally inlining should correctly track the catch context
https://bugs.webkit.org/show_bug.cgi?id=94986
<rdar://problem/11753784>

Reviewed by Sam Weinig.

Source/_javascript_Core: 

This fixes two behaviors:
        
1) Throwing from a finally block. Previously, we would seem to reenter the finally
   block - though only once.
        
2) Executing a finally block from some nested context, for example due to a
   'continue', 'break', or 'return' in the try. This would execute the finally
   block in the context of of the try block, which could lead to either scope depth
   mismatches or reexecutions of the finally block on throw, similarly to (1) but
   for different reasons.

* bytecompiler/BytecodeGenerator.cpp:
(JSC):
(JSC::BytecodeGenerator::pushFinallyContext):
(JSC::BytecodeGenerator::emitComplexJumpScopes):
(JSC::BytecodeGenerator::pushTry):
(JSC::BytecodeGenerator::popTryAndEmitCatch):
* bytecompiler/BytecodeGenerator.h:
(FinallyContext):
(TryData):
(JSC):
(TryContext):
(TryRange):
(BytecodeGenerator):
* bytecompiler/NodesCodegen.cpp:
(JSC::TryNode::emitBytecode):

LayoutTests: 

* fast/js/jsc-test-list:
* fast/js/script-tests/throw-from-finally.js: Added.
* fast/js/throw-from-finally.html: Added.
* fast/js/throw-from-finally-expected.txt: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (126717 => 126718)


--- trunk/LayoutTests/ChangeLog	2012-08-26 23:48:39 UTC (rev 126717)
+++ trunk/LayoutTests/ChangeLog	2012-08-27 00:49:25 UTC (rev 126718)
@@ -1,3 +1,16 @@
+2012-08-26  Filip Pizlo  <[email protected]>
+
+        Finally inlining should correctly track the catch context
+        https://bugs.webkit.org/show_bug.cgi?id=94986
+        <rdar://problem/11753784>
+
+        Reviewed by Sam Weinig.
+
+        * fast/js/jsc-test-list:
+        * fast/js/script-tests/throw-from-finally.js: Added.
+        * fast/js/throw-from-finally.html: Added.
+        * fast/js/throw-from-finally-expected.txt: Added.
+
 2012-08-26  Robert Hogan  <[email protected]>
 
         Suppress Mac test requiring rebaseline after r126683.

Modified: trunk/LayoutTests/fast/js/jsc-test-list (126717 => 126718)


--- trunk/LayoutTests/fast/js/jsc-test-list	2012-08-26 23:48:39 UTC (rev 126717)
+++ trunk/LayoutTests/fast/js/jsc-test-list	2012-08-27 00:49:25 UTC (rev 126718)
@@ -302,6 +302,7 @@
 fast/js/string-trim
 fast/js/string_replace
 fast/js/this-non-object-proto
+fast/js/throw-from-finally
 fast/js/ToNumber
 fast/js/toString-elision-trailing-comma
 fast/js/tostring-exception-in-property-access

Added: trunk/LayoutTests/fast/js/script-tests/throw-from-finally.js (0 => 126718)


--- trunk/LayoutTests/fast/js/script-tests/throw-from-finally.js	                        (rev 0)
+++ trunk/LayoutTests/fast/js/script-tests/throw-from-finally.js	2012-08-27 00:49:25 UTC (rev 126718)
@@ -0,0 +1,58 @@
+description(
+"This tests that throwing from a finally block has the expected effect."
+);
+
+var events = [];
+
+try {
+    events.push("1:try");
+} finally {
+    events.push("1:finally");
+}
+
+try {
+    try {
+        throw "2:thingy";
+    } finally {
+        events.push("2:finally");
+    }
+} catch (e) {
+    events.push(e);
+}
+
+try {
+    throw "3:thingy";
+} catch (e) {
+    events.push(e);
+} finally {
+    events.push("3:finally");
+}
+
+try {
+    try {
+        throw "4:thingy";
+    } catch (e) {
+        events.push(e);
+    } finally {
+        events.push("4:finally");
+        throw "4:another thingy";
+    }
+} catch (e) {
+    events.push(e);
+}
+
+try {
+    for (;;) {
+        try {
+            continue;
+        } finally {
+            events.push("5:hi");
+            throw "5:wat";
+        }
+    }
+} catch (e) {
+    events.push(e);
+}
+
+shouldBe("\"\" + events", "\"1:try,1:finally,2:finally,2:thingy,3:thingy,3:finally,4:thingy,4:finally,4:another thingy,5:hi,5:wat\"");
+

Added: trunk/LayoutTests/fast/js/throw-from-finally-expected.txt (0 => 126718)


--- trunk/LayoutTests/fast/js/throw-from-finally-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/js/throw-from-finally-expected.txt	2012-08-27 00:49:25 UTC (rev 126718)
@@ -0,0 +1,10 @@
+This tests that throwing from a finally block has the expected effect.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS "" + events is "1:try,1:finally,2:finally,2:thingy,3:thingy,3:finally,4:thingy,4:finally,4:another thingy,5:hi,5:wat"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/js/throw-from-finally.html (0 => 126718)


--- trunk/LayoutTests/fast/js/throw-from-finally.html	                        (rev 0)
+++ trunk/LayoutTests/fast/js/throw-from-finally.html	2012-08-27 00:49:25 UTC (rev 126718)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>

Modified: trunk/Source/_javascript_Core/ChangeLog (126717 => 126718)


--- trunk/Source/_javascript_Core/ChangeLog	2012-08-26 23:48:39 UTC (rev 126717)
+++ trunk/Source/_javascript_Core/ChangeLog	2012-08-27 00:49:25 UTC (rev 126718)
@@ -1,3 +1,38 @@
+2012-08-24  Filip Pizlo  <[email protected]>
+
+        Finally inlining should correctly track the catch context
+        https://bugs.webkit.org/show_bug.cgi?id=94986
+        <rdar://problem/11753784>
+
+        Reviewed by Sam Weinig.
+
+        This fixes two behaviors:
+        
+        1) Throwing from a finally block. Previously, we would seem to reenter the finally
+           block - though only once.
+        
+        2) Executing a finally block from some nested context, for example due to a
+           'continue', 'break', or 'return' in the try. This would execute the finally
+           block in the context of of the try block, which could lead to either scope depth
+           mismatches or reexecutions of the finally block on throw, similarly to (1) but
+           for different reasons.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC):
+        (JSC::BytecodeGenerator::pushFinallyContext):
+        (JSC::BytecodeGenerator::emitComplexJumpScopes):
+        (JSC::BytecodeGenerator::pushTry):
+        (JSC::BytecodeGenerator::popTryAndEmitCatch):
+        * bytecompiler/BytecodeGenerator.h:
+        (FinallyContext):
+        (TryData):
+        (JSC):
+        (TryContext):
+        (TryRange):
+        (BytecodeGenerator):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::TryNode::emitBytecode):
+
 2012-08-26  Filip Pizlo  <[email protected]>
 
         Array type checks and storage accesses should be uniformly represented and available to CSE

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (126717 => 126718)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2012-08-26 23:48:39 UTC (rev 126717)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2012-08-27 00:49:25 UTC (rev 126718)
@@ -188,6 +188,24 @@
 
     m_scopeNode->emitBytecode(*this);
     
+    for (unsigned i = 0; i < m_tryRanges.size(); ++i) {
+        TryRange& range = m_tryRanges[i];
+        ASSERT(range.tryData->targetScopeDepth != UINT_MAX);
+        HandlerInfo info = {
+            range.start->bind(0, 0), range.end->bind(0, 0),
+            range.tryData->target->bind(0, 0), range.tryData->targetScopeDepth
+#if ENABLE(JIT)
+            ,
+#if ENABLE(LLINT)
+            CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(bitwise_cast<void*>(&llint_op_catch)))
+#else
+            CodeLocationLabel()
+#endif
+#endif
+        };
+        m_codeBlock->addExceptionHandler(info);
+    }
+    
     m_codeBlock->instructions() = RefCountedArray<Instruction>(m_instructions);
 
     if (s_dumpsGeneratedCode)
@@ -2122,6 +2140,7 @@
         m_scopeContextStack.size(),
         m_switchContextStack.size(),
         m_forInContextStack.size(),
+        m_tryContextStack.size(),
         m_labelScopes.size(),
         m_finallyDepth,
         m_dynamicScopeDepth
@@ -2255,14 +2274,18 @@
         Vector<ControlFlowContext> savedScopeContextStack;
         Vector<SwitchInfo> savedSwitchContextStack;
         Vector<ForInContext> savedForInContextStack;
+        Vector<TryContext> poppedTryContexts;
         SegmentedVector<LabelScope, 8> savedLabelScopes;
         while (topScope > bottomScope && topScope->isFinallyBlock) {
+            RefPtr<Label> beforeFinally = emitLabel(newLabel().get());
+            
             // Save the current state of the world while instating the state of the world
             // for the finally block.
             FinallyContext finallyContext = topScope->finallyContext;
             bool flipScopes = finallyContext.scopeContextStackSize != m_scopeContextStack.size();
             bool flipSwitches = finallyContext.switchContextStackSize != m_switchContextStack.size();
             bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size();
+            bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size();
             bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size();
             int topScopeIndex = -1;
             int bottomScopeIndex = -1;
@@ -2280,6 +2303,19 @@
                 savedForInContextStack = m_forInContextStack;
                 m_forInContextStack.shrink(finallyContext.forInContextStackSize);
             }
+            if (flipTries) {
+                while (m_tryContextStack.size() != finallyContext.tryContextStackSize) {
+                    ASSERT(m_tryContextStack.size() > finallyContext.tryContextStackSize);
+                    TryContext context = m_tryContextStack.last();
+                    m_tryContextStack.removeLast();
+                    TryRange range;
+                    range.start = context.start;
+                    range.end = beforeFinally;
+                    range.tryData = context.tryData;
+                    m_tryRanges.append(range);
+                    poppedTryContexts.append(context);
+                }
+            }
             if (flipLabelScopes) {
                 savedLabelScopes = m_labelScopes;
                 while (m_labelScopes.size() > finallyContext.labelScopesSize)
@@ -2293,6 +2329,8 @@
             // Emit the finally block.
             emitNode(finallyContext.finallyBlock);
             
+            RefPtr<Label> afterFinally = emitLabel(newLabel().get());
+            
             // Restore the state of the world.
             if (flipScopes) {
                 m_scopeContextStack = savedScopeContextStack;
@@ -2303,6 +2341,14 @@
                 m_switchContextStack = savedSwitchContextStack;
             if (flipForIns)
                 m_forInContextStack = savedForInContextStack;
+            if (flipTries) {
+                ASSERT(m_tryContextStack.size() == finallyContext.tryContextStackSize);
+                for (unsigned i = poppedTryContexts.size(); i--;) {
+                    TryContext context = poppedTryContexts[i];
+                    context.start = afterFinally;
+                    m_tryContextStack.append(context);
+                }
+            }
             if (flipLabelScopes)
                 m_labelScopes = savedLabelScopes;
             m_finallyDepth = savedFinallyDepth;
@@ -2362,20 +2408,39 @@
     return dst;
 }
 
-RegisterID* BytecodeGenerator::emitCatch(RegisterID* targetRegister, Label* start, Label* end)
+TryData* BytecodeGenerator::pushTry(Label* start)
 {
+    TryData tryData;
+    tryData.target = newLabel();
+    tryData.targetScopeDepth = UINT_MAX;
+    m_tryData.append(tryData);
+    TryData* result = &m_tryData.last();
+    
+    TryContext tryContext;
+    tryContext.start = start;
+    tryContext.tryData = result;
+    
+    m_tryContextStack.append(tryContext);
+    
+    return result;
+}
+
+RegisterID* BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* targetRegister, Label* end)
+{
     m_usesExceptions = true;
-#if ENABLE(JIT)
-#if ENABLE(LLINT)
-    HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth, CodeLocationLabel(MacroAssemblerCodePtr::createFromExecutableAddress(bitwise_cast<void*>(&llint_op_catch))) };
-#else
-    HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth, CodeLocationLabel() };
-#endif
-#else
-    HandlerInfo info = { start->bind(0, 0), end->bind(0, 0), instructions().size(), m_dynamicScopeDepth + m_baseScopeDepth };
-#endif
+    
+    ASSERT_UNUSED(tryData, m_tryContextStack.last().tryData == tryData);
+    
+    TryRange tryRange;
+    tryRange.start = m_tryContextStack.last().start;
+    tryRange.end = end;
+    tryRange.tryData = m_tryContextStack.last().tryData;
+    m_tryRanges.append(tryRange);
+    m_tryContextStack.removeLast();
+    
+    emitLabel(tryRange.tryData->target.get());
+    tryRange.tryData->targetScopeDepth = m_dynamicScopeDepth + m_baseScopeDepth;
 
-    m_codeBlock->addExceptionHandler(info);
     emitOpcode(op_catch);
     instructions().append(targetRegister->index());
     return targetRegister;

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (126717 => 126718)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2012-08-26 23:48:39 UTC (rev 126717)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2012-08-27 00:49:25 UTC (rev 126718)
@@ -75,6 +75,7 @@
         unsigned scopeContextStackSize;
         unsigned switchContextStackSize;
         unsigned forInContextStackSize;
+        unsigned tryContextStackSize;
         unsigned labelScopesSize;
         int finallyDepth;
         int dynamicScopeDepth;
@@ -92,6 +93,22 @@
         RefPtr<RegisterID> propertyRegister;
     };
 
+    struct TryData {
+        RefPtr<Label> target;
+        unsigned targetScopeDepth;
+    };
+
+    struct TryContext {
+        RefPtr<Label> start;
+        TryData* tryData;
+    };
+
+    struct TryRange {
+        RefPtr<Label> start;
+        RefPtr<Label> end;
+        TryData* tryData;
+    };
+
     class ResolveResult {
     public:
         enum Flags {
@@ -492,7 +509,11 @@
         RegisterID* emitGetPropertyNames(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, Label* breakTarget);
         RegisterID* emitNextPropertyName(RegisterID* dst, RegisterID* base, RegisterID* i, RegisterID* size, RegisterID* iter, Label* target);
 
-        RegisterID* emitCatch(RegisterID*, Label* start, Label* end);
+        // Start a try block. 'start' must have been emitted.
+        TryData* pushTry(Label* start);
+        // End a try block. 'end' must have been emitted.
+        RegisterID* popTryAndEmitCatch(TryData*, RegisterID* targetRegister, Label* end);
+
         void emitThrow(RegisterID* exc)
         { 
             m_usesExceptions = true;
@@ -634,7 +655,9 @@
 
         RegisterID* emitInitLazyRegister(RegisterID*);
 
+    public:
         Vector<Instruction>& instructions() { return m_instructions; }
+
         SymbolTable& symbolTable() { return *m_symbolTable; }
 #if ENABLE(BYTECODE_COMMENTS)
         Vector<Comment>& comments() { return m_comments; }
@@ -708,6 +731,10 @@
         Vector<ControlFlowContext> m_scopeContextStack;
         Vector<SwitchInfo> m_switchContextStack;
         Vector<ForInContext> m_forInContextStack;
+        Vector<TryContext> m_tryContextStack;
+        
+        Vector<TryRange> m_tryRanges;
+        SegmentedVector<TryData, 8> m_tryData;
 
         int m_firstConstantIndex;
         int m_nextConstantOffset;

Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (126717 => 126718)


--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2012-08-26 23:48:39 UTC (rev 126717)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp	2012-08-27 00:49:25 UTC (rev 126718)
@@ -1970,11 +1970,15 @@
 
     generator.emitDebugHook(WillExecuteStatement, firstLine(), lastLine(), column());
 
+    ASSERT(m_catchBlock || m_finallyBlock);
+
     RefPtr<Label> tryStartLabel = generator.newLabel();
+    generator.emitLabel(tryStartLabel.get());
+    
     if (m_finallyBlock)
         generator.pushFinallyContext(m_finallyBlock);
+    TryData* tryData = generator.pushTry(tryStartLabel.get());
 
-    generator.emitLabel(tryStartLabel.get());
     generator.emitNode(dst, m_tryBlock);
 
     if (m_catchBlock) {
@@ -1985,7 +1989,14 @@
 
         // Uncaught exception path: the catch block.
         RefPtr<Label> here = generator.emitLabel(generator.newLabel().get());
-        RefPtr<RegisterID> exceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get());
+        RefPtr<RegisterID> exceptionRegister = generator.popTryAndEmitCatch(tryData, generator.newTemporary(), here.get());
+        
+        if (m_finallyBlock) {
+            // If the catch block throws an exception and we have a finally block, then the finally
+            // block should "catch" that exception.
+            tryData = generator.pushTry(here.get());
+        }
+        
         generator.emitPushNewScope(exceptionRegister.get(), m_exceptionIdent, exceptionRegister.get());
         generator.emitNode(dst, m_catchBlock);
         generator.emitPopScope();
@@ -1993,6 +2004,8 @@
     }
 
     if (m_finallyBlock) {
+        RefPtr<Label> preFinallyLabel = generator.emitLabel(generator.newLabel().get());
+        
         generator.popFinallyContext();
 
         RefPtr<Label> finallyEndLabel = generator.newLabel();
@@ -2002,8 +2015,7 @@
         generator.emitJump(finallyEndLabel.get());
 
         // Uncaught exception path: invoke the finally block, then re-throw the exception.
-        RefPtr<Label> here = generator.emitLabel(generator.newLabel().get());
-        RefPtr<RegisterID> tempExceptionRegister = generator.emitCatch(generator.newTemporary(), tryStartLabel.get(), here.get());
+        RefPtr<RegisterID> tempExceptionRegister = generator.popTryAndEmitCatch(tryData, generator.newTemporary(), preFinallyLabel.get());
         generator.emitNode(dst, m_finallyBlock);
         generator.emitThrow(tempExceptionRegister.get());
 
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to