AsyncEmitter.cpp (5272B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "frontend/AsyncEmitter.h" 8 9 #include "mozilla/Assertions.h" // MOZ_ASSERT 10 11 #include "frontend/BytecodeEmitter.h" // BytecodeEmitter 12 #include "frontend/NameOpEmitter.h" // NameOpEmitter 13 #include "frontend/ParserAtom.h" // TaggedParserAtomIndex 14 #include "vm/Opcodes.h" // JSOp 15 16 using namespace js; 17 using namespace js::frontend; 18 19 bool AsyncEmitter::prepareForParamsWithExpressionOrDestructuring() { 20 MOZ_ASSERT(state_ == State::Start); 21 #ifdef DEBUG 22 state_ = State::Parameters; 23 #endif 24 25 rejectTryCatch_.emplace(bce_, TryEmitter::Kind::TryCatch, 26 TryEmitter::ControlKind::NonSyntactic); 27 return rejectTryCatch_->emitTry(); 28 } 29 30 bool AsyncEmitter::prepareForParamsWithoutExpressionOrDestructuring() { 31 MOZ_ASSERT(state_ == State::Start); 32 #ifdef DEBUG 33 state_ = State::Parameters; 34 #endif 35 return true; 36 } 37 38 bool AsyncEmitter::emitParamsEpilogue() { 39 MOZ_ASSERT(state_ == State::Parameters); 40 41 if (rejectTryCatch_) { 42 // If we get here, we need to reset the TryEmitter. Parameters can't reuse 43 // the reject try-catch block from the function body, because the body 44 // may have pushed an additional var-environment. This messes up scope 45 // resolution for the |.generator| variable, because we'd need different 46 // hops to reach |.generator| depending on whether the error was thrown 47 // from the parameters or the function body. 48 if (!emitRejectCatch()) { 49 return false; 50 } 51 } 52 53 #ifdef DEBUG 54 state_ = State::PostParams; 55 #endif 56 return true; 57 } 58 59 bool AsyncEmitter::prepareForModule() { 60 // Unlike functions, modules do not have params that we need to worry about. 61 // Instead, this code is for setting up the required generator that will be 62 // used for top level await. Before we can start using top-level await in 63 // modules, we need to emit a 64 // |.generator| which we can use to pause and resume execution. 65 MOZ_ASSERT(state_ == State::Start); 66 MOZ_ASSERT( 67 bce_->lookupName(TaggedParserAtomIndex::WellKnown::dot_generator_()) 68 .hasKnownSlot()); 69 70 NameOpEmitter noe(bce_, TaggedParserAtomIndex::WellKnown::dot_generator_(), 71 NameOpEmitter::Kind::Initialize); 72 if (!noe.prepareForRhs()) { 73 // [stack] 74 return false; 75 } 76 if (!bce_->emit1(JSOp::Generator)) { 77 // [stack] GEN 78 return false; 79 } 80 if (!noe.emitAssignment()) { 81 // [stack] GEN 82 return false; 83 } 84 if (!bce_->emit1(JSOp::Pop)) { 85 // [stack] 86 return false; 87 } 88 89 #ifdef DEBUG 90 state_ = State::ModulePrologue; 91 #endif 92 93 return true; 94 } 95 96 bool AsyncEmitter::prepareForBody() { 97 MOZ_ASSERT(state_ == State::PostParams || state_ == State::ModulePrologue); 98 99 rejectTryCatch_.emplace(bce_, TryEmitter::Kind::TryCatch, 100 TryEmitter::ControlKind::NonSyntactic); 101 #ifdef DEBUG 102 state_ = State::Body; 103 #endif 104 return rejectTryCatch_->emitTry(); 105 } 106 107 bool AsyncEmitter::emitEndFunction() { 108 #ifdef DEBUG 109 MOZ_ASSERT(state_ == State::Body); 110 #endif 111 112 // The final yield has already been emitted 113 // by FunctionScriptEmitter::emitEndBody(). 114 115 if (!emitRejectCatch()) { 116 return false; 117 } 118 119 #ifdef DEBUG 120 state_ = State::End; 121 #endif 122 return true; 123 } 124 125 bool AsyncEmitter::emitEndModule() { 126 #ifdef DEBUG 127 MOZ_ASSERT(state_ == State::Body); 128 #endif 129 130 if (!emitFinalYield()) { 131 return false; 132 } 133 134 if (!emitRejectCatch()) { 135 return false; 136 } 137 138 #ifdef DEBUG 139 state_ = State::End; 140 #endif 141 return true; 142 } 143 144 bool AsyncEmitter::emitFinalYield() { 145 if (!bce_->emit1(JSOp::Undefined)) { 146 // [stack] UNDEF 147 return false; 148 } 149 150 if (!bce_->emitGetDotGeneratorInInnermostScope()) { 151 // [stack] UNDEF GEN 152 return false; 153 } 154 155 if (!bce_->emit1(JSOp::AsyncResolve)) { 156 // [stack] PROMISE 157 return false; 158 } 159 160 if (!bce_->emit1(JSOp::SetRval)) { 161 // [stack] 162 return false; 163 } 164 165 if (!bce_->emitGetDotGeneratorInInnermostScope()) { 166 // [stack] GEN 167 return false; 168 } 169 170 if (!bce_->emitYieldOp(JSOp::FinalYieldRval)) { 171 // [stack] 172 return false; 173 } 174 175 return true; 176 } 177 178 bool AsyncEmitter::emitRejectCatch() { 179 if (!rejectTryCatch_->emitCatch(TryEmitter::ExceptionStack::Yes)) { 180 // [stack] EXC STACK 181 return false; 182 } 183 184 if (!bce_->emitGetDotGeneratorInInnermostScope()) { 185 // [stack] EXC STACK GEN 186 return false; 187 } 188 189 if (!bce_->emit1(JSOp::AsyncReject)) { 190 // [stack] PROMISE 191 return false; 192 } 193 194 if (!bce_->emit1(JSOp::SetRval)) { 195 // [stack] 196 return false; 197 } 198 199 if (!bce_->emitGetDotGeneratorInInnermostScope()) { 200 // [stack] GEN 201 return false; 202 } 203 204 if (!bce_->emitYieldOp(JSOp::FinalYieldRval)) { 205 // [stack] 206 return false; 207 } 208 209 if (!rejectTryCatch_->emitEnd()) { 210 return false; 211 } 212 213 rejectTryCatch_.reset(); 214 return true; 215 }