ForOfEmitter.cpp (8627B)
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/ForOfEmitter.h" 8 9 #include "frontend/BytecodeEmitter.h" 10 #include "frontend/EmitterScope.h" 11 #include "frontend/ParserAtom.h" // TaggedParserAtomIndex 12 #include "frontend/UsingEmitter.h" 13 #include "vm/Opcodes.h" 14 #include "vm/StencilEnums.h" // TryNoteKind 15 16 using namespace js; 17 using namespace js::frontend; 18 19 using mozilla::Nothing; 20 21 ForOfEmitter::ForOfEmitter(BytecodeEmitter* bce, 22 const EmitterScope* headLexicalEmitterScope, 23 SelfHostedIter selfHostedIter, IteratorKind iterKind 24 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 25 , 26 HeadUsingDeclarationKind usingDeclarationInHead 27 #endif 28 ) 29 : bce_(bce), 30 selfHostedIter_(selfHostedIter), 31 iterKind_(iterKind), 32 headLexicalEmitterScope_(headLexicalEmitterScope) 33 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 34 , 35 usingDeclarationInHead_(usingDeclarationInHead) 36 #endif 37 { 38 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 39 // The using bindings are closed over and stored in the lexical environment 40 // object for headLexicalEmitterScope. 41 // Mark that the environment has disposables for them to be disposed on 42 // every iteration. 43 MOZ_ASSERT_IF(usingDeclarationInHead != HeadUsingDeclarationKind::None, 44 headLexicalEmitterScope->hasEnvironment() && 45 headLexicalEmitterScope == bce_->innermostEmitterScope() && 46 headLexicalEmitterScope->hasDisposables()); 47 MOZ_ASSERT_IF( 48 headLexicalEmitterScope && headLexicalEmitterScope->hasDisposables(), 49 usingDeclarationInHead != HeadUsingDeclarationKind::None); 50 #endif 51 } 52 53 bool ForOfEmitter::emitIterated() { 54 MOZ_ASSERT(state_ == State::Start); 55 56 // Evaluate the expression being iterated. The forHeadExpr should use a 57 // distinct TDZCheckCache to evaluate since (abstractly) it runs in its 58 // own LexicalEnvironment. 59 tdzCacheForIteratedValue_.emplace(bce_); 60 61 #ifdef DEBUG 62 state_ = State::Iterated; 63 #endif 64 return true; 65 } 66 67 bool ForOfEmitter::emitInitialize(uint32_t forPos) { 68 MOZ_ASSERT(state_ == State::Iterated); 69 70 tdzCacheForIteratedValue_.reset(); 71 72 // [stack] # if AllowContentWithNext 73 // [stack] NEXT ITER 74 // [stack] # elif AllowContentWith 75 // [stack] ITERABLE ITERFN SYNC_ITERFN? 76 // [stack] # else 77 // [stack] ITERABLE 78 79 if (iterKind_ == IteratorKind::Async) { 80 if (!bce_->emitAsyncIterator(selfHostedIter_)) { 81 // [stack] NEXT ITER 82 return false; 83 } 84 } else { 85 if (!bce_->emitIterator(selfHostedIter_)) { 86 // [stack] NEXT ITER 87 return false; 88 } 89 } 90 91 // For-of loops have the iterator next method and the iterator itself on the 92 // stack. 93 94 int32_t iterDepth = bce_->bytecodeSection().stackDepth(); 95 loopInfo_.emplace(bce_, iterDepth, selfHostedIter_, iterKind_); 96 97 if (!loopInfo_->emitLoopHead(bce_, Nothing())) { 98 // [stack] NEXT ITER 99 return false; 100 } 101 102 // If the loop had an escaping lexical declaration, replace the current 103 // environment with an dead zoned one to implement TDZ semantics. 104 if (headLexicalEmitterScope_) { 105 // The environment chain only includes an environment for the for-of 106 // loop head *if* a scope binding is captured, thereby requiring 107 // recreation each iteration. If a lexical scope exists for the head, 108 // it must be the innermost one. If that scope has closed-over 109 // bindings inducing an environment, recreate the current environment. 110 MOZ_ASSERT(headLexicalEmitterScope_ == bce_->innermostEmitterScope()); 111 MOZ_ASSERT(headLexicalEmitterScope_->scope(bce_).kind() == 112 ScopeKind::Lexical); 113 114 if (headLexicalEmitterScope_->hasEnvironment()) { 115 #ifdef ENABLE_EXPLICIT_RESOURCE_MANAGEMENT 116 // Before recreation of the lexical environment, we must dispose 117 // the disposables of the previous iteration. 118 // 119 // Emitting the bytecode to dispose over here means 120 // that we will have one extra disposal at the start of the loop which 121 // is a no op because there arent any disposables added yet. 122 // 123 // There also wouldn't be a dispose operation for the environment 124 // object recreated for the last iteration, where it leaves the loop 125 // before evaluating the body statement. 126 if (!loopInfo_->prepareForForOfLoopIteration( 127 bce_, headLexicalEmitterScope_, 128 usingDeclarationInHead_ == HeadUsingDeclarationKind::Async)) { 129 return false; 130 } 131 #endif 132 if (!bce_->emitInternedScopeOp(headLexicalEmitterScope_->index(), 133 JSOp::RecreateLexicalEnv)) { 134 // [stack] NEXT ITER 135 return false; 136 } 137 } 138 139 // For uncaptured bindings, put them back in TDZ. 140 if (!headLexicalEmitterScope_->deadZoneFrameSlots(bce_)) { 141 return false; 142 } 143 } 144 145 #ifdef DEBUG 146 loopDepth_ = bce_->bytecodeSection().stackDepth(); 147 #endif 148 149 // Make sure this code is attributed to the "for". 150 if (!bce_->updateSourceCoordNotes(forPos)) { 151 return false; 152 } 153 154 if (!bce_->emit1(JSOp::Dup2)) { 155 // [stack] NEXT ITER NEXT ITER 156 return false; 157 } 158 159 if (!bce_->emitIteratorNext(mozilla::Some(forPos), iterKind_, 160 selfHostedIter_)) { 161 // [stack] NEXT ITER RESULT 162 return false; 163 } 164 165 if (!bce_->emit1(JSOp::Dup)) { 166 // [stack] NEXT ITER RESULT RESULT 167 return false; 168 } 169 if (!bce_->emitAtomOp(JSOp::GetProp, 170 TaggedParserAtomIndex::WellKnown::done())) { 171 // [stack] NEXT ITER RESULT DONE 172 return false; 173 } 174 175 // if (done) break; 176 MOZ_ASSERT(bce_->innermostNestableControl == loopInfo_.ptr(), 177 "must be at the top-level of the loop"); 178 if (!bce_->emitJump(JSOp::JumpIfTrue, &loopInfo_->breaks)) { 179 // [stack] NEXT ITER RESULT 180 return false; 181 } 182 183 // Emit code to assign result.value to the iteration variable. 184 // 185 // Note that ES 13.7.5.13, step 5.c says getting result.value does not 186 // call IteratorClose, so start TryNoteKind::ForOfIterClose after the GetProp. 187 if (!bce_->emitAtomOp(JSOp::GetProp, 188 TaggedParserAtomIndex::WellKnown::value())) { 189 // [stack] NEXT ITER VALUE 190 return false; 191 } 192 193 if (!loopInfo_->emitBeginCodeNeedingIteratorClose(bce_)) { 194 return false; 195 } 196 197 #ifdef DEBUG 198 state_ = State::Initialize; 199 #endif 200 return true; 201 } 202 203 bool ForOfEmitter::emitBody() { 204 MOZ_ASSERT(state_ == State::Initialize); 205 206 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_ + 1, 207 "the stack must be balanced around the initializing " 208 "operation"); 209 210 #ifdef DEBUG 211 state_ = State::Body; 212 #endif 213 return true; 214 } 215 216 bool ForOfEmitter::emitEnd(uint32_t iteratedPos) { 217 MOZ_ASSERT(state_ == State::Body); 218 219 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_ + 1, 220 "the stack must be balanced around the for-of body"); 221 222 if (!loopInfo_->emitEndCodeNeedingIteratorClose(bce_)) { 223 // [stack] NEXT ITER VALUE 224 return false; 225 } 226 227 if (!loopInfo_->emitContinueTarget(bce_)) { 228 // [stack] NEXT ITER VALUE 229 return false; 230 } 231 232 // We use the iterated value's position to attribute the backedge, 233 // which corresponds to the iteration protocol. 234 // This is a bit misleading for 2nd and later iterations and might need 235 // some fix (bug 1482003). 236 if (!bce_->updateSourceCoordNotes(iteratedPos)) { 237 return false; 238 } 239 240 if (!bce_->emit1(JSOp::Pop)) { 241 // [stack] NEXT ITER 242 return false; 243 } 244 245 if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::ForOf)) { 246 // [stack] NEXT ITER 247 return false; 248 } 249 250 // All jumps/breaks to this point still have an extra value on the stack. 251 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_); 252 bce_->bytecodeSection().setStackDepth(bce_->bytecodeSection().stackDepth() + 253 1); 254 255 if (!bce_->emitPopN(3)) { 256 // [stack] 257 return false; 258 } 259 260 loopInfo_.reset(); 261 262 #ifdef DEBUG 263 state_ = State::End; 264 #endif 265 return true; 266 }