diff options
Diffstat (limited to 'js/src/frontend/AsyncEmitter.cpp')
-rw-r--r-- | js/src/frontend/AsyncEmitter.cpp | 215 |
1 files changed, 215 insertions, 0 deletions
diff --git a/js/src/frontend/AsyncEmitter.cpp b/js/src/frontend/AsyncEmitter.cpp new file mode 100644 index 0000000000..bee6b804f1 --- /dev/null +++ b/js/src/frontend/AsyncEmitter.cpp @@ -0,0 +1,215 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * 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/AsyncEmitter.h" + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include "frontend/BytecodeEmitter.h" // BytecodeEmitter +#include "frontend/NameOpEmitter.h" // NameOpEmitter +#include "frontend/ParserAtom.h" // TaggedParserAtomIndex +#include "vm/Opcodes.h" // JSOp + +using namespace js; +using namespace js::frontend; + +bool AsyncEmitter::prepareForParamsWithExpressionOrDestructuring() { + MOZ_ASSERT(state_ == State::Start); +#ifdef DEBUG + state_ = State::Parameters; +#endif + + rejectTryCatch_.emplace(bce_, TryEmitter::Kind::TryCatch, + TryEmitter::ControlKind::NonSyntactic); + return rejectTryCatch_->emitTry(); +} + +bool AsyncEmitter::prepareForParamsWithoutExpressionOrDestructuring() { + MOZ_ASSERT(state_ == State::Start); +#ifdef DEBUG + state_ = State::Parameters; +#endif + return true; +} + +bool AsyncEmitter::emitParamsEpilogue() { + MOZ_ASSERT(state_ == State::Parameters); + + if (rejectTryCatch_) { + // If we get here, we need to reset the TryEmitter. Parameters can't reuse + // the reject try-catch block from the function body, because the body + // may have pushed an additional var-environment. This messes up scope + // resolution for the |.generator| variable, because we'd need different + // hops to reach |.generator| depending on whether the error was thrown + // from the parameters or the function body. + if (!emitRejectCatch()) { + return false; + } + } + +#ifdef DEBUG + state_ = State::PostParams; +#endif + return true; +} + +bool AsyncEmitter::prepareForModule() { + // Unlike functions, modules do not have params that we need to worry about. + // Instead, this code is for setting up the required generator that will be + // used for top level await. Before we can start using top-level await in + // modules, we need to emit a + // |.generator| which we can use to pause and resume execution. + MOZ_ASSERT(state_ == State::Start); + MOZ_ASSERT( + bce_->lookupName(TaggedParserAtomIndex::WellKnown::dot_generator_()) + .hasKnownSlot()); + + NameOpEmitter noe(bce_, TaggedParserAtomIndex::WellKnown::dot_generator_(), + NameOpEmitter::Kind::Initialize); + if (!noe.prepareForRhs()) { + // [stack] + return false; + } + if (!bce_->emit1(JSOp::Generator)) { + // [stack] GEN + return false; + } + if (!noe.emitAssignment()) { + // [stack] GEN + return false; + } + if (!bce_->emit1(JSOp::Pop)) { + // [stack] + return false; + } + +#ifdef DEBUG + state_ = State::ModulePrologue; +#endif + + return true; +} + +bool AsyncEmitter::prepareForBody() { + MOZ_ASSERT(state_ == State::PostParams || state_ == State::ModulePrologue); + + rejectTryCatch_.emplace(bce_, TryEmitter::Kind::TryCatch, + TryEmitter::ControlKind::NonSyntactic); +#ifdef DEBUG + state_ = State::Body; +#endif + return rejectTryCatch_->emitTry(); +} + +bool AsyncEmitter::emitEndFunction() { +#ifdef DEBUG + MOZ_ASSERT(state_ == State::Body); +#endif + + // The final yield has already been emitted + // by FunctionScriptEmitter::emitEndBody(). + + if (!emitRejectCatch()) { + return false; + } + +#ifdef DEBUG + state_ = State::End; +#endif + return true; +} + +bool AsyncEmitter::emitEndModule() { +#ifdef DEBUG + MOZ_ASSERT(state_ == State::Body); +#endif + + if (!emitFinalYield()) { + return false; + } + + if (!emitRejectCatch()) { + return false; + } + +#ifdef DEBUG + state_ = State::End; +#endif + return true; +} + +bool AsyncEmitter::emitFinalYield() { + if (!bce_->emit1(JSOp::Undefined)) { + // [stack] UNDEF + return false; + } + + if (!bce_->emitGetDotGeneratorInInnermostScope()) { + // [stack] UNDEF GEN + return false; + } + + if (!bce_->emit1(JSOp::AsyncResolve)) { + // [stack] PROMISE + return false; + } + + if (!bce_->emit1(JSOp::SetRval)) { + // [stack] + return false; + } + + if (!bce_->emitGetDotGeneratorInInnermostScope()) { + // [stack] GEN + return false; + } + + if (!bce_->emitYieldOp(JSOp::FinalYieldRval)) { + // [stack] + return false; + } + + return true; +} + +bool AsyncEmitter::emitRejectCatch() { + if (!rejectTryCatch_->emitCatch(TryEmitter::ExceptionStack::Yes)) { + // [stack] EXC STACK + return false; + } + + if (!bce_->emitGetDotGeneratorInInnermostScope()) { + // [stack] EXC STACK GEN + return false; + } + + if (!bce_->emit1(JSOp::AsyncReject)) { + // [stack] PROMISE + return false; + } + + if (!bce_->emit1(JSOp::SetRval)) { + // [stack] + return false; + } + + if (!bce_->emitGetDotGeneratorInInnermostScope()) { + // [stack] GEN + return false; + } + + if (!bce_->emitYieldOp(JSOp::FinalYieldRval)) { + // [stack] + return false; + } + + if (!rejectTryCatch_->emitEnd()) { + return false; + } + + rejectTryCatch_.reset(); + return true; +} |