ForInEmitter.cpp (4362B)
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/ForInEmitter.h" 8 9 #include "frontend/BytecodeEmitter.h" 10 #include "frontend/EmitterScope.h" 11 #include "vm/Opcodes.h" 12 #include "vm/StencilEnums.h" // TryNoteKind 13 14 using namespace js; 15 using namespace js::frontend; 16 17 using mozilla::Nothing; 18 19 ForInEmitter::ForInEmitter(BytecodeEmitter* bce, 20 const EmitterScope* headLexicalEmitterScope) 21 : bce_(bce), headLexicalEmitterScope_(headLexicalEmitterScope) {} 22 23 bool ForInEmitter::emitIterated() { 24 MOZ_ASSERT(state_ == State::Start); 25 tdzCacheForIteratedValue_.emplace(bce_); 26 27 #ifdef DEBUG 28 state_ = State::Iterated; 29 #endif 30 return true; 31 } 32 33 bool ForInEmitter::emitInitialize() { 34 MOZ_ASSERT(state_ == State::Iterated); 35 tdzCacheForIteratedValue_.reset(); 36 37 if (!bce_->emit1(JSOp::Iter)) { 38 // [stack] ITER 39 return false; 40 } 41 42 loopInfo_.emplace(bce_, StatementKind::ForInLoop); 43 44 if (!loopInfo_->emitLoopHead(bce_, Nothing())) { 45 // [stack] ITER 46 return false; 47 } 48 49 if (!bce_->emit1(JSOp::MoreIter)) { 50 // [stack] ITER NEXTITERVAL? 51 return false; 52 } 53 if (!bce_->emit1(JSOp::IsNoIter)) { 54 // [stack] ITER NEXTITERVAL? ISNOITER 55 return false; 56 } 57 if (!bce_->emitJump(JSOp::JumpIfTrue, &loopInfo_->breaks)) { 58 // [stack] ITER NEXTITERVAL? 59 return false; 60 } 61 62 // If the loop had an escaping lexical declaration, reset the declaration's 63 // bindings to uninitialized to implement TDZ semantics. 64 if (headLexicalEmitterScope_) { 65 // The environment chain only includes an environment for the 66 // for-in loop head *if* a scope binding is captured, thereby 67 // requiring recreation each iteration. If a lexical scope exists 68 // for the head, it must be the innermost one. If that scope has 69 // closed-over bindings inducing an environment, recreate the 70 // current environment. 71 MOZ_ASSERT(headLexicalEmitterScope_ == bce_->innermostEmitterScope()); 72 MOZ_ASSERT(headLexicalEmitterScope_->scope(bce_).kind() == 73 ScopeKind::Lexical); 74 75 if (headLexicalEmitterScope_->hasEnvironment()) { 76 if (!bce_->emitInternedScopeOp(headLexicalEmitterScope_->index(), 77 JSOp::RecreateLexicalEnv)) { 78 // [stack] ITER ITERVAL 79 return false; 80 } 81 } 82 83 // For uncaptured bindings, put them back in TDZ. 84 if (!headLexicalEmitterScope_->deadZoneFrameSlots(bce_)) { 85 return false; 86 } 87 } 88 89 #ifdef DEBUG 90 loopDepth_ = bce_->bytecodeSection().stackDepth(); 91 #endif 92 MOZ_ASSERT(loopDepth_ >= 2); 93 94 #ifdef DEBUG 95 state_ = State::Initialize; 96 #endif 97 return true; 98 } 99 100 bool ForInEmitter::emitBody() { 101 MOZ_ASSERT(state_ == State::Initialize); 102 103 MOZ_ASSERT(bce_->bytecodeSection().stackDepth() == loopDepth_, 104 "iterator and iterval must be left on the stack"); 105 106 #ifdef DEBUG 107 state_ = State::Body; 108 #endif 109 return true; 110 } 111 112 bool ForInEmitter::emitEnd(uint32_t forPos) { 113 MOZ_ASSERT(state_ == State::Body); 114 115 // Make sure this code is attributed to the "for". 116 if (!bce_->updateSourceCoordNotes(forPos)) { 117 return false; 118 } 119 120 if (!loopInfo_->emitContinueTarget(bce_)) { 121 // [stack] ITER ITERVAL 122 return false; 123 } 124 125 if (!bce_->emit1(JSOp::Pop)) { 126 // [stack] ITER 127 return false; 128 } 129 if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::ForIn)) { 130 // [stack] ITER 131 return false; 132 } 133 134 // When we leave the loop body and jump to this point, the iteration value is 135 // still on the stack. Account for that by updating the stack depth manually. 136 int32_t stackDepth = bce_->bytecodeSection().stackDepth() + 1; 137 MOZ_ASSERT(stackDepth == loopDepth_); 138 bce_->bytecodeSection().setStackDepth(stackDepth); 139 140 // [stack] ITER ITERVAL 141 142 // Pop the value and iterator and close the iterator. 143 if (!bce_->emit1(JSOp::EndIter)) { 144 // [stack] 145 return false; 146 } 147 148 loopInfo_.reset(); 149 150 #ifdef DEBUG 151 state_ = State::End; 152 #endif 153 return true; 154 }