Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / CForEmitter.cpp
blob4fccde0e88476d7132f74205797faf0a9ca4890d
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
15 using namespace js;
16 using namespace js::frontend;
18 using mozilla::Maybe;
20 CForEmitter::CForEmitter(BytecodeEmitter* bce,
21 const EmitterScope* headLexicalEmitterScopeForLet)
22 : bce_(bce),
23 headLexicalEmitterScopeForLet_(headLexicalEmitterScopeForLet) {}
25 bool CForEmitter::emitInit(const Maybe<uint32_t>& initPos) {
26 MOZ_ASSERT(state_ == State::Start);
28 loopInfo_.emplace(bce_, StatementKind::ForLoop);
30 if (initPos) {
31 if (!bce_->updateSourceCoordNotes(*initPos)) {
32 return false;
36 #ifdef DEBUG
37 state_ = State::Init;
38 #endif
39 return true;
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
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);
62 if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
63 if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(),
64 JSOp::FreshenLexicalEnv)) {
65 return false;
70 if (!loopInfo_->emitLoopHead(bce_, condPos)) {
71 // [stack]
72 return false;
75 #ifdef DEBUG
76 state_ = State::Cond;
77 #endif
78 return true;
81 bool CForEmitter::emitBody(Cond cond) {
82 MOZ_ASSERT(state_ == State::Cond);
83 cond_ = cond;
85 if (cond_ == Cond::Present) {
86 if (!bce_->emitJump(JSOp::JumpIfFalse, &loopInfo_->breaks)) {
87 return false;
91 tdzCache_.emplace(bce_);
93 #ifdef DEBUG
94 state_ = State::Body;
95 #endif
96 return true;
99 bool CForEmitter::emitUpdate(Update update, const Maybe<uint32_t>& updatePos) {
100 MOZ_ASSERT(state_ == State::Body);
101 update_ = update;
102 tdzCache_.reset();
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;
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);
117 if (headLexicalEmitterScopeForLet_->hasEnvironment()) {
118 if (!bce_->emitInternedScopeOp(headLexicalEmitterScopeForLet_->index(),
119 JSOp::FreshenLexicalEnv)) {
120 return false;
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_);
130 if (updatePos) {
131 if (!bce_->updateSourceCoordNotes(*updatePos)) {
132 return false;
137 #ifdef DEBUG
138 state_ = State::Update;
139 #endif
140 return true;
143 bool CForEmitter::emitEnd(uint32_t forPos) {
144 MOZ_ASSERT(state_ == State::Update);
146 if (update_ == Update::Present) {
147 tdzCache_.reset();
149 // [stack] UPDATE
151 if (!bce_->emit1(JSOp::Pop)) {
152 // [stack]
153 return false;
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;
167 // Emit the loop-closing jump.
168 if (!loopInfo_->emitLoopEnd(bce_, JSOp::Goto, TryNoteKind::Loop)) {
169 // [stack]
170 return false;
173 loopInfo_.reset();
175 #ifdef DEBUG
176 state_ = State::End;
177 #endif
178 return true;