Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / ForOfLoopControl.cpp
blob084f8d80eb522438e1fd4f07524338f4ceee2db3
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/ForOfLoopControl.h"
9 #include "frontend/BytecodeEmitter.h" // BytecodeEmitter
10 #include "frontend/IfEmitter.h" // InternalIfEmitter
11 #include "vm/CompletionKind.h" // CompletionKind
12 #include "vm/Opcodes.h" // JSOp
14 using namespace js;
15 using namespace js::frontend;
17 ForOfLoopControl::ForOfLoopControl(BytecodeEmitter* bce, int32_t iterDepth,
18 SelfHostedIter selfHostedIter,
19 IteratorKind iterKind)
20 : LoopControl(bce, StatementKind::ForOfLoop),
21 iterDepth_(iterDepth),
22 numYieldsAtBeginCodeNeedingIterClose_(UINT32_MAX),
23 selfHostedIter_(selfHostedIter),
24 iterKind_(iterKind) {}
26 bool ForOfLoopControl::emitBeginCodeNeedingIteratorClose(BytecodeEmitter* bce) {
27 tryCatch_.emplace(bce, TryEmitter::Kind::TryCatch,
28 TryEmitter::ControlKind::NonSyntactic);
30 if (!tryCatch_->emitTry()) {
31 return false;
34 MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_ == UINT32_MAX);
35 numYieldsAtBeginCodeNeedingIterClose_ = bce->bytecodeSection().numYields();
37 return true;
40 bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter* bce) {
41 if (!tryCatch_->emitCatch()) {
42 // [stack] ITER ... EXCEPTION
43 return false;
46 unsigned slotFromTop = bce->bytecodeSection().stackDepth() - iterDepth_;
47 if (!bce->emitDupAt(slotFromTop)) {
48 // [stack] ITER ... EXCEPTION ITER
49 return false;
51 if (!emitIteratorCloseInInnermostScopeWithTryNote(bce,
52 CompletionKind::Throw)) {
53 return false; // ITER ... EXCEPTION
56 if (!bce->emit1(JSOp::Throw)) {
57 // [stack] ITER ...
58 return false;
61 // If any yields were emitted, then this for-of loop is inside a star
62 // generator and must handle the case of Generator.return. Like in
63 // yield*, it is handled with a finally block. If the generator is
64 // closing, then the exception/resumeindex value (second value on
65 // the stack) will be a magic JS_GENERATOR_CLOSING value.
66 // TODO: Refactor this to eliminate the swaps.
67 uint32_t numYieldsEmitted = bce->bytecodeSection().numYields();
68 if (numYieldsEmitted > numYieldsAtBeginCodeNeedingIterClose_) {
69 if (!tryCatch_->emitFinally()) {
70 return false;
72 // [stack] ITER ... FVALUE FTYPE
73 InternalIfEmitter ifGeneratorClosing(bce);
74 if (!bce->emit1(JSOp::Swap)) {
75 // [stack] ITER ... FTYPE FVALUE
76 return false;
78 if (!bce->emit1(JSOp::IsGenClosing)) {
79 // [stack] ITER ... FTYPE FVALUE CLOSING
80 return false;
82 if (!ifGeneratorClosing.emitThen()) {
83 // [stack] ITER ... FTYPE FVALUE
84 return false;
86 if (!bce->emitDupAt(slotFromTop + 1)) {
87 // [stack] ITER ... FTYPE FVALUE ITER
88 return false;
90 if (!emitIteratorCloseInInnermostScopeWithTryNote(bce,
91 CompletionKind::Normal)) {
92 // [stack] ITER ... FTYPE FVALUE
93 return false;
95 if (!ifGeneratorClosing.emitEnd()) {
96 // [stack] ITER ... FTYPE FVALUE
97 return false;
99 if (!bce->emit1(JSOp::Swap)) {
100 // [stack] ITER ... FVALUE FTYPE
101 return false;
105 if (!tryCatch_->emitEnd()) {
106 return false;
109 tryCatch_.reset();
110 numYieldsAtBeginCodeNeedingIterClose_ = UINT32_MAX;
112 return true;
115 bool ForOfLoopControl::emitIteratorCloseInInnermostScopeWithTryNote(
116 BytecodeEmitter* bce,
117 CompletionKind completionKind /* = CompletionKind::Normal */) {
118 BytecodeOffset start = bce->bytecodeSection().offset();
119 if (!emitIteratorCloseInScope(bce, *bce->innermostEmitterScope(),
120 completionKind)) {
121 return false;
123 BytecodeOffset end = bce->bytecodeSection().offset();
124 return bce->addTryNote(TryNoteKind::ForOfIterClose, 0, start, end);
127 bool ForOfLoopControl::emitIteratorCloseInScope(
128 BytecodeEmitter* bce, EmitterScope& currentScope,
129 CompletionKind completionKind /* = CompletionKind::Normal */) {
130 return bce->emitIteratorCloseInScope(currentScope, iterKind_, completionKind,
131 selfHostedIter_);
134 // Since we're in the middle of emitting code that will leave
135 // |bce->innermostEmitterScope()|, passing the innermost emitter scope to
136 // emitIteratorCloseInScope and looking up .generator there would be very,
137 // very wrong. We'd find .generator in the function environment, and we'd
138 // compute a NameLocation with the correct slot, but we'd compute a
139 // hop-count to the function environment that was too big. At runtime we'd
140 // either crash, or we'd find a user-controllable value in that slot, and
141 // Very Bad Things would ensue as we reinterpreted that value as an
142 // iterator.
143 bool ForOfLoopControl::emitPrepareForNonLocalJumpFromScope(
144 BytecodeEmitter* bce, EmitterScope& currentScope, bool isTarget,
145 BytecodeOffset* tryNoteStart) {
146 // Pop unnecessary value from the stack. Effectively this means
147 // leaving try-catch block. However, the performing IteratorClose can
148 // reach the depth for try-catch, and effectively re-enter the
149 // try-catch block.
150 if (!bce->emit1(JSOp::Pop)) {
151 // [stack] NEXT ITER
152 return false;
155 // Pop the iterator's next method.
156 if (!bce->emit1(JSOp::Swap)) {
157 // [stack] ITER NEXT
158 return false;
160 if (!bce->emit1(JSOp::Pop)) {
161 // [stack] ITER
162 return false;
165 if (!bce->emit1(JSOp::Dup)) {
166 // [stack] ITER ITER
167 return false;
170 *tryNoteStart = bce->bytecodeSection().offset();
171 if (!emitIteratorCloseInScope(bce, currentScope, CompletionKind::Normal)) {
172 // [stack] ITER
173 return false;
176 if (isTarget) {
177 // At the level of the target block, there's bytecode after the
178 // loop that will pop the next method, the iterator, and the
179 // value, so push two undefineds to balance the stack.
180 if (!bce->emit1(JSOp::Undefined)) {
181 // [stack] ITER UNDEF
182 return false;
184 if (!bce->emit1(JSOp::Undefined)) {
185 // [stack] ITER UNDEF UNDEF
186 return false;
188 } else {
189 if (!bce->emit1(JSOp::Pop)) {
190 // [stack]
191 return false;
195 return true;