Title: [242614] trunk/Source/_javascript_Core
Revision
242614
Author
mark....@apple.com
Date
2019-03-07 15:01:18 -0800 (Thu, 07 Mar 2019)

Log Message

Follow up refactoring in try-finally code after r242591.
https://bugs.webkit.org/show_bug.cgi?id=195428

Reviewed by Saam Barati.

1. Added some comments in emitFinallyCompletion() to describe each completion case.
2. Converted CatchEntry into a struct.
3. Renamed variable hasBreaksOrContinuesNotCoveredByJumps to hasBreaksOrContinuesThatEscapeCurrentFinally
   to be more clear about its purpose.

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::emitOutOfLineExceptionHandler):
(JSC::BytecodeGenerator::emitFinallyCompletion):
* bytecompiler/BytecodeGenerator.h:

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (242613 => 242614)


--- trunk/Source/_javascript_Core/ChangeLog	2019-03-07 22:41:17 UTC (rev 242613)
+++ trunk/Source/_javascript_Core/ChangeLog	2019-03-07 23:01:18 UTC (rev 242614)
@@ -1,3 +1,21 @@
+2019-03-07  Mark Lam  <mark....@apple.com>
+
+        Follow up refactoring in try-finally code after r242591.
+        https://bugs.webkit.org/show_bug.cgi?id=195428
+
+        Reviewed by Saam Barati.
+
+        1. Added some comments in emitFinallyCompletion() to describe each completion case.
+        2. Converted CatchEntry into a struct.
+        3. Renamed variable hasBreaksOrContinuesNotCoveredByJumps to hasBreaksOrContinuesThatEscapeCurrentFinally
+           to be more clear about its purpose.
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::generate):
+        (JSC::BytecodeGenerator::emitOutOfLineExceptionHandler):
+        (JSC::BytecodeGenerator::emitFinallyCompletion):
+        * bytecompiler/BytecodeGenerator.h:
+
 2019-03-07  Saam Barati  <sbar...@apple.com>
 
         CompactVariableMap::Handle's copy operator= leaks the previous data

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (242613 => 242614)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2019-03-07 22:41:17 UTC (rev 242613)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp	2019-03-07 23:01:18 UTC (rev 242614)
@@ -256,14 +256,14 @@
         emitUnreachable();
     }
 
-    for (auto& tuple : m_exceptionHandlersToEmit) {
+    for (auto& handler : m_exceptionHandlersToEmit) {
         Ref<Label> realCatchTarget = newLabel();
-        TryData* tryData = std::get<0>(tuple);
+        TryData* tryData = handler.tryData;
 
-        OpCatch::emit(this, std::get<1>(tuple), std::get<2>(tuple));
+        OpCatch::emit(this, handler.exceptionRegister, handler.thrownValueRegister);
         realCatchTarget->setLocation(*this, m_lastInstruction.offset());
-        if (std::get<3>(tuple).isValid()) {
-            RegisterID completionTypeRegister { std::get<3>(tuple) };
+        if (handler.completionTypeRegister.isValid()) {
+            RegisterID completionTypeRegister { handler.completionTypeRegister };
             CompletionType completionType =
                 tryData->handlerType == HandlerType::Finally || tryData->handlerType == HandlerType::SynthesizedFinally
                 ? CompletionType::Throw
@@ -3676,7 +3676,7 @@
 void BytecodeGenerator::emitOutOfLineExceptionHandler(RegisterID* exceptionRegister, RegisterID* thrownValueRegister, RegisterID* completionTypeRegister, TryData* data)
 {
     VirtualRegister completionTypeVirtualRegister = completionTypeRegister ? completionTypeRegister : VirtualRegister();
-    m_exceptionHandlersToEmit.append(CatchEntry { data, exceptionRegister, thrownValueRegister, completionTypeVirtualRegister });
+    m_exceptionHandlersToEmit.append({ data, exceptionRegister, thrownValueRegister, completionTypeVirtualRegister });
 }
 
 void BytecodeGenerator::restoreScopeRegister(int lexicalScopeIndex)
@@ -4776,10 +4776,26 @@
             auto& jump = context.jumps(i);
             emitJumpIf<OpNstricteq>(context.completionTypeRegister(), jump.jumpID, nextLabel.get());
 
-            // After a Break or Continue, we resume execution and may eventually complete with
-            // Normal completion (unless abruptly completed again). So, pre-emptively set the
-            // completion type to Normal. We can also set the completion value to undefined,
-            // but it will never be used for normal completion anyway. So, we'll skip setting it.
+            // This case is for Break / Continue completions from an inner finally context
+            // with a jump target that is not beyond the next outer finally context:
+            //
+            //     try {
+            //         for (... stuff ...) {
+            //             try {
+            //                 continue; // Sets completionType to jumpID of top of the for loop.
+            //             } finally {
+            //             } // Jump to top of the for loop on completion.
+            //         }
+            //     } finally {
+            //     }
+            //
+            // Since the jumpID is targetting a label that is inside the outer finally context,
+            // we can jump to it directly on completion of this finally context: there is no intermediate
+            // finally blocks to run. After the Break / Continue, we will contnue execution as normal.
+            // So, we'll set the completionType to Normal (on behalf of the target) before we jump.
+            // We can also set the completion value to undefined, but it will never be used for normal
+            // completion anyway. So, we'll skip setting it.
+
             restoreScopeRegister(jump.targetLexicalScopeIndex);
             emitLoad(context.completionTypeRegister(), CompletionType::Normal);
             emitJump(jump.targetLabel.get());
@@ -4793,9 +4809,22 @@
                 Ref<Label> isNotReturnLabel = newLabel();
                 emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Return, isNotReturnLabel.get());
 
-                // For Return completion, we need to pass the completion type and value to
-                // the outer finally so that it can return when it's done (unless interrupted
-                // by another abrupt completion).
+                // This case is for Return completion from an inner finally context:
+                //
+                //     try {
+                //         try {
+                //             return result; // Sets completionType to Return, and completionValue to result.
+                //         } finally {
+                //         } // Jump to outer finally on completion.
+                //     } finally {
+                //     }
+                //
+                // Since we know there's at least one outer finally context (beyond the current context),
+                // we cannot actually return from here. Instead, we pass the completionType and completionValue
+                // on to the next outer finally, and let it decide what to do next on its completion. The
+                // outer finally may or may not actual return depending on whether it encounters an abrupt
+                // completion in its body that overrrides this Return completion.
+
                 move(outerContext->completionTypeRegister(), context.completionTypeRegister());
                 move(outerContext->completionValueRegister(), context.completionValueRegister());
                 emitJump(*outerContext->finallyLabel());
@@ -4803,18 +4832,42 @@
                 emitLabel(isNotReturnLabel.get());
             }
 
-            bool hasBreaksOrContinuesNotCoveredByJumps = context.numberOfBreaksOrContinues() > numberOfJumps;
-            if (hasBreaksOrContinuesNotCoveredByJumps) {
+            bool hasBreaksOrContinuesThatEscapeCurrentFinally = context.numberOfBreaksOrContinues() > numberOfJumps;
+            if (hasBreaksOrContinuesThatEscapeCurrentFinally) {
                 Ref<Label> isThrowOrNormalLabel = newLabel();
                 emitJumpIf<OpBeloweq>(context.completionTypeRegister(), CompletionType::Throw, isThrowOrNormalLabel.get());
+
+                // A completionType above Throw means we have a Break or Continue encoded as a jumpID.
+                // We already ruled out Return above.
                 static_assert(CompletionType::Throw < CompletionType::Return && CompletionType::Throw < CompletionType::Return, "jumpIDs are above CompletionType::Return");
 
-                // Not Throw means we have a Break or Continue that should be handled by the outer context.
-                // These are for Break or Continue completions that have not reached their jump targets
-                // yet. The outer context needs to run its finally, and resume the jump outwards (unless
-                // interrupted by another abrupt completion). So, we need to pass the completion type to
-                // the outer finally. Again, we can skip the completion value because it's not used for
-                // Break nor Continue.
+                // This case is for Break / Continue completions in an inner finally context:
+                //
+                // 10: label:
+                // 11: try {
+                // 12:     try {
+                // 13:         for (... stuff ...)
+                // 14:             break label; // Sets completionType to jumpID of label.
+                // 15:     } finally {
+                // 16:     } // Jumps to outer finally on completion.
+                // 17:  } finally {
+                // 18:  }
+                //
+                // The break (line 14) says to continue execution at the label at line 10. Before we can
+                // goto line 10, the inner context's finally (line 15) needs to be run, followed by the
+                // outer context's finally (line 17). 'outerContext' being non-null above tells us that
+                // there is at least one outer finally context that we need to run after we complete the
+                // current finally. Note that unless the body of the outer finally abruptly completes in a
+                // different way, that outer finally also needs to complete with a Break / Continue to
+                // the same target label. Hence, we need to pass the jumpID in this finally's completionTypeRegister
+                // to the outer finally. The completion value for Break and Continue according to the spec
+                // is undefined, but it won't ever be used. So, we'll skip setting it.
+                //
+                // Note that all we're doing here is passing the Break / Continue completion to the next
+                // outer finally context. We don't worry about finally contexts beyond that. It is the
+                // responsibility of the next outer finally to determine what to do next at its completion,
+                // and pass on to the next outer context if present and needed.
+
                 move(outerContext->completionTypeRegister(), context.completionTypeRegister());
                 emitJump(*outerContext->finallyLabel());
 
@@ -4827,6 +4880,17 @@
                 Ref<Label> notReturnLabel = newLabel();
                 emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Return, notReturnLabel.get());
 
+                // This case is for Return completion from the outermost finally context:
+                //
+                //     try {
+                //         return result; // Sets completionType to Return, and completionValue to result.
+                //     } finally {
+                //     } // Executes the return of the completionValue.
+                //
+                // Since we know there's no outer finally context (beyond the current context) to run,
+                // we can actually execute a return for this Return completion. The value to return
+                // is whatever is in the completionValueRegister.
+
                 emitWillLeaveCallFrameDebugHook();
                 emitReturn(context.completionValueRegister(), ReturnFrom::Finally);
 
@@ -4835,13 +4899,21 @@
         }
     }
 
-    // Handle Throw or Normal completions.
+    // By now, we've rule out all Break / Continue / Return completions above. The only remaining
+    // possibilities are Normal or Throw.
+
     emitJumpIf<OpNstricteq>(context.completionTypeRegister(), CompletionType::Throw, normalCompletionLabel);
 
-    // For Throw, we just re-throw the previously caught exception captured in the completion value.
-    // The exception handler will set the completion type to Throw, and re-capture the completion
-    // value if needed (i.e. if the exception handler is for a finally). Hence, no need to set the
-    // completion type and value here.
+    // We get here because we entered this finally context with Throw completionType (i.e. we have
+    // an exception that we need to rethrow), and we didn't encounter a different abrupt completion
+    // that overrides that incoming completionType. All we have to do here is re-throw the exception
+    // captured in the completionValue.
+    //
+    // Note that unlike for Break / Continue / Return, we don't need to worry about outer finally
+    // contexts. This is because any outer finally context (if present) will have its own exception
+    // handler, which will take care of receiving the Throw completion, and re-capturing the exception
+    // in its completionValue.
+
     emitThrow(context.completionValueRegister());
 }
 

Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (242613 => 242614)


--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2019-03-07 22:41:17 UTC (rev 242613)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h	2019-03-07 23:01:18 UTC (rev 242614)
@@ -1278,7 +1278,12 @@
 
         CompactVariableMap::Handle m_cachedVariablesUnderTDZ;
 
-        using CatchEntry = std::tuple<TryData*, VirtualRegister, VirtualRegister, VirtualRegister>;
+        struct CatchEntry {
+            TryData* tryData;
+            VirtualRegister exceptionRegister;
+            VirtualRegister thrownValueRegister;
+            VirtualRegister completionTypeRegister;
+        };
         Vector<CatchEntry> m_exceptionHandlersToEmit;
     };
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to