CForEmitter.cpp (5054B)
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/CForEmitter.h" 8 9 #include "frontend/BytecodeEmitter.h" // BytecodeEmitter 10 #include "frontend/EmitterScope.h" // EmitterScope 11 #include "vm/Opcodes.h" // JSOp 12 #include "vm/ScopeKind.h" // ScopeKind 13 #include "vm/StencilEnums.h" // TryNoteKind 14 15 using namespace js; 16 using namespace js::frontend; 17 18 using mozilla::Maybe; 19 20 CForEmitter::CForEmitter(BytecodeEmitter* bce, 21 const EmitterScope* headLexicalEmitterScopeForLet) 22 : bce_(bce), 23 headLexicalEmitterScopeForLet_(headLexicalEmitterScopeForLet) {} 24 25 bool CForEmitter::emitInit(const Maybe<uint32_t>& initPos) { 26 MOZ_ASSERT(state_ == State::Start); 27 28 loopInfo_.emplace(bce_, StatementKind::ForLoop); 29 30 if (initPos) { 31 if (!bce_->updateSourceCoordNotes(*initPos)) { 32 return false; 33 } 34 } 35 36 #ifdef DEBUG 37 state_ = State::Init; 38 #endif 39 return true; 40 } 41 42 bool CForEmitter::emitCond(const Maybe<uint32_t>& condPos) { 43 MOZ_ASSERT(state_ == State::Init); 44 45 // ES 13.7.4.8 step 2. The initial freshening. 46 // 47 // If an initializer let-declaration may be captured during loop 48 // iteration, the current scope has an environment. If so, freshen the 49 // current environment to expose distinct bindings for each loop 50 // iteration. 51 if (headLexicalEmitterScopeForLet_) { 52 // The environment chain only includes an environment for the 53 // for(;;) loop head's let-declaration *if* a scope binding is 54 // captured, thus requiring a fresh environment each iteration. If 55 // a lexical scope exists for the head, it must be the innermost 56 // one. If that scope has closed-over bindings inducing an 57 // environment, recreate the current environment. 58 MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope()); 59 MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_).kind() == 60 ScopeKind::Lexical); 61 62 if (headLexicalEmitterScopeForLet_->hasEnvironment()) { 63 if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(), 64 JSOp::FreshenLexicalEnv)) { 65 return false; 66 } 67 } 68 } 69 70 if (!loopInfo_->emitLoopHead(bce_, condPos)) { 71 // [stack] 72 return false; 73 } 74 75 #ifdef DEBUG 76 state_ = State::Cond; 77 #endif 78 return true; 79 } 80 81 bool CForEmitter::emitBody(Cond cond) { 82 MOZ_ASSERT(state_ == State::Cond); 83 cond_ = cond; 84 85 if (cond_ == Cond::Present) { 86 if (!bce_->emitJump(JSOp::JumpIfFalse, &loopInfo_->breaks)) { 87 return false; 88 } 89 } 90 91 tdzCache_.emplace(bce_); 92 93 #ifdef DEBUG 94 state_ = State::Body; 95 #endif 96 return true; 97 } 98 99 bool CForEmitter::emitUpdate(Update update, const Maybe<uint32_t>& updatePos) { 100 MOZ_ASSERT(state_ == State::Body); 101 update_ = update; 102 tdzCache_.reset(); 103 104 // Set loop and enclosing "update" offsets, for continue. Note that we 105 // continue to immediately *before* the block-freshening: continuing must 106 // refresh the block. 107 if (!loopInfo_->emitContinueTarget(bce_)) { 108 return false; 109 } 110 111 // ES 13.7.4.8 step 3.e. The per-iteration freshening. 112 if (headLexicalEmitterScopeForLet_) { 113 MOZ_ASSERT(headLexicalEmitterScopeForLet_ == bce_->innermostEmitterScope()); 114 MOZ_ASSERT(headLexicalEmitterScopeForLet_->scope(bce_).kind() == 115 ScopeKind::Lexical); 116 117 if (headLexicalEmitterScopeForLet_->hasEnvironment()) { 118 if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(), 119 JSOp::FreshenLexicalEnv)) { 120 return false; 121 } 122 } 123 } 124 125 // The update code may not be executed at all; it needs its own TDZ 126 // cache. 127 if (update_ == Update::Present) { 128 tdzCache_.emplace(bce_); 129 130 if (updatePos) { 131 if (!bce_->updateSourceCoordNotes(*updatePos)) { 132 return false; 133 } 134 } 135 } 136 137 #ifdef DEBUG 138 state_ = State::Update; 139 #endif 140 return true; 141 } 142 143 bool CForEmitter::emitEnd(uint32_t forPos) { 144 MOZ_ASSERT(state_ == State::Update); 145 146 if (update_ == Update::Present) { 147 tdzCache_.reset(); 148 149 // [stack] UPDATE 150 151 if (!bce_->emit1(JSOp::Pop)) { 152 // [stack] 153 return false; 154 } 155 } 156 157 if (cond_ == Cond::Missing && update_ == Update::Missing) { 158 // If there is no condition clause and no update clause, mark 159 // the loop-ending "goto" with the location of the "for". 160 // This ensures that the debugger will stop on each loop 161 // iteration. 162 if (!bce_->updateSourceCoordNotes(forPos)) { 163 return false; 164 } 165 } 166 167 // Emit the loop-closing jump. 168 if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::Loop)) { 169 // [stack] 170 return false; 171 } 172 173 loopInfo_.reset(); 174 175 #ifdef DEBUG 176 state_ = State::End; 177 #endif 178 return true; 179 }