Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / ForInEmitter.cpp
blob4d9f9ad54065a3d9fa0f0f83f4123533fbb1ba32
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
14 using namespace js;
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_);
27 #ifdef DEBUG
28 state_ = State::Iterated;
29 #endif
30 return true;
33 bool ForInEmitter::emitInitialize() {
34 MOZ_ASSERT(state_ == State::Iterated);
35 tdzCacheForIteratedValue_.reset();
37 if (!bce_->emit1(JSOp::Iter)) {
38 // [stack] ITER
39 return false;
42 loopInfo_.emplace(bce_, StatementKind::ForInLoop);
44 if (!loopInfo_->emitLoopHead(bce_, Nothing())) {
45 // [stack] ITER
46 return false;
49 if (!bce_->emit1(JSOp::MoreIter)) {
50 // [stack] ITER NEXTITERVAL?
51 return false;
53 if (!bce_->emit1(JSOp::IsNoIter)) {
54 // [stack] ITER NEXTITERVAL? ISNOITER
55 return false;
57 if (!bce_->emitJump(JSOp::JumpIfTrue, &loopInfo_->breaks)) {
58 // [stack] ITER NEXTITERVAL?
59 return false;
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);
75 if (headLexicalEmitterScope_->hasEnvironment()) {
76 if (!bce_->emitInternedScopeOp(headLexicalEmitterScope_->index(),
77 JSOp::RecreateLexicalEnv)) {
78 // [stack] ITER ITERVAL
79 return false;
83 // For uncaptured bindings, put them back in TDZ.
84 if (!headLexicalEmitterScope_->deadZoneFrameSlots(bce_)) {
85 return false;
89 #ifdef DEBUG
90 loopDepth_ = bce_->bytecodeSection().stackDepth();
91 #endif
92 MOZ_ASSERT(loopDepth_ >= 2);
94 #ifdef DEBUG
95 state_ = State::Initialize;
96 #endif
97 return true;
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");
106 #ifdef DEBUG
107 state_ = State::Body;
108 #endif
109 return true;
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)) {
117 return false;
120 if (!loopInfo_->emitContinueTarget(bce_)) {
121 // [stack] ITER ITERVAL
122 return false;
125 if (!bce_->emit1(JSOp::Pop)) {
126 // [stack] ITER
127 return false;
129 if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::ForIn)) {
130 // [stack] ITER
131 return false;
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)) {
144 // [stack]
145 return false;
148 loopInfo_.reset();
150 #ifdef DEBUG
151 state_ = State::End;
152 #endif
153 return true;