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/ForInEmitter.h"
9 #include "frontend/BytecodeEmitter.h"
10 #include "frontend/EmitterScope.h"
11 #include "vm/Opcodes.h"
12 #include "vm/StencilEnums.h" // TryNoteKind
15 using namespace js::frontend
;
17 using mozilla::Nothing
;
19 ForInEmitter::ForInEmitter(BytecodeEmitter
* bce
,
20 const EmitterScope
* headLexicalEmitterScope
)
21 : bce_(bce
), headLexicalEmitterScope_(headLexicalEmitterScope
) {}
23 bool ForInEmitter::emitIterated() {
24 MOZ_ASSERT(state_
== State::Start
);
25 tdzCacheForIteratedValue_
.emplace(bce_
);
28 state_
= State::Iterated
;
33 bool ForInEmitter::emitInitialize() {
34 MOZ_ASSERT(state_
== State::Iterated
);
35 tdzCacheForIteratedValue_
.reset();
37 if (!bce_
->emit1(JSOp::Iter
)) {
42 loopInfo_
.emplace(bce_
, StatementKind::ForInLoop
);
44 if (!loopInfo_
->emitLoopHead(bce_
, Nothing())) {
49 if (!bce_
->emit1(JSOp::MoreIter
)) {
50 // [stack] ITER NEXTITERVAL?
53 if (!bce_
->emit1(JSOp::IsNoIter
)) {
54 // [stack] ITER NEXTITERVAL? ISNOITER
57 if (!bce_
->emitJump(JSOp::JumpIfTrue
, &loopInfo_
->breaks
)) {
58 // [stack] ITER NEXTITERVAL?
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() ==
75 if (headLexicalEmitterScope_
->hasEnvironment()) {
76 if (!bce_
->emitInternedScopeOp(headLexicalEmitterScope_
->index(),
77 JSOp::RecreateLexicalEnv
)) {
78 // [stack] ITER ITERVAL
83 // For uncaptured bindings, put them back in TDZ.
84 if (!headLexicalEmitterScope_
->deadZoneFrameSlots(bce_
)) {
90 loopDepth_
= bce_
->bytecodeSection().stackDepth();
92 MOZ_ASSERT(loopDepth_
>= 2);
95 state_
= State::Initialize
;
100 bool ForInEmitter::emitBody() {
101 MOZ_ASSERT(state_
== State::Initialize
);
103 MOZ_ASSERT(bce_
->bytecodeSection().stackDepth() == loopDepth_
,
104 "iterator and iterval must be left on the stack");
107 state_
= State::Body
;
112 bool ForInEmitter::emitEnd(uint32_t forPos
) {
113 MOZ_ASSERT(state_
== State::Body
);
115 // Make sure this code is attributed to the "for".
116 if (!bce_
->updateSourceCoordNotes(forPos
)) {
120 if (!loopInfo_
->emitContinueTarget(bce_
)) {
121 // [stack] ITER ITERVAL
125 if (!bce_
->emit1(JSOp::Pop
)) {
129 if (!loopInfo_
->emitLoopEnd(bce_
, JSOp::Goto
, TryNoteKind::ForIn
)) {
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
);
140 // [stack] ITER ITERVAL
142 // Pop the value and iterator and close the iterator.
143 if (!bce_
->emit1(JSOp::EndIter
)) {