summaryrefslogtreecommitdiffstats
path: root/js/src/frontend/AsyncEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/AsyncEmitter.cpp')
-rw-r--r--js/src/frontend/AsyncEmitter.cpp217
1 files changed, 217 insertions, 0 deletions
diff --git a/js/src/frontend/AsyncEmitter.cpp b/js/src/frontend/AsyncEmitter.cpp
new file mode 100644
index 0000000000..8596f4ba7f
--- /dev/null
+++ b/js/src/frontend/AsyncEmitter.cpp
@@ -0,0 +1,217 @@
+/* -*- 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/AsyncFunctionResolveKind.h" // AsyncFunctionResolveKind
+#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::dotGenerator())
+ .hasKnownSlot());
+
+ NameOpEmitter noe(bce_, TaggedParserAtomIndex::WellKnown::dotGenerator(),
+ 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_->emit2(JSOp::AsyncResolve,
+ uint8_t(AsyncFunctionResolveKind::Fulfill))) {
+ // [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()) {
+ // [stack] EXC
+ return false;
+ }
+
+ if (!bce_->emitGetDotGeneratorInInnermostScope()) {
+ // [stack] EXC GEN
+ return false;
+ }
+
+ if (!bce_->emit2(JSOp::AsyncResolve,
+ uint8_t(AsyncFunctionResolveKind::Reject))) {
+ // [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;
+}