<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[209952] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/209952">209952</a></dd>
<dt>Author</dt> <dd>mark.lam@apple.com</dd>
<dt>Date</dt> <dd>2016-12-16 17:06:49 -0800 (Fri, 16 Dec 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>
JSTests:
De-duplicate finally blocks.
https://bugs.webkit.org/show_bug.cgi?id=160168

Reviewed by Keith Miller.

* stress/deeply-nested-finallys.js: Added.
- Tests many levels of finally nesting.  This causes the old code to hang (and
  crashes eventually) while trying to generate bytecode for the exponentially
  duplicated finally blocks.  The new code completes this test almost instantly.

* stress/test-finally.js: Added.
- Tests control flow through various permutations of finally blocks.

Source/JavaScriptCore:
De-duplicate finally blocks.
https://bugs.webkit.org/show_bug.cgi?id=160168

Reviewed by Keith Miller.

JS execution can arrive at a finally block when there are abrupt completions from
its try or catch block.  The abrupt completion types include Break,
Continue, Return, and Throw.  The non-abrupt completion type is called Normal
(i.e. the case of a try block falling through to the finally block).

Previously, we enable each of these paths for abrupt completion (except for Throw)
to run the finally block code by duplicating the finally block code at each of
the sites that trigger those completions.  This patch fixes the implementation so
that each of these abrupt completions will set a finallyActionRegister (plus a
finallyReturnValueRegister for CompletionType::Return) and then jump to the
relevant finally blocks, and continue to thread through subsequent outer finally
blocks until execution reaches the outermost finally block that the completion
type dictates.  We no longer duplicate the finally block code.

The implementation details:
1. We allocate a pair of finallyActionRegister and finallyReturnValueRegister
   just before entering the outermost try-catch-finally scope.

   On allocating the registers, we set them to the empty JSValue.  This serves
   to set the completion type to CompletionType::Normal (see (2) below).

2. The finallyActionRegister serves 2 purpose:
   a. indicates the CompletionType that triggered entry into the finally block.

      This is how we encode the completion type in the finallyActionRegister:
      1. CompletionType::Normal
         - finallyActionRegister is set to the empty JSValue.
      2. CompletionType::Break
         - finallyActionRegister is set to the int jumpID for the site of the break statement.
      3. CompletionType::Continue
         - finallyActionRegister is set to the int jumpID for the site of the continue statement.
      4. CompletionType::Return
         - finallyActionRegister is set to CompletionType::Return as an int JSValue.
         - finallyReturnValueRegister is set to the value to be returned. 
      5. CompletionType::Throw
         - finallyActionRegister is set to the exception object that was caught by the finally block.

      Hence, if the finallyActionRegister can either be:
      1. empty i.e. we're handling CompletionType::Normal.
      2. an int JSValue i.e. we're handling CompletionType::Break, Continue, or Return.
      3. an object i.e. we're handling CompletionType::Throw.

   b. stores the exception caught in the finally block if we're handing
      CompletionType::Throw.

3. Each finally block will have 2 entries:
   a. the entry via throw.
   b. the normal entry.

   The entry via throw is recorded in the codeBlock's exception table, and can
   only be jumped to by the VM's exception handling mechanism.

   The normal entry is recorded in a FinallyContext (at bytecode generation time
   only) and is jumped to when we want enter the finally block due any of the
   other CompletionTypes.

4. CompletionType::Normal
   ======================
   We encounter this when falling through from a try or catch block to the finally block.  
           
   For the try block case, since finallyActionRegister is set to Normal by default,
   there's nothing more that needs to be done.

   For the catch block case, since we entered the catch block with an exception,
   finallyActionRegister may be set to Throw.  We'll need to set it to Normal
   before jumping to the finally block's normal entry.

   CompletionType::Break
   =====================
   When we emit bytecode for the BreakNode, we check if we have any FinallyContexts
   that we need to service before jumping to the breakTarget.  If we do, then:
   a. we'll register a jumpID along with the breakTarget with the outermost FinallyContext.
   b. we'll also increment the numberOfBreaksOrContinues count in each FinallyContext
      from the innermost to the outermost.
   c. instead of emitting bytecode to jump to the breakTarget, we:
      1. emit bytecode to set finallyActionRegister to the jumpID.
      b. emit bytecode to jump to the normal entry of the innermost finally block.

   Each finally block will take care of cascading to the next outer finally block
   as needed (see (5) below).

   CompletionType::Continue
   ========================
   Since continues and breaks work the same way (i.e. with a jump), we handle this
   exactly the same way as CompletionType::Break, except that we use the
   continueTarget instead of the breakTarget.

   CompletionType::Return
   ======================
   When we emit bytecode for the ReturnNode, we check if we have any FinallyContexts
   at all on the m_controlFlowScopeStack.

   If so, then instead of emitting op_ret, we:
      1. emit bytecode to set finallyActionRegister to the CompletionType::Return.
      1. emit bytecode to move the return value into finallyReturnValueRegister.
      2. emit bytecode to jump to the normal entry of the innermost finally block.

   Each finally block will take care of cascading to the next outer finally block
   as needed (see (5) below).

   CompletionType::Throw
   ======================
   The op_catch of a finally block will always store the caught exception object
   in the finallyActionRegister.  This means we're handling CompletionType::Throw
   (see (2) above).

5. What happens in each finally block?
   ==================================
   Only the finally block's entry via throw will have an op_catch that catches the
   pending exception (and stores it in the finallyActionRegister).  This throw
   entry then falls through to the normal entry.

   The finally block's normal entry will restore the scope of the finally block
   and proceed to execute its code.

   At the end of the finally block (see emitFinallyCompletion()), the finally
   block will check the finallyActionRegister for each completion type in the
   following order:
           
   a. CompletionType::Normal: jump to the code after the finally block as
      designated by a normalCompletion label.

   b. CompletionType::Break and Continue:
      If the FinallyContext for this block has registered FinallyJumps, we'll
      check for the jumpIDs against the finallyActionRegister.  If the jumpID
      matches, jump to the corresponding jumpTarget.

      If no jumpIDs match but the FinallyContext's numberOfBreaksOrContinues is
      greater than the number of registered FinallyJumps, then this means that
      we have a Break or Continue that needs to be handled by an outer finally
      block.  In that case, jump to the outer finally block's normal entry.
              
   c. CompletionType::Return:
      If this finally block is not the outermost and finallyActionRegister contains
      CompletionType::Return, then jump to the outer finally block's normal entry.

      Otherwise, if this finally block is the outermost and finallyActionRegister
      contains CompletionType::Return, then execute op_ret and return the value
      in finallyReturnValueRegister.

   d. CompletionType::Throw:
      If we're not handling any of the above cases, then just throw the
      finallyActionRegister which contains the exception to re-throw.

6. restoreScopeRegister()
        
   Since the needed scope objects are always stored in a local, we can restore
   the scope register by simply moving from that local instead of going through
   op_get_parent_scope.

7. m_controlFlowScopeStack needs to be a SegmentedVector instead of a Vector.
   This makes it easier to keep a pointer to the FinallyContext on that stack,
   and not have to worry about the vector being realloc'ed due to resizing. 

Performance appears to be neutral both on ES6SampleBench (run via cli) and the
JSC benchmarks.

Relevant spec references:
https://tc39.github.io/ecma262/#sec-completion-record-specification-type
https://tc39.github.io/ecma262/#sec-try-statement-runtime-semantics-evaluation

* bytecode/HandlerInfo.h:
(JSC::HandlerInfoBase::typeName):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::generate):
(JSC::BytecodeGenerator::BytecodeGenerator):
(JSC::BytecodeGenerator::emitReturn):
(JSC::BytecodeGenerator::pushFinallyControlFlowScope):
(JSC::BytecodeGenerator::popFinallyControlFlowScope):
(JSC::BytecodeGenerator::allocateAndEmitScope):
(JSC::BytecodeGenerator::pushTry):
(JSC::BytecodeGenerator::popTry):
(JSC::BytecodeGenerator::emitCatch):
(JSC::BytecodeGenerator::restoreScopeRegister):
(JSC::BytecodeGenerator::labelScopeDepthToLexicalScopeIndex):
(JSC::BytecodeGenerator::labelScopeDepth):
(JSC::BytecodeGenerator::pushLocalControlFlowScope):
(JSC::BytecodeGenerator::popLocalControlFlowScope):
(JSC::BytecodeGenerator::emitEnumeration):
(JSC::BytecodeGenerator::emitIsNumber):
(JSC::BytecodeGenerator::emitYield):
(JSC::BytecodeGenerator::emitDelegateYield):
(JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded):
(JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded):
(JSC::BytecodeGenerator::emitFinallyCompletion):
(JSC::BytecodeGenerator::allocateFinallyRegisters):
(JSC::BytecodeGenerator::releaseFinallyRegisters):
(JSC::BytecodeGenerator::emitCompareFinallyActionAndJumpIf):
(JSC::BytecodeGenerator::pushIteratorCloseControlFlowScope): Deleted.
(JSC::BytecodeGenerator::popIteratorCloseControlFlowScope): Deleted.
(JSC::BytecodeGenerator::emitComplexPopScopes): Deleted.
(JSC::BytecodeGenerator::emitPopScopes): Deleted.
(JSC::BytecodeGenerator::popTryAndEmitCatch): Deleted.
* bytecompiler/BytecodeGenerator.h:
(JSC::FinallyJump::FinallyJump):
(JSC::FinallyContext::FinallyContext):
(JSC::FinallyContext::outerContext):
(JSC::FinallyContext::finallyLabel):
(JSC::FinallyContext::depth):
(JSC::FinallyContext::numberOfBreaksOrContinues):
(JSC::FinallyContext::incNumberOfBreaksOrContinues):
(JSC::FinallyContext::handlesReturns):
(JSC::FinallyContext::setHandlesReturns):
(JSC::FinallyContext::registerJump):
(JSC::FinallyContext::numberOfJumps):
(JSC::FinallyContext::jumps):
(JSC::ControlFlowScope::ControlFlowScope):
(JSC::ControlFlowScope::isLabelScope):
(JSC::ControlFlowScope::isFinallyScope):
(JSC::BytecodeGenerator::currentLexicalScopeIndex):
(JSC::BytecodeGenerator::FinallyRegistersScope::FinallyRegistersScope):
(JSC::BytecodeGenerator::FinallyRegistersScope::~FinallyRegistersScope):
(JSC::BytecodeGenerator::finallyActionRegister):
(JSC::BytecodeGenerator::finallyReturnValueRegister):
(JSC::BytecodeGenerator::emitSetFinallyActionToNormalCompletion):
(JSC::BytecodeGenerator::emitSetFinallyActionToReturnCompletion):
(JSC::BytecodeGenerator::emitSetFinallyActionToJumpID):
(JSC::BytecodeGenerator::emitSetFinallyReturnValueRegister):
(JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNormalCompletion):
(JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotJump):
(JSC::BytecodeGenerator::emitJumpIfFinallyActionIsReturnCompletion):
(JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotReturnCompletion):
(JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotThrowCompletion):
(JSC::BytecodeGenerator::emitJumpIfCompletionTypeIsThrow):
(JSC::BytecodeGenerator::bytecodeOffsetToJumpID):
(JSC::BytecodeGenerator::isInFinallyBlock): Deleted.
* bytecompiler/NodesCodegen.cpp:
(JSC::ContinueNode::emitBytecode):
(JSC::BreakNode::emitBytecode):
(JSC::ReturnNode::emitBytecode):
(JSC::TryNode::emitBytecode):

Source/WTF:
Add predecessor info to dumps from JSC_dumpBytecodeLivenessResults=true.
https://bugs.webkit.org/show_bug.cgi?id=165958

Reviewed by Keith Miller.

Added some methods to bring SegmentedVector closer to parity with Vector.

* wtf/SegmentedVector.h:
(WTF::SegmentedVector::first):
(WTF::SegmentedVector::last):
(WTF::SegmentedVector::takeLast):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecodeHandlerInfoh">trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerNodesCodegencpp">trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfSegmentedVectorh">trunk/Source/WTF/wtf/SegmentedVector.h</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressdeeplynestedfinallysjs">trunk/JSTests/stress/deeply-nested-finallys.js</a></li>
<li><a href="#trunkJSTestsstresstestfinallyjs">trunk/JSTests/stress/test-finally.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/JSTests/ChangeLog        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -1,3 +1,18 @@
</span><ins>+2016-12-16  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        De-duplicate finally blocks.
+        https://bugs.webkit.org/show_bug.cgi?id=160168
+
+        Reviewed by Keith Miller.
+
+        * stress/deeply-nested-finallys.js: Added.
+        - Tests many levels of finally nesting.  This causes the old code to hang (and
+          crashes eventually) while trying to generate bytecode for the exponentially
+          duplicated finally blocks.  The new code completes this test almost instantly.
+
+        * stress/test-finally.js: Added.
+        - Tests control flow through various permutations of finally blocks.
+
</ins><span class="cx"> 2016-12-16  Saam Barati  &lt;sbarati@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebAssembly: WasmB3IRGenerator should throw exceptions instead of crash
</span></span></pre></div>
<a id="trunkJSTestsstressdeeplynestedfinallysjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/deeply-nested-finallys.js (0 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/deeply-nested-finallys.js                                (rev 0)
+++ trunk/JSTests/stress/deeply-nested-finallys.js        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -0,0 +1,78 @@
</span><ins>+// This test should finish almost instantly.
+
+function exp() {
+    try {
+        try {
+            try {
+                try { 
+                    try {
+                        try {
+                            try {
+                                try {
+                                    try {
+                                        try {
+                                            try {
+                                                try {
+                                                    try { 
+                                                        try {
+                                                            try {
+                                                                try {
+                                                                    try {
+                                                                        try {
+                                                                            try {
+                                                                                try {
+                                                                                    try {
+                                                                                        try { 
+                                                                                            try {
+                                                                                                try {
+                                                                                                    try {
+                                                                                                        try {
+                                                                                                            try {
+                                                                                                                try {
+                                                                                                                    try {
+                                                                                                                        try {
+                                                                                                                            try { 
+                                                                                                                                try {
+                                                                                                                                    try {
+                                                                                                                                        try {
+                                                                                                                                            try {
+                                                                                                                                                try {
+                                                                                                                                                } finally {return 1;}
+                                                                                                                                            } finally { return 1; }
+                                                                                                                                        } finally {return 1;}
+                                                                                                                                    } finally { return 1; }
+                                                                                                                                } finally {return 1;}
+                                                                                                                            } finally {return 1;}
+                                                                                                                        } finally {return 1;}
+                                                                                                                    } finally {return 1;}    
+                                                                                                                } finally { return 1; }
+                                                                                                            } finally {return 1;}
+                                                                                                        } finally { return 1; }
+                                                                                                    } finally {return 1;}
+                                                                                                } finally { return 1; }
+                                                                                            } finally {return 1;}
+                                                                                        } finally {return 1;}
+                                                                                    } finally {return 1;}
+                                                                                } finally {return 1;}    
+                                                                            } finally { return 1; }
+                                                                        } finally {return 1;}
+                                                                    } finally { return 1; }
+                                                                } finally {return 1;}
+                                                            } finally { return 1; }
+                                                        } finally {return 1;}
+                                                    } finally {return 1;}
+                                                } finally {return 1;}
+                                            } finally {return 1;}    
+                                        } finally { return 1; }
+                                    } finally {return 1;}
+                                } finally { return 1; }
+                            } finally {return 1;}
+                        } finally { return 1; }
+                    } finally {return 1;}
+                } finally {return 1;}
+            } finally {return 1;}
+        } finally {return 1;}    
+    } finally { return 1; }
+}
+
+exp();
</ins></span></pre></div>
<a id="trunkJSTestsstresstestfinallyjs"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/test-finally.js (0 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/test-finally.js                                (rev 0)
+++ trunk/JSTests/stress/test-finally.js        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -0,0 +1,1018 @@
</span><ins>+// This test just creates functions which exercise various permutations of control flow
+// thru finally blocks. The test passes if it does not throw any exceptions or crash on
+// bytecode validation.
+
+if (this.window)
+    print = console.log;
+
+var steps;
+var returned;
+var thrown;
+var testLineNumber;
+
+let NothingReturned = &quot;NothingReturned&quot;;
+let NothingThrown = &quot;NothingThrown&quot;;
+
+function assertResults(expectedSteps, expectedReturned, expectedThrown) {
+    let stepsMismatch = (steps != expectedSteps);
+    let returnedMismatch = (returned != expectedReturned);
+    let thrownMismatch = (thrown != expectedThrown &amp;&amp; !(&quot;&quot; + thrown).startsWith(&quot;&quot; + expectedThrown));
+
+    if (stepsMismatch || returnedMismatch || thrownMismatch) {
+        print(&quot;FAILED Test @ line &quot; + testLineNumber + &quot;:&quot;);
+        print(&quot;   Actual:   steps: &quot; + steps + &quot;, returned: &quot; + returned + &quot;, thrown: '&quot; + thrown + &quot;'&quot;);
+        print(&quot;   Expected: steps: &quot; + expectedSteps + &quot;, returned: &quot; + expectedReturned + &quot;, thrown: '&quot; + expectedThrown + &quot;'&quot;);
+    }
+    if (stepsMismatch)
+        throw &quot;FAILED Test @ line &quot; + testLineNumber + &quot;: steps do not match: expected ='&quot; + expectedSteps + &quot;' actual='&quot; + steps + &quot;'&quot;;
+    if (returnedMismatch)
+        throw &quot;FAILED Test @ line &quot; + testLineNumber + &quot;: returned value does not match: expected ='&quot; + expectedReturned + &quot;' actual='&quot; + returned + &quot;'&quot;;
+    if (thrownMismatch)
+        throw &quot;FAILED Test @ line &quot; + testLineNumber + &quot;: thrown value does does not match: expected ='&quot; + expectedThrown + &quot;' actual='&quot; + thrown + &quot;'&quot;;
+}
+
+function resetResults() {
+    steps = [];
+    returned = NothingReturned;
+    thrown = NothingThrown;
+}
+
+function append(step) {
+    let next = steps.length;
+    steps[next] = step;
+}
+
+function test(func, expectedSteps, expectedReturned, expectedThrown) {
+    testLineNumber = new Error().stack.match(/global code@.+\.js:([0-9]+)/)[1];
+    resetResults();
+
+    try {
+        returned = func();
+    } catch (e) {
+        thrown = e;
+    }
+
+    assertResults(expectedSteps, expectedReturned, expectedThrown);
+}
+
+// Test CompletionType::Normal on an empty try blocks.
+test(() =&gt;  {
+    try {
+        append(&quot;t2&quot;);
+        for (var i = 0; i &lt; 2; i++) {
+            try {
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,f1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Normal.
+test(() =&gt;  {
+    try {
+        append(&quot;t2&quot;);
+        for (var i = 0; i &lt; 2; i++) {
+            try {
+                append(&quot;t1&quot;);
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Break.
+test(() =&gt;  {
+    try {
+        append(&quot;t2&quot;);
+        for (var i = 0; i &lt; 2; i++) {
+            try {
+                append(&quot;t1&quot;);
+                break;
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Continue.
+test(() =&gt;  {
+    try {
+        append(&quot;t2&quot;);
+        for (var i = 0; i &lt; 2; i++) {
+            try {
+                append(&quot;t1&quot;);
+                continue;
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Return.
+test(() =&gt;  {
+    try {
+        append(&quot;t2&quot;);
+        for (var i = 0; i &lt; 2; i++) {
+            try {
+                append(&quot;t1&quot;);
+                return;
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Throw.
+test(() =&gt;  {
+    try {
+        append(&quot;t2&quot;);
+        for (var i = 0; i &lt; 2; i++) {
+            try {
+                append(&quot;t1&quot;);
+                throw { };
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,c1,f1,t1,c1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Normal in a for-of loop.
+test(() =&gt;  {
+    let arr = [1, 2];
+    try {
+        append(&quot;t2&quot;);
+        for (let x of arr) {
+            try {
+                append(&quot;t1&quot;);
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Break in a for-of loop.
+test(() =&gt;  {
+    let arr = [1, 2];
+    try {
+        append(&quot;t2&quot;);
+        for (let x of arr) {
+            try {
+                append(&quot;t1&quot;);
+                break;
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Continue in a for-of loop.
+test(() =&gt;  {
+    let arr = [1, 2];
+    try {
+        append(&quot;t2&quot;);
+        for (let x of arr) {
+            try {
+                append(&quot;t1&quot;);
+                continue;
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Return in a for-of loop.
+test(() =&gt;  {
+    let arr = [1, 2];
+    try {
+        append(&quot;t2&quot;);
+        for (let x of arr) {
+            try {
+                append(&quot;t1&quot;);
+                return;
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,f1,f2&quot;, undefined, NothingThrown);
+
+// Test CompletionType::Throw in a for-of loop.
+test(() =&gt;  {
+    let arr = [1, 2];
+    try {
+        append(&quot;t2&quot;);
+        for (let x of arr) {
+            try {
+                append(&quot;t1&quot;);
+                throw { };
+            } catch(a) {
+                append(&quot;c1&quot;);
+            } finally {
+                append(&quot;f1&quot;);
+            }
+        }
+    } catch(b) {
+        append(&quot;c2&quot;);
+    } finally {
+        append(&quot;f2&quot;);
+    }
+
+}, &quot;t2,t1,c1,f1,t1,c1,f1,f2&quot;, undefined, NothingThrown);
+
+
+// No abrupt completions.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+    } finally {
+        append(&quot;c&quot;);
+    }   
+    append(&quot;d&quot;);
+    return 400;
+
+}, &quot;a,b,c,d&quot;, 400, NothingThrown);
+
+
+// Return after a. Should not execute any finally blocks, and return a's result.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    return 100;
+    try {
+        append(&quot;b&quot;);
+        return 200;
+        try {
+            append(&quot;c&quot;);
+            return 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        return 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a&quot;, 100, NothingThrown);
+
+// Return after b. Should execute finally block f only, and return b's result.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        return 200;
+        try {
+            append(&quot;c&quot;);
+            return 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        return 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,f&quot;, 200, NothingThrown);
+
+// Return after c. Should execute finally blocks d and f, and return c's result.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            return 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        return 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,d,f&quot;, 300, NothingThrown);
+
+// Return after c and d. Should execute finally blocks d and f, and return last return value from d.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            return 300;
+        } finally {
+            append(&quot;d&quot;);
+            return 400;
+        }
+        append(&quot;e&quot;);
+        return 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,d,f&quot;, 400, NothingThrown);
+
+// Return after c, d, and f. Should execute finally blocks d and f, and return last return value from f.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            return 300;
+        } finally {
+            append(&quot;d&quot;);
+            return 400;
+        }
+        append(&quot;e&quot;);
+        return 500;
+    } finally {
+        append(&quot;f&quot;);
+        return 600;
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,d,f&quot;, 600, NothingThrown);
+
+// Return after g.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,d,e,f,g&quot;, 700, NothingThrown);
+
+// Throw after a.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    throw 100;
+    try {
+        append(&quot;b&quot;);
+        throw 200;
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    throw 700;
+
+}, &quot;a&quot;, NothingReturned, 100);
+
+// Throw after b.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        throw 200;
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    throw 700;
+
+}, &quot;a,b,f&quot;, NothingReturned, 200);
+
+// Throw after c.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    throw 700;
+
+}, &quot;a,b,c,d,f&quot;, NothingReturned, 300);
+
+// Throw after c and d.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+            throw 400;
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    throw 700;
+
+}, &quot;a,b,c,d,f&quot;, NothingReturned, 400);
+
+// Throw after c, d, and f.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+            throw 400;
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } finally {
+        append(&quot;f&quot;);
+        throw 600;
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,d,f&quot;, NothingReturned, 600);
+
+// Throw after g.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    throw 700;
+
+}, &quot;a,b,c,d,e,f,g&quot;, NothingReturned, 700);
+
+// Throw after b with catch at z.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        throw 200;
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } catch (e) {
+        append(&quot;z&quot;);
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,z,f,g&quot;, 700, NothingThrown);
+
+// Throw after b with catch at z and rethrow at z.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        throw 200;
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+        throw 500;
+    } catch (e) {
+        append(&quot;z&quot;);
+        throw 5000;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,z,f&quot;, NothingReturned, 5000);
+
+// Throw after c with catch at y and z.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } catch (e) {
+            append(&quot;y&quot;);
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+    } catch (e) {
+        append(&quot;z&quot;);
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,y,d,e,f,g&quot;, 700, NothingThrown);
+
+// Throw after c with catch at y and z, and rethrow at y.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } catch (e) {
+            append(&quot;y&quot;);
+            throw 3000;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+    } catch (e) {
+        append(&quot;z&quot;);
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,y,d,z,f,g&quot;, 700, NothingThrown);
+
+// Throw after c with catch at y and z, and rethrow at y and z.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } catch (e) {
+            append(&quot;y&quot;);
+            throw 3000;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+    } catch (e) {
+        append(&quot;z&quot;);
+        throw 5000;
+    } finally {
+        append(&quot;f&quot;);
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,y,d,z,f&quot;, NothingReturned, 5000);
+
+// Throw after c with catch at y and z, and rethrow at y, z, and f.
+test(() =&gt; {
+    append(&quot;a&quot;);
+    try {
+        append(&quot;b&quot;);
+        try {
+            append(&quot;c&quot;);
+            throw 300;
+        } catch (e) {
+            append(&quot;y&quot;);
+            throw 3000;
+        } finally {
+            append(&quot;d&quot;);
+        }
+        append(&quot;e&quot;);
+    } catch (e) {
+        append(&quot;z&quot;);
+        throw 5000;
+    } finally {
+        append(&quot;f&quot;);
+        throw 600;
+    }   
+    append(&quot;g&quot;);
+    return 700;
+
+}, &quot;a,b,c,y,d,z,f&quot;, NothingReturned, 600);
+
+// No throw or return in for-of loop.
+test(() =&gt; {
+    class TestIterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+            return { }
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new TestIterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,n,2,n,x&quot;, 200, NothingThrown);
+
+// Break in for-of loop.
+test(() =&gt; {
+    class TestIterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+            return { }
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new TestIterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            break;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r,x&quot;, 200, NothingThrown);
+
+// Break in for-of loop with throw in Iterator.return().
+test(() =&gt; {
+    class Iterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+            throw 300;
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new Iterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            break;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, NothingReturned, 300);
+
+// Break in for-of loop with Iterator.return() returning a non object.
+test(() =&gt; {
+    class Iterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new Iterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            break;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, NothingReturned, 'TypeError');
+
+// Return in for-of loop.
+test(() =&gt; {
+    class TestIterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+            return { }
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new TestIterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            return 100;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, 100, NothingThrown);
+
+// Return in for-of loop with throw in Iterator.return().
+test(() =&gt; {
+    class Iterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+            throw 300;
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new Iterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            return 100;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, NothingReturned, 300);
+
+// Return in for-of loop with Iterator.return() returning a non object.
+test(() =&gt; {
+    class Iterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new Iterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            return 100;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, NothingReturned, 'TypeError');
+
+
+// Throw in for-of loop.
+test(() =&gt; {
+    class Iterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new Iterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            throw 100;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, NothingReturned, 100);
+
+// Throw in for-of loop with throw in Iterator.return().
+test(() =&gt; {
+    class Iterator {
+        constructor() {
+            append(&quot;c&quot;);
+            this.i = 0;
+        }
+        next() {
+            append(&quot;n&quot;);
+            let done = (this.i == 3);
+            return { done, value: this.i++ };
+        }
+        return() {
+            append(&quot;r&quot;);
+            throw 300;
+        }
+    }
+
+    var arr = [];
+    arr[Symbol.iterator] = function() {
+        return new Iterator();
+    }
+
+    for (var element of arr) {
+        append(element);
+        if (element == 1)
+            throw 100;
+    }
+    append(&quot;x&quot;);
+    return 200;
+
+}, &quot;c,n,0,n,1,r&quot;, NothingReturned, 100);
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -1,3 +1,242 @@
</span><ins>+2016-12-16  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        De-duplicate finally blocks.
+        https://bugs.webkit.org/show_bug.cgi?id=160168
+
+        Reviewed by Keith Miller.
+
+        JS execution can arrive at a finally block when there are abrupt completions from
+        its try or catch block.  The abrupt completion types include Break,
+        Continue, Return, and Throw.  The non-abrupt completion type is called Normal
+        (i.e. the case of a try block falling through to the finally block).
+
+        Previously, we enable each of these paths for abrupt completion (except for Throw)
+        to run the finally block code by duplicating the finally block code at each of
+        the sites that trigger those completions.  This patch fixes the implementation so
+        that each of these abrupt completions will set a finallyActionRegister (plus a
+        finallyReturnValueRegister for CompletionType::Return) and then jump to the
+        relevant finally blocks, and continue to thread through subsequent outer finally
+        blocks until execution reaches the outermost finally block that the completion
+        type dictates.  We no longer duplicate the finally block code.
+
+        The implementation details:
+        1. We allocate a pair of finallyActionRegister and finallyReturnValueRegister
+           just before entering the outermost try-catch-finally scope.
+
+           On allocating the registers, we set them to the empty JSValue.  This serves
+           to set the completion type to CompletionType::Normal (see (2) below).
+
+        2. The finallyActionRegister serves 2 purpose:
+           a. indicates the CompletionType that triggered entry into the finally block.
+
+              This is how we encode the completion type in the finallyActionRegister:
+              1. CompletionType::Normal
+                 - finallyActionRegister is set to the empty JSValue.
+              2. CompletionType::Break
+                 - finallyActionRegister is set to the int jumpID for the site of the break statement.
+              3. CompletionType::Continue
+                 - finallyActionRegister is set to the int jumpID for the site of the continue statement.
+              4. CompletionType::Return
+                 - finallyActionRegister is set to CompletionType::Return as an int JSValue.
+                 - finallyReturnValueRegister is set to the value to be returned. 
+              5. CompletionType::Throw
+                 - finallyActionRegister is set to the exception object that was caught by the finally block.
+
+              Hence, if the finallyActionRegister can either be:
+              1. empty i.e. we're handling CompletionType::Normal.
+              2. an int JSValue i.e. we're handling CompletionType::Break, Continue, or Return.
+              3. an object i.e. we're handling CompletionType::Throw.
+
+           b. stores the exception caught in the finally block if we're handing
+              CompletionType::Throw.
+
+        3. Each finally block will have 2 entries:
+           a. the entry via throw.
+           b. the normal entry.
+
+           The entry via throw is recorded in the codeBlock's exception table, and can
+           only be jumped to by the VM's exception handling mechanism.
+
+           The normal entry is recorded in a FinallyContext (at bytecode generation time
+           only) and is jumped to when we want enter the finally block due any of the
+           other CompletionTypes.
+
+        4. CompletionType::Normal
+           ======================
+           We encounter this when falling through from a try or catch block to the finally block.  
+           
+           For the try block case, since finallyActionRegister is set to Normal by default,
+           there's nothing more that needs to be done.
+
+           For the catch block case, since we entered the catch block with an exception,
+           finallyActionRegister may be set to Throw.  We'll need to set it to Normal
+           before jumping to the finally block's normal entry.
+
+           CompletionType::Break
+           =====================
+           When we emit bytecode for the BreakNode, we check if we have any FinallyContexts
+           that we need to service before jumping to the breakTarget.  If we do, then:
+           a. we'll register a jumpID along with the breakTarget with the outermost FinallyContext.
+           b. we'll also increment the numberOfBreaksOrContinues count in each FinallyContext
+              from the innermost to the outermost.
+           c. instead of emitting bytecode to jump to the breakTarget, we:
+              1. emit bytecode to set finallyActionRegister to the jumpID.
+              b. emit bytecode to jump to the normal entry of the innermost finally block.
+
+           Each finally block will take care of cascading to the next outer finally block
+           as needed (see (5) below).
+
+           CompletionType::Continue
+           ========================
+           Since continues and breaks work the same way (i.e. with a jump), we handle this
+           exactly the same way as CompletionType::Break, except that we use the
+           continueTarget instead of the breakTarget.
+
+           CompletionType::Return
+           ======================
+           When we emit bytecode for the ReturnNode, we check if we have any FinallyContexts
+           at all on the m_controlFlowScopeStack.
+
+           If so, then instead of emitting op_ret, we:
+              1. emit bytecode to set finallyActionRegister to the CompletionType::Return.
+              1. emit bytecode to move the return value into finallyReturnValueRegister.
+              2. emit bytecode to jump to the normal entry of the innermost finally block.
+
+           Each finally block will take care of cascading to the next outer finally block
+           as needed (see (5) below).
+
+           CompletionType::Throw
+           ======================
+           The op_catch of a finally block will always store the caught exception object
+           in the finallyActionRegister.  This means we're handling CompletionType::Throw
+           (see (2) above).
+
+        5. What happens in each finally block?
+           ==================================
+           Only the finally block's entry via throw will have an op_catch that catches the
+           pending exception (and stores it in the finallyActionRegister).  This throw
+           entry then falls through to the normal entry.
+
+           The finally block's normal entry will restore the scope of the finally block
+           and proceed to execute its code.
+
+           At the end of the finally block (see emitFinallyCompletion()), the finally
+           block will check the finallyActionRegister for each completion type in the
+           following order:
+           
+           a. CompletionType::Normal: jump to the code after the finally block as
+              designated by a normalCompletion label.
+
+           b. CompletionType::Break and Continue:
+              If the FinallyContext for this block has registered FinallyJumps, we'll
+              check for the jumpIDs against the finallyActionRegister.  If the jumpID
+              matches, jump to the corresponding jumpTarget.
+
+              If no jumpIDs match but the FinallyContext's numberOfBreaksOrContinues is
+              greater than the number of registered FinallyJumps, then this means that
+              we have a Break or Continue that needs to be handled by an outer finally
+              block.  In that case, jump to the outer finally block's normal entry.
+              
+           c. CompletionType::Return:
+              If this finally block is not the outermost and finallyActionRegister contains
+              CompletionType::Return, then jump to the outer finally block's normal entry.
+
+              Otherwise, if this finally block is the outermost and finallyActionRegister
+              contains CompletionType::Return, then execute op_ret and return the value
+              in finallyReturnValueRegister.
+
+           d. CompletionType::Throw:
+              If we're not handling any of the above cases, then just throw the
+              finallyActionRegister which contains the exception to re-throw.
+
+        6. restoreScopeRegister()
+        
+           Since the needed scope objects are always stored in a local, we can restore
+           the scope register by simply moving from that local instead of going through
+           op_get_parent_scope.
+
+        7. m_controlFlowScopeStack needs to be a SegmentedVector instead of a Vector.
+           This makes it easier to keep a pointer to the FinallyContext on that stack,
+           and not have to worry about the vector being realloc'ed due to resizing. 
+
+        Performance appears to be neutral both on ES6SampleBench (run via cli) and the
+        JSC benchmarks.
+
+        Relevant spec references:
+        https://tc39.github.io/ecma262/#sec-completion-record-specification-type
+        https://tc39.github.io/ecma262/#sec-try-statement-runtime-semantics-evaluation
+
+        * bytecode/HandlerInfo.h:
+        (JSC::HandlerInfoBase::typeName):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::generate):
+        (JSC::BytecodeGenerator::BytecodeGenerator):
+        (JSC::BytecodeGenerator::emitReturn):
+        (JSC::BytecodeGenerator::pushFinallyControlFlowScope):
+        (JSC::BytecodeGenerator::popFinallyControlFlowScope):
+        (JSC::BytecodeGenerator::allocateAndEmitScope):
+        (JSC::BytecodeGenerator::pushTry):
+        (JSC::BytecodeGenerator::popTry):
+        (JSC::BytecodeGenerator::emitCatch):
+        (JSC::BytecodeGenerator::restoreScopeRegister):
+        (JSC::BytecodeGenerator::labelScopeDepthToLexicalScopeIndex):
+        (JSC::BytecodeGenerator::labelScopeDepth):
+        (JSC::BytecodeGenerator::pushLocalControlFlowScope):
+        (JSC::BytecodeGenerator::popLocalControlFlowScope):
+        (JSC::BytecodeGenerator::emitEnumeration):
+        (JSC::BytecodeGenerator::emitIsNumber):
+        (JSC::BytecodeGenerator::emitYield):
+        (JSC::BytecodeGenerator::emitDelegateYield):
+        (JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded):
+        (JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded):
+        (JSC::BytecodeGenerator::emitFinallyCompletion):
+        (JSC::BytecodeGenerator::allocateFinallyRegisters):
+        (JSC::BytecodeGenerator::releaseFinallyRegisters):
+        (JSC::BytecodeGenerator::emitCompareFinallyActionAndJumpIf):
+        (JSC::BytecodeGenerator::pushIteratorCloseControlFlowScope): Deleted.
+        (JSC::BytecodeGenerator::popIteratorCloseControlFlowScope): Deleted.
+        (JSC::BytecodeGenerator::emitComplexPopScopes): Deleted.
+        (JSC::BytecodeGenerator::emitPopScopes): Deleted.
+        (JSC::BytecodeGenerator::popTryAndEmitCatch): Deleted.
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::FinallyJump::FinallyJump):
+        (JSC::FinallyContext::FinallyContext):
+        (JSC::FinallyContext::outerContext):
+        (JSC::FinallyContext::finallyLabel):
+        (JSC::FinallyContext::depth):
+        (JSC::FinallyContext::numberOfBreaksOrContinues):
+        (JSC::FinallyContext::incNumberOfBreaksOrContinues):
+        (JSC::FinallyContext::handlesReturns):
+        (JSC::FinallyContext::setHandlesReturns):
+        (JSC::FinallyContext::registerJump):
+        (JSC::FinallyContext::numberOfJumps):
+        (JSC::FinallyContext::jumps):
+        (JSC::ControlFlowScope::ControlFlowScope):
+        (JSC::ControlFlowScope::isLabelScope):
+        (JSC::ControlFlowScope::isFinallyScope):
+        (JSC::BytecodeGenerator::currentLexicalScopeIndex):
+        (JSC::BytecodeGenerator::FinallyRegistersScope::FinallyRegistersScope):
+        (JSC::BytecodeGenerator::FinallyRegistersScope::~FinallyRegistersScope):
+        (JSC::BytecodeGenerator::finallyActionRegister):
+        (JSC::BytecodeGenerator::finallyReturnValueRegister):
+        (JSC::BytecodeGenerator::emitSetFinallyActionToNormalCompletion):
+        (JSC::BytecodeGenerator::emitSetFinallyActionToReturnCompletion):
+        (JSC::BytecodeGenerator::emitSetFinallyActionToJumpID):
+        (JSC::BytecodeGenerator::emitSetFinallyReturnValueRegister):
+        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNormalCompletion):
+        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotJump):
+        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsReturnCompletion):
+        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotReturnCompletion):
+        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotThrowCompletion):
+        (JSC::BytecodeGenerator::emitJumpIfCompletionTypeIsThrow):
+        (JSC::BytecodeGenerator::bytecodeOffsetToJumpID):
+        (JSC::BytecodeGenerator::isInFinallyBlock): Deleted.
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::ContinueNode::emitBytecode):
+        (JSC::BreakNode::emitBytecode):
+        (JSC::ReturnNode::emitBytecode):
+        (JSC::TryNode::emitBytecode):
+
</ins><span class="cx"> 2016-12-16  Keith Miller  &lt;keith_miller@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Add missing cases to parseUnreachableExpression and cleanup FunctionParser
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecodeHandlerInfoh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -31,9 +31,9 @@
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="cx"> enum class HandlerType {
</span><del>-    Illegal = 0,
-    Catch = 1,
-    Finally = 2,
</del><ins>+    Catch = 0,
+    Finally = 1,
+    SynthesizedCatch = 2,
</ins><span class="cx">     SynthesizedFinally = 3
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="lines">@@ -53,6 +53,8 @@
</span><span class="cx">             return &quot;catch&quot;;
</span><span class="cx">         case HandlerType::Finally:
</span><span class="cx">             return &quot;finally&quot;;
</span><ins>+        case HandlerType::SynthesizedCatch:
+            return &quot;synthesized catch&quot;;
</ins><span class="cx">         case HandlerType::SynthesizedFinally:
</span><span class="cx">             return &quot;synthesized finally&quot;;
</span><span class="cx">         default:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -156,7 +156,6 @@
</span><span class="cx">         if (end &lt;= start)
</span><span class="cx">             continue;
</span><span class="cx">         
</span><del>-        ASSERT(range.tryData-&gt;handlerType != HandlerType::Illegal);
</del><span class="cx">         UnlinkedHandlerInfo info(static_cast&lt;uint32_t&gt;(start), static_cast&lt;uint32_t&gt;(end),
</span><span class="cx">             static_cast&lt;uint32_t&gt;(range.tryData-&gt;target-&gt;bind()), range.tryData-&gt;handlerType);
</span><span class="cx">         m_codeBlock-&gt;addExceptionHandler(info);
</span><span class="lines">@@ -680,21 +679,25 @@
</span><span class="cx">     // because a function's default parameter ExpressionNodes will use temporary registers.
</span><span class="cx">     pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ);
</span><span class="cx"> 
</span><ins>+    RefPtr&lt;Label&gt; catchLabel = newLabel();
</ins><span class="cx">     TryData* tryFormalParametersData = nullptr;
</span><del>-    if (isAsyncFunctionWrapperParseMode(parseMode) &amp;&amp; !isSimpleParameterList) {
</del><ins>+    bool needTryCatch = isAsyncFunctionWrapperParseMode(parseMode) &amp;&amp; !isSimpleParameterList;
+    if (needTryCatch) {
</ins><span class="cx">         RefPtr&lt;Label&gt; tryFormalParametersStart = emitLabel(newLabel().get());
</span><del>-        tryFormalParametersData = pushTry(tryFormalParametersStart.get());
</del><ins>+        tryFormalParametersData = pushTry(tryFormalParametersStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, isSimpleParameterList, functionNode, functionSymbolTable, symbolTableConstantIndex, captures, shouldCreateArgumentsVariableInParameterScope);
</span><span class="cx"> 
</span><del>-    if (isAsyncFunctionWrapperParseMode(parseMode) &amp;&amp; !isSimpleParameterList) {
</del><ins>+    if (needTryCatch) {
</ins><span class="cx">         RefPtr&lt;Label&gt; didNotThrow = newLabel();
</span><span class="cx">         emitJump(didNotThrow.get());
</span><del>-        RefPtr&lt;RegisterID&gt; exception = newTemporary();
</del><ins>+        emitLabel(catchLabel.get());
+        popTry(tryFormalParametersData, catchLabel.get());
+
</ins><span class="cx">         RefPtr&lt;RegisterID&gt; thrownValue = newTemporary();
</span><del>-        RefPtr&lt;Label&gt; catchHere = emitLabel(newLabel().get());
-        popTryAndEmitCatch(tryFormalParametersData, exception.get(), thrownValue.get(), catchHere.get(), HandlerType::Catch);
</del><ins>+        RegisterID* unused = newTemporary();
+        emitCatch(unused, thrownValue.get());
</ins><span class="cx"> 
</span><span class="cx">         // return promiseCapability.@reject(thrownValue)
</span><span class="cx">         RefPtr&lt;RegisterID&gt; reject = emitGetById(newTemporary(), promiseCapabilityRegister(), m_vm-&gt;propertyNames-&gt;builtinNames().rejectPrivateName());
</span><span class="lines">@@ -3493,16 +3496,16 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
</del><ins>+RegisterID* BytecodeGenerator::emitReturn(RegisterID* src, ReturnFrom from)
</ins><span class="cx"> {
</span><span class="cx">     if (isConstructor()) {
</span><span class="cx">         bool mightBeDerived = constructorKind() == ConstructorKind::Extends;
</span><span class="cx">         bool srcIsThis = src-&gt;index() == m_thisRegister.index();
</span><span class="cx"> 
</span><del>-        if (mightBeDerived &amp;&amp; srcIsThis)
</del><ins>+        if (mightBeDerived &amp;&amp; (srcIsThis || from == ReturnFrom::Finally))
</ins><span class="cx">             emitTDZCheck(src);
</span><span class="cx"> 
</span><del>-        if (!srcIsThis) {
</del><ins>+        if (!srcIsThis || from == ReturnFrom::Finally) {
</ins><span class="cx">             RefPtr&lt;Label&gt; isObjectLabel = newLabel();
</span><span class="cx">             emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get());
</span><span class="cx"> 
</span><span class="lines">@@ -3680,82 +3683,31 @@
</span><span class="cx">     emitDebugHook(WillLeaveCallFrame, m_scopeNode-&gt;lastLine(), m_scopeNode-&gt;startOffset(), m_scopeNode-&gt;lineStartOffset());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BytecodeGenerator::pushFinallyControlFlowScope(StatementNode* finallyBlock)
</del><ins>+FinallyContext* BytecodeGenerator::pushFinallyControlFlowScope(Label* finallyLabel)
</ins><span class="cx"> {
</span><span class="cx">     // Reclaim free label scopes.
</span><span class="cx">     while (m_labelScopes.size() &amp;&amp; !m_labelScopes.last().refCount())
</span><span class="cx">         m_labelScopes.removeLast();
</span><span class="cx"> 
</span><del>-    ControlFlowScope scope;
-    scope.isFinallyBlock = true;
-    FinallyContext context = {
-        finallyBlock,
-        nullptr,
-        nullptr,
-        static_cast&lt;unsigned&gt;(m_controlFlowScopeStack.size()),
-        static_cast&lt;unsigned&gt;(m_switchContextStack.size()),
-        static_cast&lt;unsigned&gt;(m_forInContextStack.size()),
-        static_cast&lt;unsigned&gt;(m_tryContextStack.size()),
-        static_cast&lt;unsigned&gt;(m_labelScopes.size()),
-        static_cast&lt;unsigned&gt;(m_lexicalScopeStack.size()),
-        m_finallyDepth,
-        m_localScopeDepth
-    };
-    scope.finallyContext = context;
-    m_controlFlowScopeStack.append(scope);
-    m_finallyDepth++;
-}
</del><ins>+    ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), FinallyContext(m_currentFinallyContext, finallyLabel, m_finallyDepth));
+    m_controlFlowScopeStack.append(WTFMove(scope));
</ins><span class="cx"> 
</span><del>-void BytecodeGenerator::pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* node)
-{
-    // Reclaim free label scopes.
-    while (m_labelScopes.size() &amp;&amp; !m_labelScopes.last().refCount())
-        m_labelScopes.removeLast();
-
-    ControlFlowScope scope;
-    scope.isFinallyBlock = true;
-    FinallyContext context = {
-        nullptr,
-        iterator,
-        node,
-        static_cast&lt;unsigned&gt;(m_controlFlowScopeStack.size()),
-        static_cast&lt;unsigned&gt;(m_switchContextStack.size()),
-        static_cast&lt;unsigned&gt;(m_forInContextStack.size()),
-        static_cast&lt;unsigned&gt;(m_tryContextStack.size()),
-        static_cast&lt;unsigned&gt;(m_labelScopes.size()),
-        static_cast&lt;unsigned&gt;(m_lexicalScopeStack.size()),
-        m_finallyDepth,
-        m_localScopeDepth
-    };
-    scope.finallyContext = context;
-    m_controlFlowScopeStack.append(scope);
</del><span class="cx">     m_finallyDepth++;
</span><ins>+    m_currentFinallyContext = &amp;m_controlFlowScopeStack.last().finallyContext;
+    return m_currentFinallyContext;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BytecodeGenerator::popFinallyControlFlowScope()
</del><ins>+FinallyContext BytecodeGenerator::popFinallyControlFlowScope()
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(m_controlFlowScopeStack.size());
</span><del>-    ASSERT(m_controlFlowScopeStack.last().isFinallyBlock);
-    ASSERT(m_controlFlowScopeStack.last().finallyContext.finallyBlock);
-    ASSERT(!m_controlFlowScopeStack.last().finallyContext.iterator);
-    ASSERT(!m_controlFlowScopeStack.last().finallyContext.enumerationNode);
</del><ins>+    ASSERT(m_controlFlowScopeStack.last().isFinallyScope());
</ins><span class="cx">     ASSERT(m_finallyDepth &gt; 0);
</span><del>-    m_controlFlowScopeStack.removeLast();
</del><ins>+    ASSERT(m_currentFinallyContext);
+    m_currentFinallyContext = m_currentFinallyContext-&gt;outerContext();
</ins><span class="cx">     m_finallyDepth--;
</span><ins>+    return m_controlFlowScopeStack.takeLast().finallyContext;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BytecodeGenerator::popIteratorCloseControlFlowScope()
-{
-    ASSERT(m_controlFlowScopeStack.size());
-    ASSERT(m_controlFlowScopeStack.last().isFinallyBlock);
-    ASSERT(!m_controlFlowScopeStack.last().finallyContext.finallyBlock);
-    ASSERT(m_controlFlowScopeStack.last().finallyContext.iterator);
-    ASSERT(m_controlFlowScopeStack.last().finallyContext.enumerationNode);
-    ASSERT(m_finallyDepth &gt; 0);
-    m_controlFlowScopeStack.removeLast();
-    m_finallyDepth--;
-}
-
</del><span class="cx"> LabelScopePtr BytecodeGenerator::breakTarget(const Identifier&amp; name)
</span><span class="cx"> {
</span><span class="cx">     // Reclaim free label scopes.
</span><span class="lines">@@ -3853,162 +3805,12 @@
</span><span class="cx">     m_topMostScope = addVar();
</span><span class="cx">     emitMove(m_topMostScope, scopeRegister());
</span><span class="cx"> }
</span><del>-    
-void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowScope* topScope, ControlFlowScope* bottomScope)
-{
-    while (topScope &gt; bottomScope) {
-        // First we count the number of dynamic scopes we need to remove to get
-        // to a finally block.
-        int numberOfNormalScopes = 0;
-        while (topScope &gt; bottomScope) {
-            if (topScope-&gt;isFinallyBlock)
-                break;
-            ++numberOfNormalScopes;
-            --topScope;
-        }
</del><span class="cx"> 
</span><del>-        if (numberOfNormalScopes) {
-            // We need to remove a number of dynamic scopes to get to the next
-            // finally block
-            RefPtr&lt;RegisterID&gt; parentScope = newTemporary();
-            while (numberOfNormalScopes--) {
-                parentScope = emitGetParentScope(parentScope.get(), scope);
-                emitMove(scope, parentScope.get());
-            }
-
-            // If topScope == bottomScope then there isn't a finally block left to emit.
-            if (topScope == bottomScope)
-                return;
-        }
-        
-        Vector&lt;ControlFlowScope&gt; savedControlFlowScopeStack;
-        Vector&lt;SwitchInfo&gt; savedSwitchContextStack;
-        Vector&lt;RefPtr&lt;ForInContext&gt;&gt; savedForInContextStack;
-        Vector&lt;TryContext&gt; poppedTryContexts;
-        Vector&lt;LexicalScopeStackEntry&gt; savedLexicalScopeStack;
-        LabelScopeStore savedLabelScopes;
-        while (topScope &gt; bottomScope &amp;&amp; topScope-&gt;isFinallyBlock) {
-            RefPtr&lt;Label&gt; 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-&gt;finallyContext;
-            bool flipScopes = finallyContext.controlFlowScopeStackSize != m_controlFlowScopeStack.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();
-            bool flipLexicalScopeStack = finallyContext.lexicalScopeStackSize != m_lexicalScopeStack.size();
-            int topScopeIndex = -1;
-            int bottomScopeIndex = -1;
-            if (flipScopes) {
-                topScopeIndex = topScope - m_controlFlowScopeStack.begin();
-                bottomScopeIndex = bottomScope - m_controlFlowScopeStack.begin();
-                savedControlFlowScopeStack = m_controlFlowScopeStack;
-                m_controlFlowScopeStack.shrink(finallyContext.controlFlowScopeStackSize);
-            }
-            if (flipSwitches) {
-                savedSwitchContextStack = m_switchContextStack;
-                m_switchContextStack.shrink(finallyContext.switchContextStackSize);
-            }
-            if (flipForIns) {
-                savedForInContextStack = m_forInContextStack;
-                m_forInContextStack.shrink(finallyContext.forInContextStackSize);
-            }
-            if (flipTries) {
-                while (m_tryContextStack.size() != finallyContext.tryContextStackSize) {
-                    ASSERT(m_tryContextStack.size() &gt; finallyContext.tryContextStackSize);
-                    TryContext context = m_tryContextStack.takeLast();
-                    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() &gt; finallyContext.labelScopesSize)
-                    m_labelScopes.removeLast();
-            }
-            if (flipLexicalScopeStack) {
-                savedLexicalScopeStack = m_lexicalScopeStack;
-                m_lexicalScopeStack.shrink(finallyContext.lexicalScopeStackSize);
-            }
-            int savedFinallyDepth = m_finallyDepth;
-            m_finallyDepth = finallyContext.finallyDepth;
-            int savedDynamicScopeDepth = m_localScopeDepth;
-            m_localScopeDepth = finallyContext.dynamicScopeDepth;
-            
-            if (finallyContext.finallyBlock) {
-                // Emit the finally block.
-                emitNode(finallyContext.finallyBlock);
-            } else {
-                // Emit the IteratorClose block.
-                ASSERT(finallyContext.iterator);
-                emitIteratorClose(finallyContext.iterator, finallyContext.enumerationNode);
-            }
-
-            RefPtr&lt;Label&gt; afterFinally = emitLabel(newLabel().get());
-            
-            // Restore the state of the world.
-            if (flipScopes) {
-                m_controlFlowScopeStack = savedControlFlowScopeStack;
-                topScope = &amp;m_controlFlowScopeStack[topScopeIndex]; // assert it's within bounds
-                bottomScope = m_controlFlowScopeStack.begin() + bottomScopeIndex; // don't assert, since it the index might be -1.
-            }
-            if (flipSwitches)
-                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);
-                }
-                poppedTryContexts.clear();
-            }
-            if (flipLabelScopes)
-                m_labelScopes = savedLabelScopes;
-            if (flipLexicalScopeStack)
-                m_lexicalScopeStack = savedLexicalScopeStack;
-            m_finallyDepth = savedFinallyDepth;
-            m_localScopeDepth = savedDynamicScopeDepth;
-            
-            --topScope;
-        }
-    }
-}
-
-void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth)
</del><ins>+TryData* BytecodeGenerator::pushTry(Label* start, Label* handlerLabel, HandlerType handlerType)
</ins><span class="cx"> {
</span><del>-    ASSERT(labelScopeDepth() - targetScopeDepth &gt;= 0);
-
-    size_t scopeDelta = labelScopeDepth() - targetScopeDepth;
-    ASSERT(scopeDelta &lt;= m_controlFlowScopeStack.size());
-    if (!scopeDelta)
-        return;
-
-    if (!m_finallyDepth) {
-        RefPtr&lt;RegisterID&gt; parentScope = newTemporary();
-        while (scopeDelta--) {
-            parentScope = emitGetParentScope(parentScope.get(), scope);
-            emitMove(scope, parentScope.get());
-        }
-        return;
-    }
-
-    emitComplexPopScopes(scope, &amp;m_controlFlowScopeStack.last(), &amp;m_controlFlowScopeStack.last() - scopeDelta);
-}
-
-TryData* BytecodeGenerator::pushTry(Label* start)
-{
</del><span class="cx">     TryData tryData;
</span><del>-    tryData.target = newLabel();
-    tryData.handlerType = HandlerType::Illegal;
</del><ins>+    tryData.target = handlerLabel;
+    tryData.handlerType = handlerType;
</ins><span class="cx">     m_tryData.append(tryData);
</span><span class="cx">     TryData* result = &amp;m_tryData.last();
</span><span class="cx">     
</span><span class="lines">@@ -4021,7 +3823,7 @@
</span><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType handlerType)
</del><ins>+void BytecodeGenerator::popTry(TryData* tryData, Label* end)
</ins><span class="cx"> {
</span><span class="cx">     m_usesExceptions = true;
</span><span class="cx">     
</span><span class="lines">@@ -4033,28 +3835,52 @@
</span><span class="cx">     tryRange.tryData = m_tryContextStack.last().tryData;
</span><span class="cx">     m_tryRanges.append(tryRange);
</span><span class="cx">     m_tryContextStack.removeLast();
</span><del>-    
-    emitLabel(tryRange.tryData-&gt;target.get());
-    tryRange.tryData-&gt;handlerType = handlerType;
</del><ins>+}
</ins><span class="cx"> 
</span><ins>+void BytecodeGenerator::emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister)
+{
</ins><span class="cx">     emitOpcode(op_catch);
</span><span class="cx">     instructions().append(exceptionRegister-&gt;index());
</span><span class="cx">     instructions().append(thrownValueRegister-&gt;index());
</span><ins>+}
</ins><span class="cx"> 
</span><del>-    bool foundLocalScope = false;
-    for (unsigned i = m_lexicalScopeStack.size(); i--; ) {
-        // Note that if we don't find a local scope in the current function/program, 
-        // we must grab the outer-most scope of this bytecode generation.
-        if (m_lexicalScopeStack[i].m_scope) {
-            foundLocalScope = true;
-            emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope);
-            break;
</del><ins>+void BytecodeGenerator::restoreScopeRegister(int lexicalScopeIndex)
+{
+    if (lexicalScopeIndex == CurrentLexicalScopeIndex)
+        return; // No change needed.
+
+    if (lexicalScopeIndex != OutermostLexicalScopeIndex) {
+        ASSERT(lexicalScopeIndex &lt; static_cast&lt;int&gt;(m_lexicalScopeStack.size()));
+        int endIndex = lexicalScopeIndex + 1;
+        for (size_t i = endIndex; i--; ) {
+            if (m_lexicalScopeStack[i].m_scope) {
+                emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope);
+                return;
+            }
</ins><span class="cx">         }
</span><span class="cx">     }
</span><del>-    if (!foundLocalScope)
-        emitMove(scopeRegister(), m_topMostScope);
</del><ins>+    // Note that if we don't find a local scope in the current function/program,
+    // we must grab the outer-most scope of this bytecode generation.
+    emitMove(scopeRegister(), m_topMostScope);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void BytecodeGenerator::restoreScopeRegister()
+{
+    restoreScopeRegister(currentLexicalScopeIndex());
+}
+
+int BytecodeGenerator::labelScopeDepthToLexicalScopeIndex(int targetLabelScopeDepth)
+{
+    ASSERT(labelScopeDepth() - targetLabelScopeDepth &gt;= 0);
+    size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth;
+    ASSERT(scopeDelta &lt;= m_controlFlowScopeStack.size());
+    if (!scopeDelta)
+        return CurrentLexicalScopeIndex;
+
+    ControlFlowScope&amp; targetScope = m_controlFlowScopeStack[targetLabelScopeDepth];
+    return targetScope.lexicalScopeIndex;
+}
+
</ins><span class="cx"> int BytecodeGenerator::localScopeDepth() const
</span><span class="cx"> {
</span><span class="cx">     return m_localScopeDepth;
</span><span class="lines">@@ -4061,8 +3887,10 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> int BytecodeGenerator::labelScopeDepth() const
</span><del>-{ 
-    return localScopeDepth() + m_finallyDepth;
</del><ins>+{
+    int depth = localScopeDepth() + m_finallyDepth;
+    ASSERT(depth == static_cast&lt;int&gt;(m_controlFlowScopeStack.size()));
+    return depth;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void BytecodeGenerator::emitThrowStaticError(ErrorType errorType, RegisterID* raw)
</span><span class="lines">@@ -4132,9 +3960,8 @@
</span><span class="cx"> 
</span><span class="cx"> void BytecodeGenerator::pushLocalControlFlowScope()
</span><span class="cx"> {
</span><del>-    ControlFlowScope scope;
-    scope.isFinallyBlock = false;
-    m_controlFlowScopeStack.append(scope);
</del><ins>+    ControlFlowScope scope(ControlFlowScope::Label, currentLexicalScopeIndex());
+    m_controlFlowScopeStack.append(WTFMove(scope));
</ins><span class="cx">     m_localScopeDepth++;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -4141,7 +3968,7 @@
</span><span class="cx"> void BytecodeGenerator::popLocalControlFlowScope()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_controlFlowScopeStack.size());
</span><del>-    ASSERT(!m_controlFlowScopeStack.last().isFinallyBlock);
</del><ins>+    ASSERT(!m_controlFlowScopeStack.last().isFinallyScope());
</ins><span class="cx">     m_controlFlowScopeStack.removeLast();
</span><span class="cx">     m_localScopeDepth--;
</span><span class="cx"> }
</span><span class="lines">@@ -4296,9 +4123,11 @@
</span><span class="cx">     }
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><del>-    
</del><ins>+
</ins><span class="cx"> void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function&lt;void(BytecodeGenerator&amp;, RegisterID*)&gt;&amp; callBack, ForOfNode* forLoopNode, RegisterID* forLoopSymbolTable)
</span><span class="cx"> {
</span><ins>+    FinallyRegistersScope finallyRegistersScope(*this);
+
</ins><span class="cx">     RefPtr&lt;RegisterID&gt; subject = newTemporary();
</span><span class="cx">     emitNode(subject.get(), subjectNode);
</span><span class="cx">     RefPtr&lt;RegisterID&gt; iterator = emitGetById(newTemporary(), subject.get(), propertyNames().iteratorSymbol);
</span><span class="lines">@@ -4309,8 +4138,15 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     RefPtr&lt;Label&gt; loopDone = newLabel();
</span><ins>+    RefPtr&lt;Label&gt; tryStartLabel = newLabel();
+    RefPtr&lt;Label&gt; finallyViaThrowLabel = newLabel();
+    RefPtr&lt;Label&gt; finallyLabel = newLabel();
+    RefPtr&lt;Label&gt; catchLabel = newLabel();
+    RefPtr&lt;Label&gt; endCatchLabel = newLabel();
+
</ins><span class="cx">     // RefPtr&lt;Register&gt; iterator's lifetime must be longer than IteratorCloseContext.
</span><del>-    pushIteratorCloseControlFlowScope(iterator.get(), node);
</del><ins>+    FinallyContext* finallyContext = pushFinallyControlFlowScope(finallyLabel.get());
+
</ins><span class="cx">     {
</span><span class="cx">         LabelScopePtr scope = newLabelScope(LabelScope::Loop);
</span><span class="cx">         RefPtr&lt;RegisterID&gt; value = newTemporary();
</span><span class="lines">@@ -4322,40 +4158,68 @@
</span><span class="cx">         emitLabel(loopStart.get());
</span><span class="cx">         emitLoopHint();
</span><span class="cx"> 
</span><del>-        RefPtr&lt;Label&gt; tryStartLabel = newLabel();
</del><span class="cx">         emitLabel(tryStartLabel.get());
</span><del>-        TryData* tryData = pushTry(tryStartLabel.get());
</del><ins>+        TryData* tryData = pushTry(tryStartLabel.get(), finallyViaThrowLabel.get(), HandlerType::SynthesizedFinally);
</ins><span class="cx">         callBack(*this, value.get());
</span><span class="cx">         emitJump(scope-&gt;continueTarget());
</span><span class="cx"> 
</span><del>-        // IteratorClose sequence for throw-ed control flow.
</del><ins>+        // IteratorClose sequence for abrupt completions.
</ins><span class="cx">         {
</span><del>-            RefPtr&lt;Label&gt; catchHere = emitLabel(newLabel().get());
-            RefPtr&lt;RegisterID&gt; exceptionRegister = newTemporary();
-            RefPtr&lt;RegisterID&gt; thrownValueRegister = newTemporary();
-            popTryAndEmitCatch(tryData, exceptionRegister.get(),
-                thrownValueRegister.get(), catchHere.get(), HandlerType::SynthesizedFinally);
</del><ins>+            // Finally block for the enumeration.
+            emitLabel(finallyViaThrowLabel.get());
+            popTry(tryData, finallyViaThrowLabel.get());
</ins><span class="cx"> 
</span><del>-            RefPtr&lt;Label&gt; catchDone = newLabel();
</del><ins>+            RegisterID* unused = newTemporary();
+            emitCatch(finallyActionRegister(), unused);
+            // Setting the finallyActionRegister to the caught exception here implies CompletionType::Throw.
</ins><span class="cx"> 
</span><ins>+            emitLabel(finallyLabel.get());
+            restoreScopeRegister();
+
+            RefPtr&lt;Label&gt; finallyDone = newLabel();
+
</ins><span class="cx">             RefPtr&lt;RegisterID&gt; returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword);
</span><del>-            emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), catchDone.get());
</del><ins>+            emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), finallyDone.get());
</ins><span class="cx"> 
</span><ins>+            RefPtr&lt;RegisterID&gt; originalFinallyActionRegister = newTemporary();
+            emitMove(originalFinallyActionRegister.get(), finallyActionRegister());
+
</ins><span class="cx">             RefPtr&lt;Label&gt; returnCallTryStart = newLabel();
</span><span class="cx">             emitLabel(returnCallTryStart.get());
</span><del>-            TryData* returnCallTryData = pushTry(returnCallTryStart.get());
</del><ins>+            TryData* returnCallTryData = pushTry(returnCallTryStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch);
</ins><span class="cx"> 
</span><span class="cx">             CallArguments returnArguments(*this, nullptr);
</span><span class="cx">             emitMove(returnArguments.thisRegister(), iterator.get());
</span><span class="cx">             emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node-&gt;divot(), node-&gt;divotStart(), node-&gt;divotEnd(), DebuggableCall::No);
</span><ins>+            emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), finallyDone.get());
+            emitThrowTypeError(ASCIILiteral(&quot;Iterator result interface is not an object.&quot;));
</ins><span class="cx"> 
</span><del>-            emitLabel(catchDone.get());
-            emitThrow(exceptionRegister.get());
</del><ins>+            emitLabel(finallyDone.get());
+            emitFinallyCompletion(*finallyContext, endCatchLabel.get());
</ins><span class="cx"> 
</span><del>-            // Absorb exception.
-            popTryAndEmitCatch(returnCallTryData, newTemporary(),
-                newTemporary(), catchDone.get(), HandlerType::SynthesizedFinally);
-            emitThrow(exceptionRegister.get());
</del><ins>+            popTry(returnCallTryData, finallyDone.get());
+
+            // Catch block for exceptions that may be thrown while calling the return
+            // handler in the enumeration finally block. The only reason we need this
+            // catch block is because if entered the above finally block due to a thrown
+            // exception, then we want to re-throw the original exception on exiting
+            // the finally block. Otherwise, we'll let any new exception pass through.
+            {
+                emitLabel(catchLabel.get());
+                RefPtr&lt;RegisterID&gt; exceptionRegister = newTemporary();
+                RegisterID* unused = newTemporary();
+                emitCatch(exceptionRegister.get(), unused);
+                restoreScopeRegister();
+
+                RefPtr&lt;Label&gt; throwLabel = newLabel();
+                emitJumpIfCompletionTypeIsThrow(originalFinallyActionRegister.get(), throwLabel.get());
+                emitMove(originalFinallyActionRegister.get(), exceptionRegister.get());
+
+                emitLabel(throwLabel.get());
+                emitThrow(originalFinallyActionRegister.get());
+
+                emitLabel(endCatchLabel.get());
+            }
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         emitLabel(scope-&gt;continueTarget());
</span><span class="lines">@@ -4376,7 +4240,7 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // IteratorClose sequence for break-ed control flow.
</span><del>-    popIteratorCloseControlFlowScope();
</del><ins>+    popFinallyControlFlowScope();
</ins><span class="cx">     emitIteratorClose(iterator.get(), node);
</span><span class="cx">     emitLabel(loopDone.get());
</span><span class="cx"> }
</span><span class="lines">@@ -4497,6 +4361,14 @@
</span><span class="cx">     return dst;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+RegisterID* BytecodeGenerator::emitIsNumber(RegisterID* dst, RegisterID* src)
+{
+    emitOpcode(op_is_number);
+    instructions().append(dst-&gt;index());
+    instructions().append(src-&gt;index());
+    return dst;
+}
+
</ins><span class="cx"> RegisterID* BytecodeGenerator::emitIsUndefined(RegisterID* dst, RegisterID* src)
</span><span class="cx"> {
</span><span class="cx">     emitOpcode(op_is_undefined);
</span><span class="lines">@@ -4784,11 +4656,9 @@
</span><span class="cx">     // Return.
</span><span class="cx">     {
</span><span class="cx">         RefPtr&lt;RegisterID&gt; returnRegister = generatorValueRegister();
</span><del>-        if (isInFinallyBlock()) {
-            returnRegister = emitMove(newTemporary(), returnRegister.get());
-            emitPopScopes(scopeRegister(), 0);
-        }
-        emitReturn(returnRegister.get());
</del><ins>+        bool hasFinally = emitReturnViaFinallyIfNeeded(returnRegister.get());
+        if (!hasFinally)
+            emitReturn(returnRegister.get());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Throw.
</span><span class="lines">@@ -4891,9 +4761,9 @@
</span><span class="cx">                     emitGetById(value.get(), value.get(), propertyNames().value);
</span><span class="cx"> 
</span><span class="cx">                     emitLabel(returnSequence.get());
</span><del>-                    if (isInFinallyBlock())
-                        emitPopScopes(scopeRegister(), 0);
-                    emitReturn(value.get());
</del><ins>+                    bool hasFinally = emitReturnViaFinallyIfNeeded(value.get());
+                    if (!hasFinally)
+                        emitReturn(value.get());
</ins><span class="cx">                 }
</span><span class="cx"> 
</span><span class="cx">                 // Normal.
</span><span class="lines">@@ -4923,6 +4793,157 @@
</span><span class="cx">     emitPutById(generatorRegister(), propertyNames().builtinNames().generatorStatePrivateName(), completedState);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool BytecodeGenerator::emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget)
+{
+    ASSERT(labelScopeDepth() - targetLabelScopeDepth &gt;= 0);
+    size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth;
+    ASSERT(scopeDelta &lt;= m_controlFlowScopeStack.size());
+    if (!scopeDelta)
+        return nullptr; // No finallys to thread through.
+
+    ControlFlowScope* topScope = &amp;m_controlFlowScopeStack.last();
+    ControlFlowScope* bottomScope = &amp;m_controlFlowScopeStack.last() - scopeDelta;
+
+    FinallyContext* innermostFinallyContext = nullptr;
+    FinallyContext* outermostFinallyContext = nullptr;
+    while (topScope &gt; bottomScope) {
+        if (topScope-&gt;isFinallyScope()) {
+            FinallyContext* finallyContext = &amp;topScope-&gt;finallyContext;
+            if (!innermostFinallyContext)
+                innermostFinallyContext = finallyContext;
+            outermostFinallyContext = finallyContext;
+            finallyContext-&gt;incNumberOfBreaksOrContinues();
+        }
+        --topScope;
+    }
+    if (!outermostFinallyContext)
+        return false; // No finallys to thread through.
+
+    int jumpID = bytecodeOffsetToJumpID(instructions().size());
+    int lexicalScopeIndex = labelScopeDepthToLexicalScopeIndex(targetLabelScopeDepth);
+    outermostFinallyContext-&gt;registerJump(jumpID, lexicalScopeIndex, jumpTarget);
+
+    emitSetFinallyActionToJumpID(jumpID);
+    emitJump(innermostFinallyContext-&gt;finallyLabel());
+    return true; // We'll be jumping to a finally block.
+}
+
+bool BytecodeGenerator::emitReturnViaFinallyIfNeeded(RegisterID* returnRegister)
+{
+    if (!m_controlFlowScopeStack.size())
+        return false; // No finallys to thread through.
+
+    ControlFlowScope* topScope = &amp;m_controlFlowScopeStack.last();
+    ControlFlowScope* bottomScope = &amp;m_controlFlowScopeStack.first();
+
+    FinallyContext* innermostFinallyContext = nullptr;
+    while (topScope &gt;= bottomScope) {
+        if (topScope-&gt;isFinallyScope()) {
+            FinallyContext* finallyContext = &amp;topScope-&gt;finallyContext;
+            if (!innermostFinallyContext)
+                innermostFinallyContext = finallyContext;
+            finallyContext-&gt;setHandlesReturns();
+        }
+        --topScope;
+    }
+    if (!innermostFinallyContext)
+        return false; // No finallys to thread through.
+
+    emitSetFinallyActionToReturnCompletion();
+    emitSetFinallyReturnValueRegister(returnRegister);
+    emitJump(innermostFinallyContext-&gt;finallyLabel());
+    return true; // We'll be jumping to a finally block.
+}
+
+void BytecodeGenerator::emitFinallyCompletion(FinallyContext&amp; context, Label* normalCompletionLabel)
+{
+    // FIXME: switch the finallyActionRegister to only store int values for all CompletionTypes. This is more optimal for JIT type speculation.
+    // https://bugs.webkit.org/show_bug.cgi?id=165979
+    emitJumpIfFinallyActionIsNormalCompletion(normalCompletionLabel);
+
+    if (context.numberOfBreaksOrContinues() || context.handlesReturns()) {
+        FinallyContext* outerContext = context.outerContext();
+        if (outerContext) {
+            // We are not the outermost finally.
+            size_t numberOfJumps = context.numberOfJumps();
+            for (size_t i = 0; i &lt; numberOfJumps; i++) {
+                RefPtr&lt;Label&gt; nextLabel = newLabel();
+                auto&amp; jump = context.jumps(i);
+                emitJumpIfFinallyActionIsNotJump(jump.jumpID, nextLabel.get());
+
+                restoreScopeRegister(jump.targetLexicalScopeIndex);
+                emitSetFinallyActionToNormalCompletion();
+                emitJump(jump.targetLabel.get());
+
+                emitLabel(nextLabel.get());
+            }
+
+            bool hasBreaksOrContinuesNotCoveredByJumps = context.numberOfBreaksOrContinues() &gt; numberOfJumps;
+            if (hasBreaksOrContinuesNotCoveredByJumps || context.handlesReturns())
+                emitJumpIfFinallyActionIsNotThrowCompletion(outerContext-&gt;finallyLabel());
+
+        } else {
+            // We are the outermost finally.
+            size_t numberOfJumps = context.numberOfJumps();
+            ASSERT(numberOfJumps == context.numberOfBreaksOrContinues());
+
+            for (size_t i = 0; i &lt; numberOfJumps; i++) {
+                RefPtr&lt;Label&gt; nextLabel = newLabel();
+                auto&amp; jump = context.jumps(i);
+                emitJumpIfFinallyActionIsNotJump(jump.jumpID, nextLabel.get());
+
+                restoreScopeRegister(jump.targetLexicalScopeIndex);
+                emitSetFinallyActionToNormalCompletion();
+                emitJump(jump.targetLabel.get());
+
+                emitLabel(nextLabel.get());
+            }
+
+            if (context.handlesReturns()) {
+                RefPtr&lt;Label&gt; notReturnLabel = newLabel();
+                emitJumpIfFinallyActionIsNotReturnCompletion(notReturnLabel.get());
+
+                emitWillLeaveCallFrameDebugHook();
+                emitReturn(finallyReturnValueRegister(), ReturnFrom::Finally);
+                
+                emitLabel(notReturnLabel.get());
+            }
+        }
+    }
+    emitThrow(finallyActionRegister());
+}
+
+bool BytecodeGenerator::allocateFinallyRegisters()
+{
+    if (m_finallyActionRegister)
+        return false;
+
+    ASSERT(!m_finallyReturnValueRegister);
+    m_finallyActionRegister = newTemporary();
+    m_finallyReturnValueRegister = newTemporary();
+
+    emitSetFinallyActionToNormalCompletion();
+    emitMoveEmptyValue(m_finallyReturnValueRegister.get());
+    return true;
+}
+
+void BytecodeGenerator::releaseFinallyRegisters()
+{
+    ASSERT(m_finallyActionRegister &amp;&amp; m_finallyReturnValueRegister);
+    m_finallyActionRegister = nullptr;
+    m_finallyReturnValueRegister = nullptr;
+}
+
+void BytecodeGenerator::emitCompareFinallyActionAndJumpIf(OpcodeID compareOpcode, int value, Label* jumpTarget)
+{
+    RefPtr&lt;RegisterID&gt; tempRegister = newTemporary();
+    RegisterID* valueConstant = addConstantValue(JSValue(value));
+    OperandTypes operandTypes = OperandTypes(ResultType::numberTypeIsInt32(), ResultType::unknownType());
+
+    auto equivalenceResult = emitBinaryOp(compareOpcode, tempRegister.get(), valueConstant, finallyActionRegister(), operandTypes);
+    emitJumpIfTrue(equivalenceResult, jumpTarget);
+}
+
</ins><span class="cx"> } // namespace JSC
</span><span class="cx"> 
</span><span class="cx"> namespace WTF {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -80,22 +80,78 @@
</span><span class="cx">         unsigned m_padding;
</span><span class="cx">     };
</span><span class="cx"> 
</span><ins>+    struct FinallyJump {
+        FinallyJump(int jumpID, int targetLexicalScopeIndex, Label* targetLabel)
+            : jumpID(jumpID)
+            , targetLexicalScopeIndex(targetLexicalScopeIndex)
+            , targetLabel(targetLabel)
+        { }
+
+        int jumpID { 0 };
+        int targetLexicalScopeIndex { 0 };
+        RefPtr&lt;Label&gt; targetLabel;
+    };
+
</ins><span class="cx">     struct FinallyContext {
</span><del>-        StatementNode* finallyBlock;
-        RegisterID* iterator;
-        ThrowableExpressionData* enumerationNode;
-        unsigned controlFlowScopeStackSize;
-        unsigned switchContextStackSize;
-        unsigned forInContextStackSize;
-        unsigned tryContextStackSize;
-        unsigned labelScopesSize;
-        unsigned lexicalScopeStackSize;
-        int finallyDepth;
-        int dynamicScopeDepth;
</del><ins>+        FinallyContext() { }
+        FinallyContext(FinallyContext* outerContext, Label* finallyLabel, int finallyDepth)
+            : m_outerContext(outerContext)
+            , m_finallyLabel(finallyLabel)
+            , m_finallyDepth(finallyDepth)
+        {
+            ASSERT(!m_jumps || m_jumps-&gt;isEmpty());
+        }
+
+        FinallyContext* outerContext() const { return m_outerContext; }
+        Label* finallyLabel() const { return m_finallyLabel; }
+        int depth() const { return m_finallyDepth; }
+
+        unsigned numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues; }
+        void incNumberOfBreaksOrContinues()
+        {
+            ASSERT(m_numberOfBreaksOrContinues &lt; INT_MAX);
+            m_numberOfBreaksOrContinues++;
+        }
+
+        bool handlesReturns() const { return m_handlesReturns; }
+        void setHandlesReturns() { m_handlesReturns = true; }
+
+        void registerJump(int jumpID, int lexicalScopeIndex, Label* targetLabel)
+        {
+            if (!m_jumps)
+                m_jumps = std::make_unique&lt;Vector&lt;FinallyJump&gt;&gt;();
+            m_jumps-&gt;append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel));
+        }
+
+        size_t numberOfJumps() const { return m_jumps ? m_jumps-&gt;size() : 0; }
+        FinallyJump&amp; jumps(size_t i) { return (*m_jumps)[i]; }
+
+    private:
+        FinallyContext* m_outerContext { nullptr };
+        Label* m_finallyLabel { nullptr };
+        int m_finallyDepth { 0 };
+        unsigned m_numberOfBreaksOrContinues { 0 };
+        bool m_handlesReturns { false };
+        std::unique_ptr&lt;Vector&lt;FinallyJump&gt;&gt; m_jumps;
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     struct ControlFlowScope {
</span><del>-        bool isFinallyBlock;
</del><ins>+        typedef uint8_t Type;
+        enum {
+            Label,
+            Finally
+        };
+        ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext&amp;&amp; finallyContext = FinallyContext())
+            : type(type)
+            , lexicalScopeIndex(lexicalScopeIndex)
+            , finallyContext(std::forward&lt;FinallyContext&gt;(finallyContext))
+        { }
+
+        bool isLabelScope() const { return type == Label; }
+        bool isFinallyScope() const { return type == Finally; }
+
+        Type type;
+        int lexicalScopeIndex;
</ins><span class="cx">         FinallyContext finallyContext;
</span><span class="cx">     };
</span><span class="cx"> 
</span><span class="lines">@@ -605,7 +661,8 @@
</span><span class="cx"> 
</span><span class="cx">         RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*);
</span><span class="cx"> 
</span><del>-        RegisterID* emitReturn(RegisterID* src);
</del><ins>+        enum class ReturnFrom { Normal, Finally };
+        RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal);
</ins><span class="cx">         RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); }
</span><span class="cx"> 
</span><span class="cx">         RegisterID* emitConstruct(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&amp;, const JSTextPosition&amp; divot, const JSTextPosition&amp; divotStart, const JSTextPosition&amp; divotEnd);
</span><span class="lines">@@ -626,7 +683,6 @@
</span><span class="cx">         PassRefPtr&lt;Label&gt; emitJumpIfFalse(RegisterID* cond, Label* target);
</span><span class="cx">         PassRefPtr&lt;Label&gt; emitJumpIfNotFunctionCall(RegisterID* cond, Label* target);
</span><span class="cx">         PassRefPtr&lt;Label&gt; emitJumpIfNotFunctionApply(RegisterID* cond, Label* target);
</span><del>-        void emitPopScopes(RegisterID* srcDst, int targetScopeDepth);
</del><span class="cx"> 
</span><span class="cx">         void emitEnter();
</span><span class="cx">         void emitWatchdog();
</span><span class="lines">@@ -649,6 +705,7 @@
</span><span class="cx">         RegisterID* emitIsMap(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSMapType); }
</span><span class="cx">         RegisterID* emitIsSet(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSSetType); }
</span><span class="cx">         RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
</span><ins>+        RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src);
</ins><span class="cx">         RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
</span><span class="cx">         RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
</span><span class="cx">         RegisterID* emitIsDerivedArray(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, DerivedArrayType); }
</span><span class="lines">@@ -663,10 +720,30 @@
</span><span class="cx">         bool emitReadOnlyExceptionIfNeeded(const Variable&amp;);
</span><span class="cx"> 
</span><span class="cx">         // Start a try block. 'start' must have been emitted.
</span><del>-        TryData* pushTry(Label* start);
</del><ins>+        TryData* pushTry(Label* start, Label* handlerLabel, HandlerType);
</ins><span class="cx">         // End a try block. 'end' must have been emitted.
</span><del>-        void popTryAndEmitCatch(TryData*, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType);
</del><ins>+        void popTry(TryData*, Label* end);
+        void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister);
</ins><span class="cx"> 
</span><ins>+    private:
+        static const int CurrentLexicalScopeIndex = -2;
+        static const int OutermostLexicalScopeIndex = -1;
+
+    public:
+        void restoreScopeRegister();
+        void restoreScopeRegister(int lexicalScopeIndex);
+
+        int currentLexicalScopeIndex() const
+        {
+            int size = static_cast&lt;int&gt;(m_lexicalScopeStack.size());
+            ASSERT(static_cast&lt;size_t&gt;(size) == m_lexicalScopeStack.size());
+            ASSERT(size &gt;= 0);
+            int index = size - 1;
+            return index;
+        }
+
+        int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth);
+
</ins><span class="cx">         void emitThrow(RegisterID* exc)
</span><span class="cx">         { 
</span><span class="cx">             m_usesExceptions = true;
</span><span class="lines">@@ -698,13 +775,102 @@
</span><span class="cx">         void emitDebugHook(ExpressionNode*);
</span><span class="cx">         void emitWillLeaveCallFrameDebugHook();
</span><span class="cx"> 
</span><del>-        bool isInFinallyBlock() { return m_finallyDepth &gt; 0; }
</del><ins>+        class FinallyRegistersScope {
+        public:
+            FinallyRegistersScope(BytecodeGenerator&amp; generator, bool needFinallyRegisters = true)
+                : m_generator(generator)
+            {
+                if (needFinallyRegisters &amp;&amp; m_generator.allocateFinallyRegisters())
+                    m_needToReleaseOnDestruction = true;
+            }
+            ~FinallyRegistersScope()
+            {
+                if (m_needToReleaseOnDestruction)
+                    m_generator.releaseFinallyRegisters();
+            }
</ins><span class="cx"> 
</span><del>-        void pushFinallyControlFlowScope(StatementNode* finallyBlock);
-        void popFinallyControlFlowScope();
-        void pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* enumerationNode);
-        void popIteratorCloseControlFlowScope();
</del><ins>+        private:
+            BytecodeGenerator&amp; m_generator;
+            bool m_needToReleaseOnDestruction { false };
+        };
</ins><span class="cx"> 
</span><ins>+        RegisterID* finallyActionRegister() const
+        {
+            ASSERT(m_finallyActionRegister);
+            return m_finallyActionRegister.get();
+        }
+        RegisterID* finallyReturnValueRegister() const
+        {
+            ASSERT(m_finallyReturnValueRegister);
+            return m_finallyReturnValueRegister.get();
+        }
+
+        void emitSetFinallyActionToNormalCompletion()
+        {
+            emitMoveEmptyValue(m_finallyActionRegister.get());
+        }
+        void emitSetFinallyActionToReturnCompletion()
+        {
+            emitLoad(finallyActionRegister(), JSValue(static_cast&lt;int&gt;(CompletionType::Return)));
+        }
+        void emitSetFinallyActionToJumpID(int jumpID)
+        {
+            emitLoad(finallyActionRegister(), JSValue(jumpID));
+        }
+        void emitSetFinallyReturnValueRegister(RegisterID* reg)
+        {
+            emitMove(finallyReturnValueRegister(), reg);
+        }
+
+        void emitJumpIfFinallyActionIsNormalCompletion(Label* jumpTarget)
+        {
+            emitJumpIfTrue(emitIsEmpty(newTemporary(), finallyActionRegister()), jumpTarget);
+        }
+
+        void emitJumpIfFinallyActionIsNotJump(int jumpID, Label* jumpTarget)
+        {
+            emitCompareFinallyActionAndJumpIf(op_nstricteq, jumpID, jumpTarget);
+        }
+
+        void emitJumpIfFinallyActionIsReturnCompletion(Label* jumpTarget)
+        {
+            emitCompareFinallyActionAndJumpIf(op_stricteq, static_cast&lt;int&gt;(CompletionType::Return), jumpTarget);
+        }
+        void emitJumpIfFinallyActionIsNotReturnCompletion(Label* jumpTarget)
+        {
+            emitCompareFinallyActionAndJumpIf(op_nstricteq, static_cast&lt;int&gt;(CompletionType::Return), jumpTarget);
+        }
+
+        void emitJumpIfFinallyActionIsNotThrowCompletion(Label* jumpTarget)
+        {
+            emitJumpIfTrue(emitIsNumber(newTemporary(), finallyActionRegister()), jumpTarget);
+        }
+        void emitJumpIfCompletionTypeIsThrow(RegisterID* reg, Label* jumpTarget)
+        {
+            emitJumpIfFalse(emitIsNumber(newTemporary(), reg), jumpTarget);
+        }
+
+        bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget);
+        bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister);
+        void emitFinallyCompletion(FinallyContext&amp;, Label* normalCompletionLabel);
+
+    private:
+        void emitCompareFinallyActionAndJumpIf(OpcodeID compareOpcode, int value, Label* jumpTarget);
+
+        int bytecodeOffsetToJumpID(unsigned offset)
+        {
+            int jumpID = offset + static_cast&lt;int&gt;(CompletionType::NumberOfTypes);
+            ASSERT(jumpID &gt;= static_cast&lt;int&gt;(CompletionType::NumberOfTypes));
+            return jumpID;
+        }
+
+        bool allocateFinallyRegisters();
+        void releaseFinallyRegisters();
+
+    public:
+        FinallyContext* pushFinallyControlFlowScope(Label* finallyLabel);
+        FinallyContext popFinallyControlFlowScope();
+
</ins><span class="cx">         void pushIndexedForInScope(RegisterID* local, RegisterID* index);
</span><span class="cx">         void popIndexedForInScope(RegisterID* local);
</span><span class="cx">         void pushStructureForInScope(RegisterID* local, RegisterID* index, RegisterID* property, RegisterID* enumerator);
</span><span class="lines">@@ -797,7 +963,6 @@
</span><span class="cx"> 
</span><span class="cx">         void allocateCalleeSaveSpace();
</span><span class="cx">         void allocateAndEmitScope();
</span><del>-        void emitComplexPopScopes(RegisterID*, ControlFlowScope* topScope, ControlFlowScope* bottomScope);
</del><span class="cx"> 
</span><span class="cx">         typedef HashMap&lt;double, JSValue&gt; NumberMap;
</span><span class="cx">         typedef HashMap&lt;UniquedStringImpl*, JSString*, IdentifierRepHash&gt; IdentifierStringMap;
</span><span class="lines">@@ -935,6 +1100,35 @@
</span><span class="cx">         RegisterID* m_arrowFunctionContextLexicalEnvironmentRegister { nullptr };
</span><span class="cx">         RegisterID* m_promiseCapabilityRegister { nullptr };
</span><span class="cx"> 
</span><ins>+        // The spec at https://tc39.github.io/ecma262/#sec-completion-record-specification-type says
+        // that there are 5 types of completions. Conceptually, we'll set m_finallyActionRegister
+        // to one of these completion types. However, to optimize our implementation, we'll encode
+        // these type info as follows:
+        //
+        //     CompletionType::Normal   - m_finallyActionRegister is empty.
+        //     CompletionType::Break    - m_finallyActionRegister is an int JSValue jumpID.
+        //     CompletionType::Continue - m_finallyActionRegister is an int JSValue jumpID.
+        //     CompletionType::Return   - m_finallyActionRegister is the Return enum as an int JSValue.
+        //     CompletionType::Throw    - m_finallyActionRegister is the Exception object to rethrow.
+        //
+        // Hence, of the 5 completion types, only the CompletionType::Return enum value is used in
+        // our implementation. The rest are just provided for completeness.
+
+        enum class CompletionType : int {
+            Normal,
+            Break,
+            Continue,
+            Return,
+            Throw,
+
+            NumberOfTypes
+        };
+
+        RefPtr&lt;RegisterID&gt; m_finallyActionRegister;
+        RefPtr&lt;RegisterID&gt; m_finallyReturnValueRegister;
+
+        FinallyContext* m_currentFinallyContext { nullptr };
+
</ins><span class="cx">         SegmentedVector&lt;RegisterID*, 16&gt; m_localRegistersForCalleeSaveRegisters;
</span><span class="cx">         SegmentedVector&lt;RegisterID, 32&gt; m_constantPoolRegisters;
</span><span class="cx">         SegmentedVector&lt;RegisterID, 32&gt; m_calleeLocals;
</span><span class="lines">@@ -949,7 +1143,9 @@
</span><span class="cx">         void pushLocalControlFlowScope();
</span><span class="cx">         void popLocalControlFlowScope();
</span><span class="cx"> 
</span><del>-        Vector&lt;ControlFlowScope, 0, UnsafeVectorOverflow&gt; m_controlFlowScopeStack;
</del><ins>+        // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it. 
+        // https://bugs.webkit.org/show_bug.cgi?id=165980
+        SegmentedVector&lt;ControlFlowScope, 16&gt; m_controlFlowScopeStack;
</ins><span class="cx">         Vector&lt;SwitchInfo&gt; m_switchContextStack;
</span><span class="cx">         Vector&lt;RefPtr&lt;ForInContext&gt;&gt; m_forInContextStack;
</span><span class="cx">         Vector&lt;TryContext&gt; m_tryContextStack;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerNodesCodegencpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -2998,8 +2998,12 @@
</span><span class="cx">     LabelScopePtr scope = generator.continueTarget(m_ident);
</span><span class="cx">     ASSERT(scope);
</span><span class="cx"> 
</span><del>-    generator.emitPopScopes(generator.scopeRegister(), scope-&gt;scopeDepth());
-    generator.emitJump(scope-&gt;continueTarget());
</del><ins>+    bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope-&gt;scopeDepth(), scope-&gt;continueTarget());
+    if (!hasFinally) {
+        int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope-&gt;scopeDepth());
+        generator.restoreScopeRegister(lexicalScopeIndex);
+        generator.emitJump(scope-&gt;continueTarget());
+    }
</ins><span class="cx"> 
</span><span class="cx">     generator.emitProfileControlFlow(endOffset());
</span><span class="cx"> }
</span><span class="lines">@@ -3025,8 +3029,12 @@
</span><span class="cx">     LabelScopePtr scope = generator.breakTarget(m_ident);
</span><span class="cx">     ASSERT(scope);
</span><span class="cx"> 
</span><del>-    generator.emitPopScopes(generator.scopeRegister(), scope-&gt;scopeDepth());
-    generator.emitJump(scope-&gt;breakTarget());
</del><ins>+    bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope-&gt;scopeDepth(), scope-&gt;breakTarget());
+    if (!hasFinally) {
+        int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope-&gt;scopeDepth());
+        generator.restoreScopeRegister(lexicalScopeIndex);
+        generator.emitJump(scope-&gt;breakTarget());
+    }
</ins><span class="cx"> 
</span><span class="cx">     generator.emitProfileControlFlow(endOffset());
</span><span class="cx"> }
</span><span class="lines">@@ -3043,14 +3051,14 @@
</span><span class="cx">     RefPtr&lt;RegisterID&gt; returnRegister = m_value ? generator.emitNodeInTailPosition(dst, m_value) : generator.emitLoad(dst, jsUndefined());
</span><span class="cx"> 
</span><span class="cx">     generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, divotStart(), divotEnd());
</span><del>-    if (generator.isInFinallyBlock()) {
-        returnRegister = generator.emitMove(generator.newTemporary(), returnRegister.get());
-        generator.emitPopScopes(generator.scopeRegister(), 0);
</del><ins>+
+    bool hasFinally = generator.emitReturnViaFinallyIfNeeded(returnRegister.get());
+    if (!hasFinally) {
+        generator.emitWillLeaveCallFrameDebugHook();
+        generator.emitReturn(returnRegister.get());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    generator.emitWillLeaveCallFrameDebugHook();
-    generator.emitReturn(returnRegister.get());
-    generator.emitProfileControlFlow(endOffset()); 
</del><ins>+    generator.emitProfileControlFlow(endOffset());
</ins><span class="cx">     // Emitting an unreachable return here is needed in case this op_profile_control_flow is the 
</span><span class="cx">     // last opcode in a CodeBlock because a CodeBlock's instructions must end with a terminal opcode.
</span><span class="cx">     if (generator.vm()-&gt;controlFlowProfiler())
</span><span class="lines">@@ -3279,32 +3287,57 @@
</span><span class="cx">     // optimizer knows they may be jumped to from anywhere.
</span><span class="cx"> 
</span><span class="cx">     ASSERT(m_catchBlock || m_finallyBlock);
</span><ins>+    BytecodeGenerator::FinallyRegistersScope finallyRegistersScope(generator, m_finallyBlock);
</ins><span class="cx"> 
</span><ins>+    RefPtr&lt;Label&gt; catchLabel;
+    RefPtr&lt;Label&gt; catchEndLabel;
+    RefPtr&lt;Label&gt; finallyViaThrowLabel;
+    RefPtr&lt;Label&gt; finallyLabel;
+    RefPtr&lt;Label&gt; finallyEndLabel;
+
</ins><span class="cx">     RefPtr&lt;Label&gt; tryStartLabel = generator.newLabel();
</span><span class="cx">     generator.emitLabel(tryStartLabel.get());
</span><del>-    
-    if (m_finallyBlock)
-        generator.pushFinallyControlFlowScope(m_finallyBlock);
-    TryData* tryData = generator.pushTry(tryStartLabel.get());
</del><span class="cx"> 
</span><ins>+    if (m_finallyBlock) {
+        finallyViaThrowLabel = generator.newLabel();
+        finallyLabel = generator.newLabel();
+        finallyEndLabel = generator.newLabel();
+
+        generator.pushFinallyControlFlowScope(finallyLabel.get());
+    }
+    if (m_catchBlock) {
+        catchLabel = generator.newLabel();
+        catchEndLabel = generator.newLabel();
+    }
+
+    Label* tryHandlerLabel = m_catchBlock ? catchLabel.get() : finallyViaThrowLabel.get();
+    HandlerType tryHandlerType = m_catchBlock ? HandlerType::Catch : HandlerType::Finally;
+    TryData* tryData = generator.pushTry(tryStartLabel.get(), tryHandlerLabel, tryHandlerType);
+
</ins><span class="cx">     generator.emitNode(dst, m_tryBlock);
</span><span class="cx"> 
</span><del>-    if (m_catchBlock) {
-        RefPtr&lt;Label&gt; catchEndLabel = generator.newLabel();
-        
-        // Normal path: jump over the catch block.
</del><ins>+    // The finallyActionRegister is an empty value by default, which implies CompletionType::Normal.
+    if (m_finallyBlock)
+        generator.emitJump(finallyLabel.get());
+    else
</ins><span class="cx">         generator.emitJump(catchEndLabel.get());
</span><span class="cx"> 
</span><ins>+    RefPtr&lt;Label&gt; endTryLabel = generator.emitLabel(generator.newLabel().get());
+    generator.popTry(tryData, endTryLabel.get());
+
+    if (m_catchBlock) {
</ins><span class="cx">         // Uncaught exception path: the catch block.
</span><del>-        RefPtr&lt;Label&gt; here = generator.emitLabel(generator.newLabel().get());
-        RefPtr&lt;RegisterID&gt; exceptionRegister = generator.newTemporary();
</del><ins>+        generator.emitLabel(catchLabel.get());
</ins><span class="cx">         RefPtr&lt;RegisterID&gt; thrownValueRegister = generator.newTemporary();
</span><del>-        generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), here.get(), HandlerType::Catch);
-        
</del><ins>+        RegisterID* unused = generator.newTemporary();
+        generator.emitCatch(unused, thrownValueRegister.get());
+        generator.restoreScopeRegister();
+
+        TryData* tryData = nullptr;
</ins><span class="cx">         if (m_finallyBlock) {
</span><span class="cx">             // If the catch block throws an exception and we have a finally block, then the finally
</span><span class="cx">             // block should &quot;catch&quot; that exception.
</span><del>-            tryData = generator.pushTry(here.get());
</del><ins>+            tryData = generator.pushTry(catchLabel.get(), finallyViaThrowLabel.get(), HandlerType::Finally);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         generator.emitPushCatchScope(m_lexicalVariables);
</span><span class="lines">@@ -3316,37 +3349,38 @@
</span><span class="cx">             generator.emitNodeInTailPosition(dst, m_catchBlock);
</span><span class="cx">         generator.emitLoad(thrownValueRegister.get(), jsUndefined());
</span><span class="cx">         generator.emitPopCatchScope(m_lexicalVariables);
</span><ins>+
+        if (m_finallyBlock) {
+            generator.emitSetFinallyActionToNormalCompletion();
+            generator.emitJump(finallyLabel.get());
+            generator.popTry(tryData, finallyViaThrowLabel.get());
+        }
+
</ins><span class="cx">         generator.emitLabel(catchEndLabel.get());
</span><ins>+        generator.emitProfileControlFlow(m_catchBlock-&gt;endOffset() + 1);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (m_finallyBlock) {
</span><del>-        RefPtr&lt;Label&gt; preFinallyLabel = generator.emitLabel(generator.newLabel().get());
-        
-        generator.popFinallyControlFlowScope();
</del><ins>+        FinallyContext finallyContext = generator.popFinallyControlFlowScope();
</ins><span class="cx"> 
</span><del>-        RefPtr&lt;Label&gt; finallyEndLabel = generator.newLabel();
</del><ins>+        // Entry to the finally block for CompletionType::Throw.
+        generator.emitLabel(finallyViaThrowLabel.get());
+        RegisterID* unused = generator.newTemporary();
+        generator.emitCatch(generator.finallyActionRegister(), unused);
+        // Setting the finallyActionRegister to the caught exception here implies CompletionType::Throw.
</ins><span class="cx"> 
</span><ins>+        // Entry to the finally block for CompletionTypes other than Throw.
+        generator.emitLabel(finallyLabel.get());
+        generator.restoreScopeRegister();
+
</ins><span class="cx">         int finallyStartOffset = m_catchBlock ? m_catchBlock-&gt;endOffset() + 1 : m_tryBlock-&gt;endOffset() + 1;
</span><del>-
-        // Normal path: run the finally code, and jump to the end.
</del><span class="cx">         generator.emitProfileControlFlow(finallyStartOffset);
</span><span class="cx">         generator.emitNodeInTailPosition(dst, m_finallyBlock);
</span><del>-        generator.emitProfileControlFlow(m_finallyBlock-&gt;endOffset() + 1);
-        generator.emitJump(finallyEndLabel.get());
</del><span class="cx"> 
</span><del>-        // Uncaught exception path: invoke the finally block, then re-throw the exception.
-        RefPtr&lt;RegisterID&gt; exceptionRegister = generator.newTemporary();
-        RefPtr&lt;RegisterID&gt; thrownValueRegister = generator.newTemporary();
-        generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), preFinallyLabel.get(), HandlerType::Finally);
-        generator.emitProfileControlFlow(finallyStartOffset);
-        generator.emitNodeInTailPosition(dst, m_finallyBlock);
-        generator.emitThrow(exceptionRegister.get());
-
</del><ins>+        generator.emitFinallyCompletion(finallyContext, finallyEndLabel.get());
</ins><span class="cx">         generator.emitLabel(finallyEndLabel.get());
</span><span class="cx">         generator.emitProfileControlFlow(m_finallyBlock-&gt;endOffset() + 1);
</span><del>-    } else
-        generator.emitProfileControlFlow(m_catchBlock-&gt;endOffset() + 1);
-
</del><ins>+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ ScopeNode -----------------------------
</span></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/WTF/ChangeLog        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2016-12-16  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        Add predecessor info to dumps from JSC_dumpBytecodeLivenessResults=true.
+        https://bugs.webkit.org/show_bug.cgi?id=165958
+
+        Reviewed by Keith Miller.
+
+        Added some methods to bring SegmentedVector closer to parity with Vector.
+
+        * wtf/SegmentedVector.h:
+        (WTF::SegmentedVector::first):
+        (WTF::SegmentedVector::last):
+        (WTF::SegmentedVector::takeLast):
+
</ins><span class="cx"> 2016-12-16  Michael Saboff  &lt;msaboff@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         REGRESSION: HipChat and Mail sometimes hang beneath JSC::Heap::lastChanceToFinalize()
</span></span></pre></div>
<a id="trunkSourceWTFwtfSegmentedVectorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/SegmentedVector.h (209951 => 209952)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/SegmentedVector.h        2016-12-17 00:48:31 UTC (rev 209951)
+++ trunk/Source/WTF/wtf/SegmentedVector.h        2016-12-17 01:06:49 UTC (rev 209952)
</span><span class="lines">@@ -127,9 +127,17 @@
</span><span class="cx">             return at(index);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        T&amp; last()
</del><ins>+        T&amp; first() { return at(0); }
+        const T&amp; first() const { return at(0); }
+        T&amp; last() { return at(size() - 1); }
+        const T&amp; last() const { return at(size() - 1); }
+
+        T takeLast()
</ins><span class="cx">         {
</span><del>-            return at(size() - 1);
</del><ins>+            ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty());
+            T result = WTFMove(last());
+            --m_size;
+            return result;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         template&lt;typename... Args&gt;
</span></span></pre>
</div>
</div>

</body>
</html>