Changeset 209952 in webkit


Ignore:
Timestamp:
Dec 16, 2016 5:06:49 PM (7 years ago)
Author:
mark.lam@apple.com
Message:

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).

  1. The finallyActionRegister serves 2 purpose:
    1. 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.
  1. stores the exception caught in the finally block if we're handing CompletionType::Throw.
  1. Each finally block will have 2 entries:
    1. the entry via throw.
    2. 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.

  1. 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:

  1. we'll register a jumpID along with the breakTarget with the outermost FinallyContext.
  2. we'll also increment the numberOfBreaksOrContinues count in each FinallyContext from the innermost to the outermost.
  3. instead of emitting bytecode to jump to the breakTarget, we:
    1. emit bytecode to set finallyActionRegister to the jumpID.
    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::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.
  2. emit bytecode to move the return value into finallyReturnValueRegister.
  3. 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).

  1. 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:


  1. CompletionType::Normal: jump to the code after the finally block as designated by a normalCompletion label.
  1. 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.


  1. 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.

  1. CompletionType::Throw: If we're not handling any of the above cases, then just throw the finallyActionRegister which contains the exception to re-throw.
  1. 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.

  1. 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):

Location:
trunk
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/JSTests/ChangeLog

    r209928 r209952  
     12016-12-16  Mark Lam  <mark.lam@apple.com>
     2
     3        De-duplicate finally blocks.
     4        https://bugs.webkit.org/show_bug.cgi?id=160168
     5
     6        Reviewed by Keith Miller.
     7
     8        * stress/deeply-nested-finallys.js: Added.
     9        - Tests many levels of finally nesting.  This causes the old code to hang (and
     10          crashes eventually) while trying to generate bytecode for the exponentially
     11          duplicated finally blocks.  The new code completes this test almost instantly.
     12
     13        * stress/test-finally.js: Added.
     14        - Tests control flow through various permutations of finally blocks.
     15
    1162016-12-16  Saam Barati  <sbarati@apple.com>
    217
  • trunk/Source/JavaScriptCore/ChangeLog

    r209934 r209952  
     12016-12-16  Mark Lam  <mark.lam@apple.com>
     2
     3        De-duplicate finally blocks.
     4        https://bugs.webkit.org/show_bug.cgi?id=160168
     5
     6        Reviewed by Keith Miller.
     7
     8        JS execution can arrive at a finally block when there are abrupt completions from
     9        its try or catch block.  The abrupt completion types include Break,
     10        Continue, Return, and Throw.  The non-abrupt completion type is called Normal
     11        (i.e. the case of a try block falling through to the finally block).
     12
     13        Previously, we enable each of these paths for abrupt completion (except for Throw)
     14        to run the finally block code by duplicating the finally block code at each of
     15        the sites that trigger those completions.  This patch fixes the implementation so
     16        that each of these abrupt completions will set a finallyActionRegister (plus a
     17        finallyReturnValueRegister for CompletionType::Return) and then jump to the
     18        relevant finally blocks, and continue to thread through subsequent outer finally
     19        blocks until execution reaches the outermost finally block that the completion
     20        type dictates.  We no longer duplicate the finally block code.
     21
     22        The implementation details:
     23        1. We allocate a pair of finallyActionRegister and finallyReturnValueRegister
     24           just before entering the outermost try-catch-finally scope.
     25
     26           On allocating the registers, we set them to the empty JSValue.  This serves
     27           to set the completion type to CompletionType::Normal (see (2) below).
     28
     29        2. The finallyActionRegister serves 2 purpose:
     30           a. indicates the CompletionType that triggered entry into the finally block.
     31
     32              This is how we encode the completion type in the finallyActionRegister:
     33              1. CompletionType::Normal
     34                 - finallyActionRegister is set to the empty JSValue.
     35              2. CompletionType::Break
     36                 - finallyActionRegister is set to the int jumpID for the site of the break statement.
     37              3. CompletionType::Continue
     38                 - finallyActionRegister is set to the int jumpID for the site of the continue statement.
     39              4. CompletionType::Return
     40                 - finallyActionRegister is set to CompletionType::Return as an int JSValue.
     41                 - finallyReturnValueRegister is set to the value to be returned.
     42              5. CompletionType::Throw
     43                 - finallyActionRegister is set to the exception object that was caught by the finally block.
     44
     45              Hence, if the finallyActionRegister can either be:
     46              1. empty i.e. we're handling CompletionType::Normal.
     47              2. an int JSValue i.e. we're handling CompletionType::Break, Continue, or Return.
     48              3. an object i.e. we're handling CompletionType::Throw.
     49
     50           b. stores the exception caught in the finally block if we're handing
     51              CompletionType::Throw.
     52
     53        3. Each finally block will have 2 entries:
     54           a. the entry via throw.
     55           b. the normal entry.
     56
     57           The entry via throw is recorded in the codeBlock's exception table, and can
     58           only be jumped to by the VM's exception handling mechanism.
     59
     60           The normal entry is recorded in a FinallyContext (at bytecode generation time
     61           only) and is jumped to when we want enter the finally block due any of the
     62           other CompletionTypes.
     63
     64        4. CompletionType::Normal
     65           ======================
     66           We encounter this when falling through from a try or catch block to the finally block. 
     67           
     68           For the try block case, since finallyActionRegister is set to Normal by default,
     69           there's nothing more that needs to be done.
     70
     71           For the catch block case, since we entered the catch block with an exception,
     72           finallyActionRegister may be set to Throw.  We'll need to set it to Normal
     73           before jumping to the finally block's normal entry.
     74
     75           CompletionType::Break
     76           =====================
     77           When we emit bytecode for the BreakNode, we check if we have any FinallyContexts
     78           that we need to service before jumping to the breakTarget.  If we do, then:
     79           a. we'll register a jumpID along with the breakTarget with the outermost FinallyContext.
     80           b. we'll also increment the numberOfBreaksOrContinues count in each FinallyContext
     81              from the innermost to the outermost.
     82           c. instead of emitting bytecode to jump to the breakTarget, we:
     83              1. emit bytecode to set finallyActionRegister to the jumpID.
     84              b. emit bytecode to jump to the normal entry of the innermost finally block.
     85
     86           Each finally block will take care of cascading to the next outer finally block
     87           as needed (see (5) below).
     88
     89           CompletionType::Continue
     90           ========================
     91           Since continues and breaks work the same way (i.e. with a jump), we handle this
     92           exactly the same way as CompletionType::Break, except that we use the
     93           continueTarget instead of the breakTarget.
     94
     95           CompletionType::Return
     96           ======================
     97           When we emit bytecode for the ReturnNode, we check if we have any FinallyContexts
     98           at all on the m_controlFlowScopeStack.
     99
     100           If so, then instead of emitting op_ret, we:
     101              1. emit bytecode to set finallyActionRegister to the CompletionType::Return.
     102              1. emit bytecode to move the return value into finallyReturnValueRegister.
     103              2. emit bytecode to jump to the normal entry of the innermost finally block.
     104
     105           Each finally block will take care of cascading to the next outer finally block
     106           as needed (see (5) below).
     107
     108           CompletionType::Throw
     109           ======================
     110           The op_catch of a finally block will always store the caught exception object
     111           in the finallyActionRegister.  This means we're handling CompletionType::Throw
     112           (see (2) above).
     113
     114        5. What happens in each finally block?
     115           ==================================
     116           Only the finally block's entry via throw will have an op_catch that catches the
     117           pending exception (and stores it in the finallyActionRegister).  This throw
     118           entry then falls through to the normal entry.
     119
     120           The finally block's normal entry will restore the scope of the finally block
     121           and proceed to execute its code.
     122
     123           At the end of the finally block (see emitFinallyCompletion()), the finally
     124           block will check the finallyActionRegister for each completion type in the
     125           following order:
     126           
     127           a. CompletionType::Normal: jump to the code after the finally block as
     128              designated by a normalCompletion label.
     129
     130           b. CompletionType::Break and Continue:
     131              If the FinallyContext for this block has registered FinallyJumps, we'll
     132              check for the jumpIDs against the finallyActionRegister.  If the jumpID
     133              matches, jump to the corresponding jumpTarget.
     134
     135              If no jumpIDs match but the FinallyContext's numberOfBreaksOrContinues is
     136              greater than the number of registered FinallyJumps, then this means that
     137              we have a Break or Continue that needs to be handled by an outer finally
     138              block.  In that case, jump to the outer finally block's normal entry.
     139             
     140           c. CompletionType::Return:
     141              If this finally block is not the outermost and finallyActionRegister contains
     142              CompletionType::Return, then jump to the outer finally block's normal entry.
     143
     144              Otherwise, if this finally block is the outermost and finallyActionRegister
     145              contains CompletionType::Return, then execute op_ret and return the value
     146              in finallyReturnValueRegister.
     147
     148           d. CompletionType::Throw:
     149              If we're not handling any of the above cases, then just throw the
     150              finallyActionRegister which contains the exception to re-throw.
     151
     152        6. restoreScopeRegister()
     153       
     154           Since the needed scope objects are always stored in a local, we can restore
     155           the scope register by simply moving from that local instead of going through
     156           op_get_parent_scope.
     157
     158        7. m_controlFlowScopeStack needs to be a SegmentedVector instead of a Vector.
     159           This makes it easier to keep a pointer to the FinallyContext on that stack,
     160           and not have to worry about the vector being realloc'ed due to resizing.
     161
     162        Performance appears to be neutral both on ES6SampleBench (run via cli) and the
     163        JSC benchmarks.
     164
     165        Relevant spec references:
     166        https://tc39.github.io/ecma262/#sec-completion-record-specification-type
     167        https://tc39.github.io/ecma262/#sec-try-statement-runtime-semantics-evaluation
     168
     169        * bytecode/HandlerInfo.h:
     170        (JSC::HandlerInfoBase::typeName):
     171        * bytecompiler/BytecodeGenerator.cpp:
     172        (JSC::BytecodeGenerator::generate):
     173        (JSC::BytecodeGenerator::BytecodeGenerator):
     174        (JSC::BytecodeGenerator::emitReturn):
     175        (JSC::BytecodeGenerator::pushFinallyControlFlowScope):
     176        (JSC::BytecodeGenerator::popFinallyControlFlowScope):
     177        (JSC::BytecodeGenerator::allocateAndEmitScope):
     178        (JSC::BytecodeGenerator::pushTry):
     179        (JSC::BytecodeGenerator::popTry):
     180        (JSC::BytecodeGenerator::emitCatch):
     181        (JSC::BytecodeGenerator::restoreScopeRegister):
     182        (JSC::BytecodeGenerator::labelScopeDepthToLexicalScopeIndex):
     183        (JSC::BytecodeGenerator::labelScopeDepth):
     184        (JSC::BytecodeGenerator::pushLocalControlFlowScope):
     185        (JSC::BytecodeGenerator::popLocalControlFlowScope):
     186        (JSC::BytecodeGenerator::emitEnumeration):
     187        (JSC::BytecodeGenerator::emitIsNumber):
     188        (JSC::BytecodeGenerator::emitYield):
     189        (JSC::BytecodeGenerator::emitDelegateYield):
     190        (JSC::BytecodeGenerator::emitJumpViaFinallyIfNeeded):
     191        (JSC::BytecodeGenerator::emitReturnViaFinallyIfNeeded):
     192        (JSC::BytecodeGenerator::emitFinallyCompletion):
     193        (JSC::BytecodeGenerator::allocateFinallyRegisters):
     194        (JSC::BytecodeGenerator::releaseFinallyRegisters):
     195        (JSC::BytecodeGenerator::emitCompareFinallyActionAndJumpIf):
     196        (JSC::BytecodeGenerator::pushIteratorCloseControlFlowScope): Deleted.
     197        (JSC::BytecodeGenerator::popIteratorCloseControlFlowScope): Deleted.
     198        (JSC::BytecodeGenerator::emitComplexPopScopes): Deleted.
     199        (JSC::BytecodeGenerator::emitPopScopes): Deleted.
     200        (JSC::BytecodeGenerator::popTryAndEmitCatch): Deleted.
     201        * bytecompiler/BytecodeGenerator.h:
     202        (JSC::FinallyJump::FinallyJump):
     203        (JSC::FinallyContext::FinallyContext):
     204        (JSC::FinallyContext::outerContext):
     205        (JSC::FinallyContext::finallyLabel):
     206        (JSC::FinallyContext::depth):
     207        (JSC::FinallyContext::numberOfBreaksOrContinues):
     208        (JSC::FinallyContext::incNumberOfBreaksOrContinues):
     209        (JSC::FinallyContext::handlesReturns):
     210        (JSC::FinallyContext::setHandlesReturns):
     211        (JSC::FinallyContext::registerJump):
     212        (JSC::FinallyContext::numberOfJumps):
     213        (JSC::FinallyContext::jumps):
     214        (JSC::ControlFlowScope::ControlFlowScope):
     215        (JSC::ControlFlowScope::isLabelScope):
     216        (JSC::ControlFlowScope::isFinallyScope):
     217        (JSC::BytecodeGenerator::currentLexicalScopeIndex):
     218        (JSC::BytecodeGenerator::FinallyRegistersScope::FinallyRegistersScope):
     219        (JSC::BytecodeGenerator::FinallyRegistersScope::~FinallyRegistersScope):
     220        (JSC::BytecodeGenerator::finallyActionRegister):
     221        (JSC::BytecodeGenerator::finallyReturnValueRegister):
     222        (JSC::BytecodeGenerator::emitSetFinallyActionToNormalCompletion):
     223        (JSC::BytecodeGenerator::emitSetFinallyActionToReturnCompletion):
     224        (JSC::BytecodeGenerator::emitSetFinallyActionToJumpID):
     225        (JSC::BytecodeGenerator::emitSetFinallyReturnValueRegister):
     226        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNormalCompletion):
     227        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotJump):
     228        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsReturnCompletion):
     229        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotReturnCompletion):
     230        (JSC::BytecodeGenerator::emitJumpIfFinallyActionIsNotThrowCompletion):
     231        (JSC::BytecodeGenerator::emitJumpIfCompletionTypeIsThrow):
     232        (JSC::BytecodeGenerator::bytecodeOffsetToJumpID):
     233        (JSC::BytecodeGenerator::isInFinallyBlock): Deleted.
     234        * bytecompiler/NodesCodegen.cpp:
     235        (JSC::ContinueNode::emitBytecode):
     236        (JSC::BreakNode::emitBytecode):
     237        (JSC::ReturnNode::emitBytecode):
     238        (JSC::TryNode::emitBytecode):
     239
    12402016-12-16  Keith Miller  <keith_miller@apple.com>
    2241
  • trunk/Source/JavaScriptCore/bytecode/HandlerInfo.h

    r206525 r209952  
    3232
    3333enum class HandlerType {
    34     Illegal = 0,
    35     Catch = 1,
    36     Finally = 2,
     34    Catch = 0,
     35    Finally = 1,
     36    SynthesizedCatch = 2,
    3737    SynthesizedFinally = 3
    3838};
     
    5454        case HandlerType::Finally:
    5555            return "finally";
     56        case HandlerType::SynthesizedCatch:
     57            return "synthesized catch";
    5658        case HandlerType::SynthesizedFinally:
    5759            return "synthesized finally";
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp

    r209847 r209952  
    157157            continue;
    158158       
    159         ASSERT(range.tryData->handlerType != HandlerType::Illegal);
    160159        UnlinkedHandlerInfo info(static_cast<uint32_t>(start), static_cast<uint32_t>(end),
    161160            static_cast<uint32_t>(range.tryData->target->bind()), range.tryData->handlerType);
     
    681680    pushTDZVariables(*parentScopeTDZVariables, TDZCheckOptimization::DoNotOptimize, TDZRequirement::UnderTDZ);
    682681
     682    RefPtr<Label> catchLabel = newLabel();
    683683    TryData* tryFormalParametersData = nullptr;
    684     if (isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList) {
     684    bool needTryCatch = isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList;
     685    if (needTryCatch) {
    685686        RefPtr<Label> tryFormalParametersStart = emitLabel(newLabel().get());
    686         tryFormalParametersData = pushTry(tryFormalParametersStart.get());
     687        tryFormalParametersData = pushTry(tryFormalParametersStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch);
    687688    }
    688689
    689690    initializeDefaultParameterValuesAndSetupFunctionScopeStack(parameters, isSimpleParameterList, functionNode, functionSymbolTable, symbolTableConstantIndex, captures, shouldCreateArgumentsVariableInParameterScope);
    690691
    691     if (isAsyncFunctionWrapperParseMode(parseMode) && !isSimpleParameterList) {
     692    if (needTryCatch) {
    692693        RefPtr<Label> didNotThrow = newLabel();
    693694        emitJump(didNotThrow.get());
    694         RefPtr<RegisterID> exception = newTemporary();
     695        emitLabel(catchLabel.get());
     696        popTry(tryFormalParametersData, catchLabel.get());
     697
    695698        RefPtr<RegisterID> thrownValue = newTemporary();
    696         RefPtr<Label> catchHere = emitLabel(newLabel().get());
    697         popTryAndEmitCatch(tryFormalParametersData, exception.get(), thrownValue.get(), catchHere.get(), HandlerType::Catch);
     699        RegisterID* unused = newTemporary();
     700        emitCatch(unused, thrownValue.get());
    698701
    699702        // return promiseCapability.@reject(thrownValue)
     
    34943497}
    34953498
    3496 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
     3499RegisterID* BytecodeGenerator::emitReturn(RegisterID* src, ReturnFrom from)
    34973500{
    34983501    if (isConstructor()) {
     
    35003503        bool srcIsThis = src->index() == m_thisRegister.index();
    35013504
    3502         if (mightBeDerived && srcIsThis)
     3505        if (mightBeDerived && (srcIsThis || from == ReturnFrom::Finally))
    35033506            emitTDZCheck(src);
    35043507
    3505         if (!srcIsThis) {
     3508        if (!srcIsThis || from == ReturnFrom::Finally) {
    35063509            RefPtr<Label> isObjectLabel = newLabel();
    35073510            emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectLabel.get());
     
    36813684}
    36823685
    3683 void BytecodeGenerator::pushFinallyControlFlowScope(StatementNode* finallyBlock)
     3686FinallyContext* BytecodeGenerator::pushFinallyControlFlowScope(Label* finallyLabel)
    36843687{
    36853688    // Reclaim free label scopes.
     
    36873690        m_labelScopes.removeLast();
    36883691
    3689     ControlFlowScope scope;
    3690     scope.isFinallyBlock = true;
    3691     FinallyContext context = {
    3692         finallyBlock,
    3693         nullptr,
    3694         nullptr,
    3695         static_cast<unsigned>(m_controlFlowScopeStack.size()),
    3696         static_cast<unsigned>(m_switchContextStack.size()),
    3697         static_cast<unsigned>(m_forInContextStack.size()),
    3698         static_cast<unsigned>(m_tryContextStack.size()),
    3699         static_cast<unsigned>(m_labelScopes.size()),
    3700         static_cast<unsigned>(m_lexicalScopeStack.size()),
    3701         m_finallyDepth,
    3702         m_localScopeDepth
    3703     };
    3704     scope.finallyContext = context;
    3705     m_controlFlowScopeStack.append(scope);
     3692    ControlFlowScope scope(ControlFlowScope::Finally, currentLexicalScopeIndex(), FinallyContext(m_currentFinallyContext, finallyLabel, m_finallyDepth));
     3693    m_controlFlowScopeStack.append(WTFMove(scope));
     3694
    37063695    m_finallyDepth++;
    3707 }
    3708 
    3709 void BytecodeGenerator::pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* node)
    3710 {
    3711     // Reclaim free label scopes.
    3712     while (m_labelScopes.size() && !m_labelScopes.last().refCount())
    3713         m_labelScopes.removeLast();
    3714 
    3715     ControlFlowScope scope;
    3716     scope.isFinallyBlock = true;
    3717     FinallyContext context = {
    3718         nullptr,
    3719         iterator,
    3720         node,
    3721         static_cast<unsigned>(m_controlFlowScopeStack.size()),
    3722         static_cast<unsigned>(m_switchContextStack.size()),
    3723         static_cast<unsigned>(m_forInContextStack.size()),
    3724         static_cast<unsigned>(m_tryContextStack.size()),
    3725         static_cast<unsigned>(m_labelScopes.size()),
    3726         static_cast<unsigned>(m_lexicalScopeStack.size()),
    3727         m_finallyDepth,
    3728         m_localScopeDepth
    3729     };
    3730     scope.finallyContext = context;
    3731     m_controlFlowScopeStack.append(scope);
    3732     m_finallyDepth++;
    3733 }
    3734 
    3735 void BytecodeGenerator::popFinallyControlFlowScope()
     3696    m_currentFinallyContext = &m_controlFlowScopeStack.last().finallyContext;
     3697    return m_currentFinallyContext;
     3698}
     3699
     3700FinallyContext BytecodeGenerator::popFinallyControlFlowScope()
    37363701{
    37373702    ASSERT(m_controlFlowScopeStack.size());
    3738     ASSERT(m_controlFlowScopeStack.last().isFinallyBlock);
    3739     ASSERT(m_controlFlowScopeStack.last().finallyContext.finallyBlock);
    3740     ASSERT(!m_controlFlowScopeStack.last().finallyContext.iterator);
    3741     ASSERT(!m_controlFlowScopeStack.last().finallyContext.enumerationNode);
     3703    ASSERT(m_controlFlowScopeStack.last().isFinallyScope());
    37423704    ASSERT(m_finallyDepth > 0);
    3743     m_controlFlowScopeStack.removeLast();
     3705    ASSERT(m_currentFinallyContext);
     3706    m_currentFinallyContext = m_currentFinallyContext->outerContext();
    37443707    m_finallyDepth--;
    3745 }
    3746 
    3747 void BytecodeGenerator::popIteratorCloseControlFlowScope()
    3748 {
    3749     ASSERT(m_controlFlowScopeStack.size());
    3750     ASSERT(m_controlFlowScopeStack.last().isFinallyBlock);
    3751     ASSERT(!m_controlFlowScopeStack.last().finallyContext.finallyBlock);
    3752     ASSERT(m_controlFlowScopeStack.last().finallyContext.iterator);
    3753     ASSERT(m_controlFlowScopeStack.last().finallyContext.enumerationNode);
    3754     ASSERT(m_finallyDepth > 0);
    3755     m_controlFlowScopeStack.removeLast();
    3756     m_finallyDepth--;
     3708    return m_controlFlowScopeStack.takeLast().finallyContext;
    37573709}
    37583710
     
    38543806    emitMove(m_topMostScope, scopeRegister());
    38553807}
    3856    
    3857 void BytecodeGenerator::emitComplexPopScopes(RegisterID* scope, ControlFlowScope* topScope, ControlFlowScope* bottomScope)
    3858 {
    3859     while (topScope > bottomScope) {
    3860         // First we count the number of dynamic scopes we need to remove to get
    3861         // to a finally block.
    3862         int numberOfNormalScopes = 0;
    3863         while (topScope > bottomScope) {
    3864             if (topScope->isFinallyBlock)
    3865                 break;
    3866             ++numberOfNormalScopes;
    3867             --topScope;
    3868         }
    3869 
    3870         if (numberOfNormalScopes) {
    3871             // We need to remove a number of dynamic scopes to get to the next
    3872             // finally block
    3873             RefPtr<RegisterID> parentScope = newTemporary();
    3874             while (numberOfNormalScopes--) {
    3875                 parentScope = emitGetParentScope(parentScope.get(), scope);
    3876                 emitMove(scope, parentScope.get());
    3877             }
    3878 
    3879             // If topScope == bottomScope then there isn't a finally block left to emit.
    3880             if (topScope == bottomScope)
    3881                 return;
    3882         }
    3883        
    3884         Vector<ControlFlowScope> savedControlFlowScopeStack;
    3885         Vector<SwitchInfo> savedSwitchContextStack;
    3886         Vector<RefPtr<ForInContext>> savedForInContextStack;
    3887         Vector<TryContext> poppedTryContexts;
    3888         Vector<LexicalScopeStackEntry> savedLexicalScopeStack;
    3889         LabelScopeStore savedLabelScopes;
    3890         while (topScope > bottomScope && topScope->isFinallyBlock) {
    3891             RefPtr<Label> beforeFinally = emitLabel(newLabel().get());
    3892            
    3893             // Save the current state of the world while instating the state of the world
    3894             // for the finally block.
    3895             FinallyContext finallyContext = topScope->finallyContext;
    3896             bool flipScopes = finallyContext.controlFlowScopeStackSize != m_controlFlowScopeStack.size();
    3897             bool flipSwitches = finallyContext.switchContextStackSize != m_switchContextStack.size();
    3898             bool flipForIns = finallyContext.forInContextStackSize != m_forInContextStack.size();
    3899             bool flipTries = finallyContext.tryContextStackSize != m_tryContextStack.size();
    3900             bool flipLabelScopes = finallyContext.labelScopesSize != m_labelScopes.size();
    3901             bool flipLexicalScopeStack = finallyContext.lexicalScopeStackSize != m_lexicalScopeStack.size();
    3902             int topScopeIndex = -1;
    3903             int bottomScopeIndex = -1;
    3904             if (flipScopes) {
    3905                 topScopeIndex = topScope - m_controlFlowScopeStack.begin();
    3906                 bottomScopeIndex = bottomScope - m_controlFlowScopeStack.begin();
    3907                 savedControlFlowScopeStack = m_controlFlowScopeStack;
    3908                 m_controlFlowScopeStack.shrink(finallyContext.controlFlowScopeStackSize);
    3909             }
    3910             if (flipSwitches) {
    3911                 savedSwitchContextStack = m_switchContextStack;
    3912                 m_switchContextStack.shrink(finallyContext.switchContextStackSize);
    3913             }
    3914             if (flipForIns) {
    3915                 savedForInContextStack = m_forInContextStack;
    3916                 m_forInContextStack.shrink(finallyContext.forInContextStackSize);
    3917             }
    3918             if (flipTries) {
    3919                 while (m_tryContextStack.size() != finallyContext.tryContextStackSize) {
    3920                     ASSERT(m_tryContextStack.size() > finallyContext.tryContextStackSize);
    3921                     TryContext context = m_tryContextStack.takeLast();
    3922                     TryRange range;
    3923                     range.start = context.start;
    3924                     range.end = beforeFinally;
    3925                     range.tryData = context.tryData;
    3926                     m_tryRanges.append(range);
    3927                     poppedTryContexts.append(context);
    3928                 }
    3929             }
    3930             if (flipLabelScopes) {
    3931                 savedLabelScopes = m_labelScopes;
    3932                 while (m_labelScopes.size() > finallyContext.labelScopesSize)
    3933                     m_labelScopes.removeLast();
    3934             }
    3935             if (flipLexicalScopeStack) {
    3936                 savedLexicalScopeStack = m_lexicalScopeStack;
    3937                 m_lexicalScopeStack.shrink(finallyContext.lexicalScopeStackSize);
    3938             }
    3939             int savedFinallyDepth = m_finallyDepth;
    3940             m_finallyDepth = finallyContext.finallyDepth;
    3941             int savedDynamicScopeDepth = m_localScopeDepth;
    3942             m_localScopeDepth = finallyContext.dynamicScopeDepth;
    3943            
    3944             if (finallyContext.finallyBlock) {
    3945                 // Emit the finally block.
    3946                 emitNode(finallyContext.finallyBlock);
    3947             } else {
    3948                 // Emit the IteratorClose block.
    3949                 ASSERT(finallyContext.iterator);
    3950                 emitIteratorClose(finallyContext.iterator, finallyContext.enumerationNode);
    3951             }
    3952 
    3953             RefPtr<Label> afterFinally = emitLabel(newLabel().get());
    3954            
    3955             // Restore the state of the world.
    3956             if (flipScopes) {
    3957                 m_controlFlowScopeStack = savedControlFlowScopeStack;
    3958                 topScope = &m_controlFlowScopeStack[topScopeIndex]; // assert it's within bounds
    3959                 bottomScope = m_controlFlowScopeStack.begin() + bottomScopeIndex; // don't assert, since it the index might be -1.
    3960             }
    3961             if (flipSwitches)
    3962                 m_switchContextStack = savedSwitchContextStack;
    3963             if (flipForIns)
    3964                 m_forInContextStack = savedForInContextStack;
    3965             if (flipTries) {
    3966                 ASSERT(m_tryContextStack.size() == finallyContext.tryContextStackSize);
    3967                 for (unsigned i = poppedTryContexts.size(); i--;) {
    3968                     TryContext context = poppedTryContexts[i];
    3969                     context.start = afterFinally;
    3970                     m_tryContextStack.append(context);
    3971                 }
    3972                 poppedTryContexts.clear();
    3973             }
    3974             if (flipLabelScopes)
    3975                 m_labelScopes = savedLabelScopes;
    3976             if (flipLexicalScopeStack)
    3977                 m_lexicalScopeStack = savedLexicalScopeStack;
    3978             m_finallyDepth = savedFinallyDepth;
    3979             m_localScopeDepth = savedDynamicScopeDepth;
    3980            
    3981             --topScope;
    3982         }
    3983     }
    3984 }
    3985 
    3986 void BytecodeGenerator::emitPopScopes(RegisterID* scope, int targetScopeDepth)
    3987 {
    3988     ASSERT(labelScopeDepth() - targetScopeDepth >= 0);
    3989 
    3990     size_t scopeDelta = labelScopeDepth() - targetScopeDepth;
    3991     ASSERT(scopeDelta <= m_controlFlowScopeStack.size());
    3992     if (!scopeDelta)
    3993         return;
    3994 
    3995     if (!m_finallyDepth) {
    3996         RefPtr<RegisterID> parentScope = newTemporary();
    3997         while (scopeDelta--) {
    3998             parentScope = emitGetParentScope(parentScope.get(), scope);
    3999             emitMove(scope, parentScope.get());
    4000         }
    4001         return;
    4002     }
    4003 
    4004     emitComplexPopScopes(scope, &m_controlFlowScopeStack.last(), &m_controlFlowScopeStack.last() - scopeDelta);
    4005 }
    4006 
    4007 TryData* BytecodeGenerator::pushTry(Label* start)
     3808
     3809TryData* BytecodeGenerator::pushTry(Label* start, Label* handlerLabel, HandlerType handlerType)
    40083810{
    40093811    TryData tryData;
    4010     tryData.target = newLabel();
    4011     tryData.handlerType = HandlerType::Illegal;
     3812    tryData.target = handlerLabel;
     3813    tryData.handlerType = handlerType;
    40123814    m_tryData.append(tryData);
    40133815    TryData* result = &m_tryData.last();
     
    40223824}
    40233825
    4024 void BytecodeGenerator::popTryAndEmitCatch(TryData* tryData, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType handlerType)
     3826void BytecodeGenerator::popTry(TryData* tryData, Label* end)
    40253827{
    40263828    m_usesExceptions = true;
     
    40343836    m_tryRanges.append(tryRange);
    40353837    m_tryContextStack.removeLast();
    4036    
    4037     emitLabel(tryRange.tryData->target.get());
    4038     tryRange.tryData->handlerType = handlerType;
    4039 
     3838}
     3839
     3840void BytecodeGenerator::emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister)
     3841{
    40403842    emitOpcode(op_catch);
    40413843    instructions().append(exceptionRegister->index());
    40423844    instructions().append(thrownValueRegister->index());
    4043 
    4044     bool foundLocalScope = false;
    4045     for (unsigned i = m_lexicalScopeStack.size(); i--; ) {
    4046         // Note that if we don't find a local scope in the current function/program,
    4047         // we must grab the outer-most scope of this bytecode generation.
    4048         if (m_lexicalScopeStack[i].m_scope) {
    4049             foundLocalScope = true;
    4050             emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope);
    4051             break;
    4052         }
    4053     }
    4054     if (!foundLocalScope)
    4055         emitMove(scopeRegister(), m_topMostScope);
     3845}
     3846
     3847void BytecodeGenerator::restoreScopeRegister(int lexicalScopeIndex)
     3848{
     3849    if (lexicalScopeIndex == CurrentLexicalScopeIndex)
     3850        return; // No change needed.
     3851
     3852    if (lexicalScopeIndex != OutermostLexicalScopeIndex) {
     3853        ASSERT(lexicalScopeIndex < static_cast<int>(m_lexicalScopeStack.size()));
     3854        int endIndex = lexicalScopeIndex + 1;
     3855        for (size_t i = endIndex; i--; ) {
     3856            if (m_lexicalScopeStack[i].m_scope) {
     3857                emitMove(scopeRegister(), m_lexicalScopeStack[i].m_scope);
     3858                return;
     3859            }
     3860        }
     3861    }
     3862    // Note that if we don't find a local scope in the current function/program,
     3863    // we must grab the outer-most scope of this bytecode generation.
     3864    emitMove(scopeRegister(), m_topMostScope);
     3865}
     3866
     3867void BytecodeGenerator::restoreScopeRegister()
     3868{
     3869    restoreScopeRegister(currentLexicalScopeIndex());
     3870}
     3871
     3872int BytecodeGenerator::labelScopeDepthToLexicalScopeIndex(int targetLabelScopeDepth)
     3873{
     3874    ASSERT(labelScopeDepth() - targetLabelScopeDepth >= 0);
     3875    size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth;
     3876    ASSERT(scopeDelta <= m_controlFlowScopeStack.size());
     3877    if (!scopeDelta)
     3878        return CurrentLexicalScopeIndex;
     3879
     3880    ControlFlowScope& targetScope = m_controlFlowScopeStack[targetLabelScopeDepth];
     3881    return targetScope.lexicalScopeIndex;
    40563882}
    40573883
     
    40623888
    40633889int BytecodeGenerator::labelScopeDepth() const
    4064 {
    4065     return localScopeDepth() + m_finallyDepth;
     3890{
     3891    int depth = localScopeDepth() + m_finallyDepth;
     3892    ASSERT(depth == static_cast<int>(m_controlFlowScopeStack.size()));
     3893    return depth;
    40663894}
    40673895
     
    41333961void BytecodeGenerator::pushLocalControlFlowScope()
    41343962{
    4135     ControlFlowScope scope;
    4136     scope.isFinallyBlock = false;
    4137     m_controlFlowScopeStack.append(scope);
     3963    ControlFlowScope scope(ControlFlowScope::Label, currentLexicalScopeIndex());
     3964    m_controlFlowScopeStack.append(WTFMove(scope));
    41383965    m_localScopeDepth++;
    41393966}
     
    41423969{
    41433970    ASSERT(m_controlFlowScopeStack.size());
    4144     ASSERT(!m_controlFlowScopeStack.last().isFinallyBlock);
     3971    ASSERT(!m_controlFlowScopeStack.last().isFinallyScope());
    41453972    m_controlFlowScopeStack.removeLast();
    41463973    m_localScopeDepth--;
     
    42974124    return false;
    42984125}
    4299    
     4126
    43004127void BytecodeGenerator::emitEnumeration(ThrowableExpressionData* node, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack, ForOfNode* forLoopNode, RegisterID* forLoopSymbolTable)
    43014128{
     4129    FinallyRegistersScope finallyRegistersScope(*this);
     4130
    43024131    RefPtr<RegisterID> subject = newTemporary();
    43034132    emitNode(subject.get(), subjectNode);
     
    43104139
    43114140    RefPtr<Label> loopDone = newLabel();
     4141    RefPtr<Label> tryStartLabel = newLabel();
     4142    RefPtr<Label> finallyViaThrowLabel = newLabel();
     4143    RefPtr<Label> finallyLabel = newLabel();
     4144    RefPtr<Label> catchLabel = newLabel();
     4145    RefPtr<Label> endCatchLabel = newLabel();
     4146
    43124147    // RefPtr<Register> iterator's lifetime must be longer than IteratorCloseContext.
    4313     pushIteratorCloseControlFlowScope(iterator.get(), node);
     4148    FinallyContext* finallyContext = pushFinallyControlFlowScope(finallyLabel.get());
     4149
    43144150    {
    43154151        LabelScopePtr scope = newLabelScope(LabelScope::Loop);
     
    43234159        emitLoopHint();
    43244160
    4325         RefPtr<Label> tryStartLabel = newLabel();
    43264161        emitLabel(tryStartLabel.get());
    4327         TryData* tryData = pushTry(tryStartLabel.get());
     4162        TryData* tryData = pushTry(tryStartLabel.get(), finallyViaThrowLabel.get(), HandlerType::SynthesizedFinally);
    43284163        callBack(*this, value.get());
    43294164        emitJump(scope->continueTarget());
    43304165
    4331         // IteratorClose sequence for throw-ed control flow.
     4166        // IteratorClose sequence for abrupt completions.
    43324167        {
    4333             RefPtr<Label> catchHere = emitLabel(newLabel().get());
    4334             RefPtr<RegisterID> exceptionRegister = newTemporary();
    4335             RefPtr<RegisterID> thrownValueRegister = newTemporary();
    4336             popTryAndEmitCatch(tryData, exceptionRegister.get(),
    4337                 thrownValueRegister.get(), catchHere.get(), HandlerType::SynthesizedFinally);
    4338 
    4339             RefPtr<Label> catchDone = newLabel();
     4168            // Finally block for the enumeration.
     4169            emitLabel(finallyViaThrowLabel.get());
     4170            popTry(tryData, finallyViaThrowLabel.get());
     4171
     4172            RegisterID* unused = newTemporary();
     4173            emitCatch(finallyActionRegister(), unused);
     4174            // Setting the finallyActionRegister to the caught exception here implies CompletionType::Throw.
     4175
     4176            emitLabel(finallyLabel.get());
     4177            restoreScopeRegister();
     4178
     4179            RefPtr<Label> finallyDone = newLabel();
    43404180
    43414181            RefPtr<RegisterID> returnMethod = emitGetById(newTemporary(), iterator.get(), propertyNames().returnKeyword);
    4342             emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), catchDone.get());
     4182            emitJumpIfTrue(emitIsUndefined(newTemporary(), returnMethod.get()), finallyDone.get());
     4183
     4184            RefPtr<RegisterID> originalFinallyActionRegister = newTemporary();
     4185            emitMove(originalFinallyActionRegister.get(), finallyActionRegister());
    43434186
    43444187            RefPtr<Label> returnCallTryStart = newLabel();
    43454188            emitLabel(returnCallTryStart.get());
    4346             TryData* returnCallTryData = pushTry(returnCallTryStart.get());
     4189            TryData* returnCallTryData = pushTry(returnCallTryStart.get(), catchLabel.get(), HandlerType::SynthesizedCatch);
    43474190
    43484191            CallArguments returnArguments(*this, nullptr);
    43494192            emitMove(returnArguments.thisRegister(), iterator.get());
    43504193            emitCall(value.get(), returnMethod.get(), NoExpectedFunction, returnArguments, node->divot(), node->divotStart(), node->divotEnd(), DebuggableCall::No);
    4351 
    4352             emitLabel(catchDone.get());
    4353             emitThrow(exceptionRegister.get());
    4354 
    4355             // Absorb exception.
    4356             popTryAndEmitCatch(returnCallTryData, newTemporary(),
    4357                 newTemporary(), catchDone.get(), HandlerType::SynthesizedFinally);
    4358             emitThrow(exceptionRegister.get());
     4194            emitJumpIfTrue(emitIsObject(newTemporary(), value.get()), finallyDone.get());
     4195            emitThrowTypeError(ASCIILiteral("Iterator result interface is not an object."));
     4196
     4197            emitLabel(finallyDone.get());
     4198            emitFinallyCompletion(*finallyContext, endCatchLabel.get());
     4199
     4200            popTry(returnCallTryData, finallyDone.get());
     4201
     4202            // Catch block for exceptions that may be thrown while calling the return
     4203            // handler in the enumeration finally block. The only reason we need this
     4204            // catch block is because if entered the above finally block due to a thrown
     4205            // exception, then we want to re-throw the original exception on exiting
     4206            // the finally block. Otherwise, we'll let any new exception pass through.
     4207            {
     4208                emitLabel(catchLabel.get());
     4209                RefPtr<RegisterID> exceptionRegister = newTemporary();
     4210                RegisterID* unused = newTemporary();
     4211                emitCatch(exceptionRegister.get(), unused);
     4212                restoreScopeRegister();
     4213
     4214                RefPtr<Label> throwLabel = newLabel();
     4215                emitJumpIfCompletionTypeIsThrow(originalFinallyActionRegister.get(), throwLabel.get());
     4216                emitMove(originalFinallyActionRegister.get(), exceptionRegister.get());
     4217
     4218                emitLabel(throwLabel.get());
     4219                emitThrow(originalFinallyActionRegister.get());
     4220
     4221                emitLabel(endCatchLabel.get());
     4222            }
    43594223        }
    43604224
     
    43774241
    43784242    // IteratorClose sequence for break-ed control flow.
    4379     popIteratorCloseControlFlowScope();
     4243    popFinallyControlFlowScope();
    43804244    emitIteratorClose(iterator.get(), node);
    43814245    emitLabel(loopDone.get());
     
    44934357{
    44944358    emitOpcode(op_is_object);
     4359    instructions().append(dst->index());
     4360    instructions().append(src->index());
     4361    return dst;
     4362}
     4363
     4364RegisterID* BytecodeGenerator::emitIsNumber(RegisterID* dst, RegisterID* src)
     4365{
     4366    emitOpcode(op_is_number);
    44954367    instructions().append(dst->index());
    44964368    instructions().append(src->index());
     
    47854657    {
    47864658        RefPtr<RegisterID> returnRegister = generatorValueRegister();
    4787         if (isInFinallyBlock()) {
    4788             returnRegister = emitMove(newTemporary(), returnRegister.get());
    4789             emitPopScopes(scopeRegister(), 0);
    4790         }
    4791         emitReturn(returnRegister.get());
     4659        bool hasFinally = emitReturnViaFinallyIfNeeded(returnRegister.get());
     4660        if (!hasFinally)
     4661            emitReturn(returnRegister.get());
    47924662    }
    47934663
     
    48924762
    48934763                    emitLabel(returnSequence.get());
    4894                     if (isInFinallyBlock())
    4895                         emitPopScopes(scopeRegister(), 0);
    4896                     emitReturn(value.get());
     4764                    bool hasFinally = emitReturnViaFinallyIfNeeded(value.get());
     4765                    if (!hasFinally)
     4766                        emitReturn(value.get());
    48974767                }
    48984768
     
    49224792    RegisterID* completedState = emitLoad(nullptr, jsNumber(state));
    49234793    emitPutById(generatorRegister(), propertyNames().builtinNames().generatorStatePrivateName(), completedState);
     4794}
     4795
     4796bool BytecodeGenerator::emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget)
     4797{
     4798    ASSERT(labelScopeDepth() - targetLabelScopeDepth >= 0);
     4799    size_t scopeDelta = labelScopeDepth() - targetLabelScopeDepth;
     4800    ASSERT(scopeDelta <= m_controlFlowScopeStack.size());
     4801    if (!scopeDelta)
     4802        return nullptr; // No finallys to thread through.
     4803
     4804    ControlFlowScope* topScope = &m_controlFlowScopeStack.last();
     4805    ControlFlowScope* bottomScope = &m_controlFlowScopeStack.last() - scopeDelta;
     4806
     4807    FinallyContext* innermostFinallyContext = nullptr;
     4808    FinallyContext* outermostFinallyContext = nullptr;
     4809    while (topScope > bottomScope) {
     4810        if (topScope->isFinallyScope()) {
     4811            FinallyContext* finallyContext = &topScope->finallyContext;
     4812            if (!innermostFinallyContext)
     4813                innermostFinallyContext = finallyContext;
     4814            outermostFinallyContext = finallyContext;
     4815            finallyContext->incNumberOfBreaksOrContinues();
     4816        }
     4817        --topScope;
     4818    }
     4819    if (!outermostFinallyContext)
     4820        return false; // No finallys to thread through.
     4821
     4822    int jumpID = bytecodeOffsetToJumpID(instructions().size());
     4823    int lexicalScopeIndex = labelScopeDepthToLexicalScopeIndex(targetLabelScopeDepth);
     4824    outermostFinallyContext->registerJump(jumpID, lexicalScopeIndex, jumpTarget);
     4825
     4826    emitSetFinallyActionToJumpID(jumpID);
     4827    emitJump(innermostFinallyContext->finallyLabel());
     4828    return true; // We'll be jumping to a finally block.
     4829}
     4830
     4831bool BytecodeGenerator::emitReturnViaFinallyIfNeeded(RegisterID* returnRegister)
     4832{
     4833    if (!m_controlFlowScopeStack.size())
     4834        return false; // No finallys to thread through.
     4835
     4836    ControlFlowScope* topScope = &m_controlFlowScopeStack.last();
     4837    ControlFlowScope* bottomScope = &m_controlFlowScopeStack.first();
     4838
     4839    FinallyContext* innermostFinallyContext = nullptr;
     4840    while (topScope >= bottomScope) {
     4841        if (topScope->isFinallyScope()) {
     4842            FinallyContext* finallyContext = &topScope->finallyContext;
     4843            if (!innermostFinallyContext)
     4844                innermostFinallyContext = finallyContext;
     4845            finallyContext->setHandlesReturns();
     4846        }
     4847        --topScope;
     4848    }
     4849    if (!innermostFinallyContext)
     4850        return false; // No finallys to thread through.
     4851
     4852    emitSetFinallyActionToReturnCompletion();
     4853    emitSetFinallyReturnValueRegister(returnRegister);
     4854    emitJump(innermostFinallyContext->finallyLabel());
     4855    return true; // We'll be jumping to a finally block.
     4856}
     4857
     4858void BytecodeGenerator::emitFinallyCompletion(FinallyContext& context, Label* normalCompletionLabel)
     4859{
     4860    // FIXME: switch the finallyActionRegister to only store int values for all CompletionTypes. This is more optimal for JIT type speculation.
     4861    // https://bugs.webkit.org/show_bug.cgi?id=165979
     4862    emitJumpIfFinallyActionIsNormalCompletion(normalCompletionLabel);
     4863
     4864    if (context.numberOfBreaksOrContinues() || context.handlesReturns()) {
     4865        FinallyContext* outerContext = context.outerContext();
     4866        if (outerContext) {
     4867            // We are not the outermost finally.
     4868            size_t numberOfJumps = context.numberOfJumps();
     4869            for (size_t i = 0; i < numberOfJumps; i++) {
     4870                RefPtr<Label> nextLabel = newLabel();
     4871                auto& jump = context.jumps(i);
     4872                emitJumpIfFinallyActionIsNotJump(jump.jumpID, nextLabel.get());
     4873
     4874                restoreScopeRegister(jump.targetLexicalScopeIndex);
     4875                emitSetFinallyActionToNormalCompletion();
     4876                emitJump(jump.targetLabel.get());
     4877
     4878                emitLabel(nextLabel.get());
     4879            }
     4880
     4881            bool hasBreaksOrContinuesNotCoveredByJumps = context.numberOfBreaksOrContinues() > numberOfJumps;
     4882            if (hasBreaksOrContinuesNotCoveredByJumps || context.handlesReturns())
     4883                emitJumpIfFinallyActionIsNotThrowCompletion(outerContext->finallyLabel());
     4884
     4885        } else {
     4886            // We are the outermost finally.
     4887            size_t numberOfJumps = context.numberOfJumps();
     4888            ASSERT(numberOfJumps == context.numberOfBreaksOrContinues());
     4889
     4890            for (size_t i = 0; i < numberOfJumps; i++) {
     4891                RefPtr<Label> nextLabel = newLabel();
     4892                auto& jump = context.jumps(i);
     4893                emitJumpIfFinallyActionIsNotJump(jump.jumpID, nextLabel.get());
     4894
     4895                restoreScopeRegister(jump.targetLexicalScopeIndex);
     4896                emitSetFinallyActionToNormalCompletion();
     4897                emitJump(jump.targetLabel.get());
     4898
     4899                emitLabel(nextLabel.get());
     4900            }
     4901
     4902            if (context.handlesReturns()) {
     4903                RefPtr<Label> notReturnLabel = newLabel();
     4904                emitJumpIfFinallyActionIsNotReturnCompletion(notReturnLabel.get());
     4905
     4906                emitWillLeaveCallFrameDebugHook();
     4907                emitReturn(finallyReturnValueRegister(), ReturnFrom::Finally);
     4908               
     4909                emitLabel(notReturnLabel.get());
     4910            }
     4911        }
     4912    }
     4913    emitThrow(finallyActionRegister());
     4914}
     4915
     4916bool BytecodeGenerator::allocateFinallyRegisters()
     4917{
     4918    if (m_finallyActionRegister)
     4919        return false;
     4920
     4921    ASSERT(!m_finallyReturnValueRegister);
     4922    m_finallyActionRegister = newTemporary();
     4923    m_finallyReturnValueRegister = newTemporary();
     4924
     4925    emitSetFinallyActionToNormalCompletion();
     4926    emitMoveEmptyValue(m_finallyReturnValueRegister.get());
     4927    return true;
     4928}
     4929
     4930void BytecodeGenerator::releaseFinallyRegisters()
     4931{
     4932    ASSERT(m_finallyActionRegister && m_finallyReturnValueRegister);
     4933    m_finallyActionRegister = nullptr;
     4934    m_finallyReturnValueRegister = nullptr;
     4935}
     4936
     4937void BytecodeGenerator::emitCompareFinallyActionAndJumpIf(OpcodeID compareOpcode, int value, Label* jumpTarget)
     4938{
     4939    RefPtr<RegisterID> tempRegister = newTemporary();
     4940    RegisterID* valueConstant = addConstantValue(JSValue(value));
     4941    OperandTypes operandTypes = OperandTypes(ResultType::numberTypeIsInt32(), ResultType::unknownType());
     4942
     4943    auto equivalenceResult = emitBinaryOp(compareOpcode, tempRegister.get(), valueConstant, finallyActionRegister(), operandTypes);
     4944    emitJumpIfTrue(equivalenceResult, jumpTarget);
    49244945}
    49254946
  • trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h

    r209871 r209952  
    8181    };
    8282
     83    struct FinallyJump {
     84        FinallyJump(int jumpID, int targetLexicalScopeIndex, Label* targetLabel)
     85            : jumpID(jumpID)
     86            , targetLexicalScopeIndex(targetLexicalScopeIndex)
     87            , targetLabel(targetLabel)
     88        { }
     89
     90        int jumpID { 0 };
     91        int targetLexicalScopeIndex { 0 };
     92        RefPtr<Label> targetLabel;
     93    };
     94
    8395    struct FinallyContext {
    84         StatementNode* finallyBlock;
    85         RegisterID* iterator;
    86         ThrowableExpressionData* enumerationNode;
    87         unsigned controlFlowScopeStackSize;
    88         unsigned switchContextStackSize;
    89         unsigned forInContextStackSize;
    90         unsigned tryContextStackSize;
    91         unsigned labelScopesSize;
    92         unsigned lexicalScopeStackSize;
    93         int finallyDepth;
    94         int dynamicScopeDepth;
     96        FinallyContext() { }
     97        FinallyContext(FinallyContext* outerContext, Label* finallyLabel, int finallyDepth)
     98            : m_outerContext(outerContext)
     99            , m_finallyLabel(finallyLabel)
     100            , m_finallyDepth(finallyDepth)
     101        {
     102            ASSERT(!m_jumps || m_jumps->isEmpty());
     103        }
     104
     105        FinallyContext* outerContext() const { return m_outerContext; }
     106        Label* finallyLabel() const { return m_finallyLabel; }
     107        int depth() const { return m_finallyDepth; }
     108
     109        unsigned numberOfBreaksOrContinues() const { return m_numberOfBreaksOrContinues; }
     110        void incNumberOfBreaksOrContinues()
     111        {
     112            ASSERT(m_numberOfBreaksOrContinues < INT_MAX);
     113            m_numberOfBreaksOrContinues++;
     114        }
     115
     116        bool handlesReturns() const { return m_handlesReturns; }
     117        void setHandlesReturns() { m_handlesReturns = true; }
     118
     119        void registerJump(int jumpID, int lexicalScopeIndex, Label* targetLabel)
     120        {
     121            if (!m_jumps)
     122                m_jumps = std::make_unique<Vector<FinallyJump>>();
     123            m_jumps->append(FinallyJump(jumpID, lexicalScopeIndex, targetLabel));
     124        }
     125
     126        size_t numberOfJumps() const { return m_jumps ? m_jumps->size() : 0; }
     127        FinallyJump& jumps(size_t i) { return (*m_jumps)[i]; }
     128
     129    private:
     130        FinallyContext* m_outerContext { nullptr };
     131        Label* m_finallyLabel { nullptr };
     132        int m_finallyDepth { 0 };
     133        unsigned m_numberOfBreaksOrContinues { 0 };
     134        bool m_handlesReturns { false };
     135        std::unique_ptr<Vector<FinallyJump>> m_jumps;
    95136    };
    96137
    97138    struct ControlFlowScope {
    98         bool isFinallyBlock;
     139        typedef uint8_t Type;
     140        enum {
     141            Label,
     142            Finally
     143        };
     144        ControlFlowScope(Type type, int lexicalScopeIndex, FinallyContext&& finallyContext = FinallyContext())
     145            : type(type)
     146            , lexicalScopeIndex(lexicalScopeIndex)
     147            , finallyContext(std::forward<FinallyContext>(finallyContext))
     148        { }
     149
     150        bool isLabelScope() const { return type == Label; }
     151        bool isFinallyScope() const { return type == Finally; }
     152
     153        Type type;
     154        int lexicalScopeIndex;
    99155        FinallyContext finallyContext;
    100156    };
     
    606662        RegisterID* emitGetTemplateObject(RegisterID* dst, TaggedTemplateNode*);
    607663
    608         RegisterID* emitReturn(RegisterID* src);
     664        enum class ReturnFrom { Normal, Finally };
     665        RegisterID* emitReturn(RegisterID* src, ReturnFrom = ReturnFrom::Normal);
    609666        RegisterID* emitEnd(RegisterID* src) { return emitUnaryNoDstOp(op_end, src); }
    610667
     
    627684        PassRefPtr<Label> emitJumpIfNotFunctionCall(RegisterID* cond, Label* target);
    628685        PassRefPtr<Label> emitJumpIfNotFunctionApply(RegisterID* cond, Label* target);
    629         void emitPopScopes(RegisterID* srcDst, int targetScopeDepth);
    630686
    631687        void emitEnter();
     
    650706        RegisterID* emitIsSet(RegisterID* dst, RegisterID* src) { return emitIsCellWithType(dst, src, JSSetType); }
    651707        RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
     708        RegisterID* emitIsNumber(RegisterID* dst, RegisterID* src);
    652709        RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
    653710        RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
     
    664721
    665722        // Start a try block. 'start' must have been emitted.
    666         TryData* pushTry(Label* start);
     723        TryData* pushTry(Label* start, Label* handlerLabel, HandlerType);
    667724        // End a try block. 'end' must have been emitted.
    668         void popTryAndEmitCatch(TryData*, RegisterID* exceptionRegister, RegisterID* thrownValueRegister, Label* end, HandlerType);
     725        void popTry(TryData*, Label* end);
     726        void emitCatch(RegisterID* exceptionRegister, RegisterID* thrownValueRegister);
     727
     728    private:
     729        static const int CurrentLexicalScopeIndex = -2;
     730        static const int OutermostLexicalScopeIndex = -1;
     731
     732    public:
     733        void restoreScopeRegister();
     734        void restoreScopeRegister(int lexicalScopeIndex);
     735
     736        int currentLexicalScopeIndex() const
     737        {
     738            int size = static_cast<int>(m_lexicalScopeStack.size());
     739            ASSERT(static_cast<size_t>(size) == m_lexicalScopeStack.size());
     740            ASSERT(size >= 0);
     741            int index = size - 1;
     742            return index;
     743        }
     744
     745        int labelScopeDepthToLexicalScopeIndex(int labelScopeDepth);
    669746
    670747        void emitThrow(RegisterID* exc)
     
    699776        void emitWillLeaveCallFrameDebugHook();
    700777
    701         bool isInFinallyBlock() { return m_finallyDepth > 0; }
    702 
    703         void pushFinallyControlFlowScope(StatementNode* finallyBlock);
    704         void popFinallyControlFlowScope();
    705         void pushIteratorCloseControlFlowScope(RegisterID* iterator, ThrowableExpressionData* enumerationNode);
    706         void popIteratorCloseControlFlowScope();
     778        class FinallyRegistersScope {
     779        public:
     780            FinallyRegistersScope(BytecodeGenerator& generator, bool needFinallyRegisters = true)
     781                : m_generator(generator)
     782            {
     783                if (needFinallyRegisters && m_generator.allocateFinallyRegisters())
     784                    m_needToReleaseOnDestruction = true;
     785            }
     786            ~FinallyRegistersScope()
     787            {
     788                if (m_needToReleaseOnDestruction)
     789                    m_generator.releaseFinallyRegisters();
     790            }
     791
     792        private:
     793            BytecodeGenerator& m_generator;
     794            bool m_needToReleaseOnDestruction { false };
     795        };
     796
     797        RegisterID* finallyActionRegister() const
     798        {
     799            ASSERT(m_finallyActionRegister);
     800            return m_finallyActionRegister.get();
     801        }
     802        RegisterID* finallyReturnValueRegister() const
     803        {
     804            ASSERT(m_finallyReturnValueRegister);
     805            return m_finallyReturnValueRegister.get();
     806        }
     807
     808        void emitSetFinallyActionToNormalCompletion()
     809        {
     810            emitMoveEmptyValue(m_finallyActionRegister.get());
     811        }
     812        void emitSetFinallyActionToReturnCompletion()
     813        {
     814            emitLoad(finallyActionRegister(), JSValue(static_cast<int>(CompletionType::Return)));
     815        }
     816        void emitSetFinallyActionToJumpID(int jumpID)
     817        {
     818            emitLoad(finallyActionRegister(), JSValue(jumpID));
     819        }
     820        void emitSetFinallyReturnValueRegister(RegisterID* reg)
     821        {
     822            emitMove(finallyReturnValueRegister(), reg);
     823        }
     824
     825        void emitJumpIfFinallyActionIsNormalCompletion(Label* jumpTarget)
     826        {
     827            emitJumpIfTrue(emitIsEmpty(newTemporary(), finallyActionRegister()), jumpTarget);
     828        }
     829
     830        void emitJumpIfFinallyActionIsNotJump(int jumpID, Label* jumpTarget)
     831        {
     832            emitCompareFinallyActionAndJumpIf(op_nstricteq, jumpID, jumpTarget);
     833        }
     834
     835        void emitJumpIfFinallyActionIsReturnCompletion(Label* jumpTarget)
     836        {
     837            emitCompareFinallyActionAndJumpIf(op_stricteq, static_cast<int>(CompletionType::Return), jumpTarget);
     838        }
     839        void emitJumpIfFinallyActionIsNotReturnCompletion(Label* jumpTarget)
     840        {
     841            emitCompareFinallyActionAndJumpIf(op_nstricteq, static_cast<int>(CompletionType::Return), jumpTarget);
     842        }
     843
     844        void emitJumpIfFinallyActionIsNotThrowCompletion(Label* jumpTarget)
     845        {
     846            emitJumpIfTrue(emitIsNumber(newTemporary(), finallyActionRegister()), jumpTarget);
     847        }
     848        void emitJumpIfCompletionTypeIsThrow(RegisterID* reg, Label* jumpTarget)
     849        {
     850            emitJumpIfFalse(emitIsNumber(newTemporary(), reg), jumpTarget);
     851        }
     852
     853        bool emitJumpViaFinallyIfNeeded(int targetLabelScopeDepth, Label* jumpTarget);
     854        bool emitReturnViaFinallyIfNeeded(RegisterID* returnRegister);
     855        void emitFinallyCompletion(FinallyContext&, Label* normalCompletionLabel);
     856
     857    private:
     858        void emitCompareFinallyActionAndJumpIf(OpcodeID compareOpcode, int value, Label* jumpTarget);
     859
     860        int bytecodeOffsetToJumpID(unsigned offset)
     861        {
     862            int jumpID = offset + static_cast<int>(CompletionType::NumberOfTypes);
     863            ASSERT(jumpID >= static_cast<int>(CompletionType::NumberOfTypes));
     864            return jumpID;
     865        }
     866
     867        bool allocateFinallyRegisters();
     868        void releaseFinallyRegisters();
     869
     870    public:
     871        FinallyContext* pushFinallyControlFlowScope(Label* finallyLabel);
     872        FinallyContext popFinallyControlFlowScope();
    707873
    708874        void pushIndexedForInScope(RegisterID* local, RegisterID* index);
     
    798964        void allocateCalleeSaveSpace();
    799965        void allocateAndEmitScope();
    800         void emitComplexPopScopes(RegisterID*, ControlFlowScope* topScope, ControlFlowScope* bottomScope);
    801966
    802967        typedef HashMap<double, JSValue> NumberMap;
     
    9361101        RegisterID* m_promiseCapabilityRegister { nullptr };
    9371102
     1103        // The spec at https://tc39.github.io/ecma262/#sec-completion-record-specification-type says
     1104        // that there are 5 types of completions. Conceptually, we'll set m_finallyActionRegister
     1105        // to one of these completion types. However, to optimize our implementation, we'll encode
     1106        // these type info as follows:
     1107        //
     1108        //     CompletionType::Normal   - m_finallyActionRegister is empty.
     1109        //     CompletionType::Break    - m_finallyActionRegister is an int JSValue jumpID.
     1110        //     CompletionType::Continue - m_finallyActionRegister is an int JSValue jumpID.
     1111        //     CompletionType::Return   - m_finallyActionRegister is the Return enum as an int JSValue.
     1112        //     CompletionType::Throw    - m_finallyActionRegister is the Exception object to rethrow.
     1113        //
     1114        // Hence, of the 5 completion types, only the CompletionType::Return enum value is used in
     1115        // our implementation. The rest are just provided for completeness.
     1116
     1117        enum class CompletionType : int {
     1118            Normal,
     1119            Break,
     1120            Continue,
     1121            Return,
     1122            Throw,
     1123
     1124            NumberOfTypes
     1125        };
     1126
     1127        RefPtr<RegisterID> m_finallyActionRegister;
     1128        RefPtr<RegisterID> m_finallyReturnValueRegister;
     1129
     1130        FinallyContext* m_currentFinallyContext { nullptr };
     1131
    9381132        SegmentedVector<RegisterID*, 16> m_localRegistersForCalleeSaveRegisters;
    9391133        SegmentedVector<RegisterID, 32> m_constantPoolRegisters;
     
    9501144        void popLocalControlFlowScope();
    9511145
    952         Vector<ControlFlowScope, 0, UnsafeVectorOverflow> m_controlFlowScopeStack;
     1146        // FIXME: Restore overflow checking with UnsafeVectorOverflow once SegmentVector supports it.
     1147        // https://bugs.webkit.org/show_bug.cgi?id=165980
     1148        SegmentedVector<ControlFlowScope, 16> m_controlFlowScopeStack;
    9531149        Vector<SwitchInfo> m_switchContextStack;
    9541150        Vector<RefPtr<ForInContext>> m_forInContextStack;
  • trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp

    r209728 r209952  
    29992999    ASSERT(scope);
    30003000
    3001     generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth());
    3002     generator.emitJump(scope->continueTarget());
     3001    bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope->scopeDepth(), scope->continueTarget());
     3002    if (!hasFinally) {
     3003        int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope->scopeDepth());
     3004        generator.restoreScopeRegister(lexicalScopeIndex);
     3005        generator.emitJump(scope->continueTarget());
     3006    }
    30033007
    30043008    generator.emitProfileControlFlow(endOffset());
     
    30263030    ASSERT(scope);
    30273031
    3028     generator.emitPopScopes(generator.scopeRegister(), scope->scopeDepth());
    3029     generator.emitJump(scope->breakTarget());
     3032    bool hasFinally = generator.emitJumpViaFinallyIfNeeded(scope->scopeDepth(), scope->breakTarget());
     3033    if (!hasFinally) {
     3034        int lexicalScopeIndex = generator.labelScopeDepthToLexicalScopeIndex(scope->scopeDepth());
     3035        generator.restoreScopeRegister(lexicalScopeIndex);
     3036        generator.emitJump(scope->breakTarget());
     3037    }
    30303038
    30313039    generator.emitProfileControlFlow(endOffset());
     
    30443052
    30453053    generator.emitProfileType(returnRegister.get(), ProfileTypeBytecodeFunctionReturnStatement, divotStart(), divotEnd());
    3046     if (generator.isInFinallyBlock()) {
    3047         returnRegister = generator.emitMove(generator.newTemporary(), returnRegister.get());
    3048         generator.emitPopScopes(generator.scopeRegister(), 0);
    3049     }
    3050 
    3051     generator.emitWillLeaveCallFrameDebugHook();
    3052     generator.emitReturn(returnRegister.get());
    3053     generator.emitProfileControlFlow(endOffset()); 
     3054
     3055    bool hasFinally = generator.emitReturnViaFinallyIfNeeded(returnRegister.get());
     3056    if (!hasFinally) {
     3057        generator.emitWillLeaveCallFrameDebugHook();
     3058        generator.emitReturn(returnRegister.get());
     3059    }
     3060
     3061    generator.emitProfileControlFlow(endOffset());
    30543062    // Emitting an unreachable return here is needed in case this op_profile_control_flow is the
    30553063    // last opcode in a CodeBlock because a CodeBlock's instructions must end with a terminal opcode.
     
    32803288
    32813289    ASSERT(m_catchBlock || m_finallyBlock);
     3290    BytecodeGenerator::FinallyRegistersScope finallyRegistersScope(generator, m_finallyBlock);
     3291
     3292    RefPtr<Label> catchLabel;
     3293    RefPtr<Label> catchEndLabel;
     3294    RefPtr<Label> finallyViaThrowLabel;
     3295    RefPtr<Label> finallyLabel;
     3296    RefPtr<Label> finallyEndLabel;
    32823297
    32833298    RefPtr<Label> tryStartLabel = generator.newLabel();
    32843299    generator.emitLabel(tryStartLabel.get());
    3285    
     3300
     3301    if (m_finallyBlock) {
     3302        finallyViaThrowLabel = generator.newLabel();
     3303        finallyLabel = generator.newLabel();
     3304        finallyEndLabel = generator.newLabel();
     3305
     3306        generator.pushFinallyControlFlowScope(finallyLabel.get());
     3307    }
     3308    if (m_catchBlock) {
     3309        catchLabel = generator.newLabel();
     3310        catchEndLabel = generator.newLabel();
     3311    }
     3312
     3313    Label* tryHandlerLabel = m_catchBlock ? catchLabel.get() : finallyViaThrowLabel.get();
     3314    HandlerType tryHandlerType = m_catchBlock ? HandlerType::Catch : HandlerType::Finally;
     3315    TryData* tryData = generator.pushTry(tryStartLabel.get(), tryHandlerLabel, tryHandlerType);
     3316
     3317    generator.emitNode(dst, m_tryBlock);
     3318
     3319    // The finallyActionRegister is an empty value by default, which implies CompletionType::Normal.
    32863320    if (m_finallyBlock)
    3287         generator.pushFinallyControlFlowScope(m_finallyBlock);
    3288     TryData* tryData = generator.pushTry(tryStartLabel.get());
    3289 
    3290     generator.emitNode(dst, m_tryBlock);
     3321        generator.emitJump(finallyLabel.get());
     3322    else
     3323        generator.emitJump(catchEndLabel.get());
     3324
     3325    RefPtr<Label> endTryLabel = generator.emitLabel(generator.newLabel().get());
     3326    generator.popTry(tryData, endTryLabel.get());
    32913327
    32923328    if (m_catchBlock) {
    3293         RefPtr<Label> catchEndLabel = generator.newLabel();
    3294        
    3295         // Normal path: jump over the catch block.
    3296         generator.emitJump(catchEndLabel.get());
    3297 
    32983329        // Uncaught exception path: the catch block.
    3299         RefPtr<Label> here = generator.emitLabel(generator.newLabel().get());
    3300         RefPtr<RegisterID> exceptionRegister = generator.newTemporary();
     3330        generator.emitLabel(catchLabel.get());
    33013331        RefPtr<RegisterID> thrownValueRegister = generator.newTemporary();
    3302         generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), here.get(), HandlerType::Catch);
    3303        
     3332        RegisterID* unused = generator.newTemporary();
     3333        generator.emitCatch(unused, thrownValueRegister.get());
     3334        generator.restoreScopeRegister();
     3335
     3336        TryData* tryData = nullptr;
    33043337        if (m_finallyBlock) {
    33053338            // If the catch block throws an exception and we have a finally block, then the finally
    33063339            // block should "catch" that exception.
    3307             tryData = generator.pushTry(here.get());
     3340            tryData = generator.pushTry(catchLabel.get(), finallyViaThrowLabel.get(), HandlerType::Finally);
    33083341        }
    33093342
     
    33173350        generator.emitLoad(thrownValueRegister.get(), jsUndefined());
    33183351        generator.emitPopCatchScope(m_lexicalVariables);
     3352
     3353        if (m_finallyBlock) {
     3354            generator.emitSetFinallyActionToNormalCompletion();
     3355            generator.emitJump(finallyLabel.get());
     3356            generator.popTry(tryData, finallyViaThrowLabel.get());
     3357        }
     3358
    33193359        generator.emitLabel(catchEndLabel.get());
     3360        generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1);
    33203361    }
    33213362
    33223363    if (m_finallyBlock) {
    3323         RefPtr<Label> preFinallyLabel = generator.emitLabel(generator.newLabel().get());
    3324        
    3325         generator.popFinallyControlFlowScope();
    3326 
    3327         RefPtr<Label> finallyEndLabel = generator.newLabel();
     3364        FinallyContext finallyContext = generator.popFinallyControlFlowScope();
     3365
     3366        // Entry to the finally block for CompletionType::Throw.
     3367        generator.emitLabel(finallyViaThrowLabel.get());
     3368        RegisterID* unused = generator.newTemporary();
     3369        generator.emitCatch(generator.finallyActionRegister(), unused);
     3370        // Setting the finallyActionRegister to the caught exception here implies CompletionType::Throw.
     3371
     3372        // Entry to the finally block for CompletionTypes other than Throw.
     3373        generator.emitLabel(finallyLabel.get());
     3374        generator.restoreScopeRegister();
    33283375
    33293376        int finallyStartOffset = m_catchBlock ? m_catchBlock->endOffset() + 1 : m_tryBlock->endOffset() + 1;
    3330 
    3331         // Normal path: run the finally code, and jump to the end.
    33323377        generator.emitProfileControlFlow(finallyStartOffset);
    33333378        generator.emitNodeInTailPosition(dst, m_finallyBlock);
    3334         generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
    3335         generator.emitJump(finallyEndLabel.get());
    3336 
    3337         // Uncaught exception path: invoke the finally block, then re-throw the exception.
    3338         RefPtr<RegisterID> exceptionRegister = generator.newTemporary();
    3339         RefPtr<RegisterID> thrownValueRegister = generator.newTemporary();
    3340         generator.popTryAndEmitCatch(tryData, exceptionRegister.get(), thrownValueRegister.get(), preFinallyLabel.get(), HandlerType::Finally);
    3341         generator.emitProfileControlFlow(finallyStartOffset);
    3342         generator.emitNodeInTailPosition(dst, m_finallyBlock);
    3343         generator.emitThrow(exceptionRegister.get());
    3344 
     3379
     3380        generator.emitFinallyCompletion(finallyContext, finallyEndLabel.get());
    33453381        generator.emitLabel(finallyEndLabel.get());
    33463382        generator.emitProfileControlFlow(m_finallyBlock->endOffset() + 1);
    3347     } else
    3348         generator.emitProfileControlFlow(m_catchBlock->endOffset() + 1);
    3349 
     3383    }
    33503384}
    33513385
  • trunk/Source/WTF/ChangeLog

    r209938 r209952  
     12016-12-16  Mark Lam  <mark.lam@apple.com>
     2
     3        Add predecessor info to dumps from JSC_dumpBytecodeLivenessResults=true.
     4        https://bugs.webkit.org/show_bug.cgi?id=165958
     5
     6        Reviewed by Keith Miller.
     7
     8        Added some methods to bring SegmentedVector closer to parity with Vector.
     9
     10        * wtf/SegmentedVector.h:
     11        (WTF::SegmentedVector::first):
     12        (WTF::SegmentedVector::last):
     13        (WTF::SegmentedVector::takeLast):
     14
    1152016-12-16  Michael Saboff  <msaboff@apple.com>
    216
  • trunk/Source/WTF/wtf/SegmentedVector.h

    r195339 r209952  
    128128        }
    129129
    130         T& last()
    131         {
    132             return at(size() - 1);
     130        T& first() { return at(0); }
     131        const T& first() const { return at(0); }
     132        T& last() { return at(size() - 1); }
     133        const T& last() const { return at(size() - 1); }
     134
     135        T takeLast()
     136        {
     137            ASSERT_WITH_SECURITY_IMPLICATION(!isEmpty());
     138            T result = WTFMove(last());
     139            --m_size;
     140            return result;
    133141        }
    134142
Note: See TracChangeset for help on using the changeset viewer.