DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (409f3966645a)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "frontend/ForOfLoopControl.h"

#include "frontend/BytecodeEmitter.h"
#include "frontend/EmitterScope.h"
#include "frontend/IfEmitter.h"

using namespace js;
using namespace js::frontend;

ForOfLoopControl::ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth, bool allowSelfHosted,
                                   IteratorKind iterKind)
  : LoopControl(bce, StatementKind::ForOfLoop),
    iterDepth_(iterDepth),
    numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX),
    allowSelfHosted_(allowSelfHosted),
    iterKind_(iterKind)
{}

bool
ForOfLoopControl::emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce)
{
    tryCatch_.emplace(bce, TryEmitter::Kind::TryCatch, TryEmitter::ControlKind::NonSyntactic);

    if (!tryCatch_->emitTry())
        return false;

    MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
    numYieldsAtBeginCodeNeedingIterClose_ = bce->yieldAndAwaitOffsetList.numYields;

    return true;
}

bool
ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce)
{
    if (!tryCatch_->emitCatch())              // ITER ...
        return false;

    if (!bce->emit1(JSOP_EXCEPTION))          // ITER ... EXCEPTION
        return false;
    unsigned slotFromTop = bce->stackDepth - iterDepth_;
    if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
        return false;

    // If ITER is undefined, it means the exception is thrown by
    // IteratorClose for non-local jump, and we should't perform
    // IteratorClose again here.
    if (!bce->emit1(JSOP_UNDEFINED))          // ITER ... EXCEPTION ITER UNDEF
        return false;
    if (!bce->emit1(JSOP_STRICTNE))           // ITER ... EXCEPTION NE
        return false;

    InternalIfEmitter ifIteratorIsNotClosed(bce);
    if (!ifIteratorIsNotClosed.emitThen())    // ITER ... EXCEPTION
        return false;

    MOZ_ASSERT(slotFromTop == unsigned(bce->stackDepth - iterDepth_));
    if (!bce->emitDupAt(slotFromTop))         // ITER ... EXCEPTION ITER
        return false;
    if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Throw))
        return false;                         // ITER ... EXCEPTION

    if (!ifIteratorIsNotClosed.emitEnd())     // ITER ... EXCEPTION
        return false;

    if (!bce->emit1(JSOP_THROW))              // ITER ...
        return false;

    // If any yields were emitted, then this for-of loop is inside a star
    // generator and must handle the case of Generator.return. Like in
    // yield*, it is handled with a finally block.
    uint32_t numYieldsEmitted = bce->yieldAndAwaitOffsetList.numYields;
    if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) {
        if (!tryCatch_->emitFinally())
            return false;

        InternalIfEmitter ifGeneratorClosing(bce);
        if (!bce->emit1(JSOP_ISGENCLOSING))   // ITER ... FTYPE FVALUE CLOSING
            return false;
        if (!ifGeneratorClosing.emitThen())   // ITER ... FTYPE FVALUE
            return false;
        if (!bce->emitDupAt(slotFromTop + 1)) // ITER ... FTYPE FVALUE ITER
            return false;
        if (!emitIteratorCloseInInnermostScope(bce, CompletionKind::Normal))
            return false;                     // ITER ... FTYPE FVALUE
        if (!ifGeneratorClosing.emitEnd())    // ITER ... FTYPE FVALUE
            return false;
    }

    if (!tryCatch_->emitEnd())
        return false;

    tryCatch_.reset();
    numYieldsAtBeginCodeNeedingIterClose_ = UINT32_MAX;

    return true;
}

bool
ForOfLoopControl::emitIteratorCloseInInnermostScope(BytecodeEmitter* bce,
                                                    CompletionKind completionKind /* = CompletionKind::Normal */)
{
    return emitIteratorCloseInScope(bce,  *bce->innermostEmitterScope(), completionKind);
}

bool
ForOfLoopControl::emitIteratorCloseInScope(BytecodeEmitter* bce,
                                           EmitterScope& currentScope,
                                           CompletionKind completionKind /* = CompletionKind::Normal */)
{
    ptrdiff_t start = bce->offset();
    if (!bce->emitIteratorCloseInScope(currentScope, iterKind_, completionKind,
                                       allowSelfHosted_))
    {
        return false;
    }
    ptrdiff_t end = bce->offset();
    return bce->tryNoteList.append(JSTRY_FOR_OF_ITERCLOSE, 0, start, end);
}

// Since we're in the middle of emitting code that will leave
// |bce->innermostEmitterScope()|, passing the innermost emitter scope to
// emitIteratorCloseInScope and looking up .generator there would be very,
// very wrong.  We'd find .generator in the function environment, and we'd
// compute a NameLocation with the correct slot, but we'd compute a
// hop-count to the function environment that was too big.  At runtime we'd
// either crash, or we'd find a user-controllable value in that slot, and
// Very Bad Things would ensue as we reinterpreted that value as an
// iterator.
bool
ForOfLoopControl::emitPrepareForNonLocalJumpFromScope(BytecodeEmitter* bce,
                                                      EmitterScope& currentScope,
                                                      bool isTarget)
{
    // Pop unnecessary value from the stack.  Effectively this means
    // leaving try-catch block.  However, the performing IteratorClose can
    // reach the depth for try-catch, and effectively re-enter the
    // try-catch block.
    if (!bce->emit1(JSOP_POP))                        // NEXT ITER
        return false;

    // Pop the iterator's next method.
    if (!bce->emit1(JSOP_SWAP))                       // ITER NEXT
        return false;
    if (!bce->emit1(JSOP_POP))                        // ITER
        return false;

    // Clear ITER slot on the stack to tell catch block to avoid performing
    // IteratorClose again.
    if (!bce->emit1(JSOP_UNDEFINED))                  // ITER UNDEF
        return false;
    if (!bce->emit1(JSOP_SWAP))                       // UNDEF ITER
        return false;

    if (!emitIteratorCloseInScope(bce, currentScope, CompletionKind::Normal)) // UNDEF
        return false;

    if (isTarget) {
        // At the level of the target block, there's bytecode after the
        // loop that will pop the next method, the iterator, and the
        // value, so push two undefineds to balance the stack.
        if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF
            return false;
        if (!bce->emit1(JSOP_UNDEFINED))              // UNDEF UNDEF UNDEF
            return false;
    } else {
        if (!bce->emit1(JSOP_POP))                    //
            return false;
    }

    return true;
}