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/. */
7 #include "frontend/CForEmitter.h"
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
16 using namespace js::frontend
;
20 CForEmitter::CForEmitter(BytecodeEmitter
* bce
,
21 const EmitterScope
* headLexicalEmitterScopeForLet
)
23 headLexicalEmitterScopeForLet_(headLexicalEmitterScopeForLet
) {}
25 bool CForEmitter::emitInit(const Maybe
<uint32_t>& initPos
) {
26 MOZ_ASSERT(state_
== State::Start
);
28 loopInfo_
.emplace(bce_
, StatementKind::ForLoop
);
31 if (!bce_
->updateSourceCoordNotes(*initPos
)) {
42 bool CForEmitter::emitCond(const Maybe
<uint32_t>& condPos
) {
43 MOZ_ASSERT(state_
== State::Init
);
45 // ES 13.7.4.8 step 2. The initial freshening.
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
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() ==
62 if (headLexicalEmitterScopeForLet_
->hasEnvironment()) {
63 if (!bce_
->emitInternedScopeOp(headLexicalEmitterScopeForLet_
->index(),
64 JSOp::FreshenLexicalEnv
)) {
70 if (!loopInfo_
->emitLoopHead(bce_
, condPos
)) {
81 bool CForEmitter::emitBody(Cond cond
) {
82 MOZ_ASSERT(state_
== State::Cond
);
85 if (cond_
== Cond::Present
) {
86 if (!bce_
->emitJump(JSOp::JumpIfFalse
, &loopInfo_
->breaks
)) {
91 tdzCache_
.emplace(bce_
);
99 bool CForEmitter::emitUpdate(Update update
, const Maybe
<uint32_t>& updatePos
) {
100 MOZ_ASSERT(state_
== State::Body
);
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_
)) {
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() ==
117 if (headLexicalEmitterScopeForLet_
->hasEnvironment()) {
118 if (!bce_
->emitInternedScopeOp(headLexicalEmitterScopeForLet_
->index(),
119 JSOp::FreshenLexicalEnv
)) {
125 // The update code may not be executed at all; it needs its own TDZ
127 if (update_
== Update::Present
) {
128 tdzCache_
.emplace(bce_
);
131 if (!bce_
->updateSourceCoordNotes(*updatePos
)) {
138 state_
= State::Update
;
143 bool CForEmitter::emitEnd(uint32_t forPos
) {
144 MOZ_ASSERT(state_
== State::Update
);
146 if (update_
== Update::Present
) {
151 if (!bce_
->emit1(JSOp::Pop
)) {
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
162 if (!bce_
->updateSourceCoordNotes(forPos
)) {
167 // Emit the loop-closing jump.
168 if (!loopInfo_
->emitLoopEnd(bce_
, JSOp::Goto
, TryNoteKind::Loop
)) {