Bug 1865597 - Add error checking when initializing parallel marking and disable on...
[gecko.git] / js / src / frontend / BytecodeControlStructures.cpp
blobebc659aa43908f99c63b72d0acc78e23134fad5d
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/BytecodeControlStructures.h"
9 #include "frontend/BytecodeEmitter.h" // BytecodeEmitter
10 #include "frontend/EmitterScope.h" // EmitterScope
11 #include "frontend/ForOfLoopControl.h" // ForOfLoopControl
12 #include "frontend/SwitchEmitter.h" // SwitchEmitter
13 #include "vm/Opcodes.h" // JSOp
15 using namespace js;
16 using namespace js::frontend;
18 using mozilla::Maybe;
20 NestableControl::NestableControl(BytecodeEmitter* bce, StatementKind kind)
21 : Nestable<NestableControl>(&bce->innermostNestableControl),
22 kind_(kind),
23 emitterScope_(bce->innermostEmitterScopeNoCheck()) {}
25 BreakableControl::BreakableControl(BytecodeEmitter* bce, StatementKind kind)
26 : NestableControl(bce, kind) {
27 MOZ_ASSERT(is<BreakableControl>());
30 bool BreakableControl::patchBreaks(BytecodeEmitter* bce) {
31 return bce->emitJumpTargetAndPatch(breaks);
34 LabelControl::LabelControl(BytecodeEmitter* bce, TaggedParserAtomIndex label,
35 BytecodeOffset startOffset)
36 : BreakableControl(bce, StatementKind::Label),
37 label_(label),
38 startOffset_(startOffset) {}
40 LoopControl::LoopControl(BytecodeEmitter* bce, StatementKind loopKind)
41 : BreakableControl(bce, loopKind), tdzCache_(bce) {
42 MOZ_ASSERT(is<LoopControl>());
44 LoopControl* enclosingLoop = findNearest<LoopControl>(enclosing());
46 stackDepth_ = bce->bytecodeSection().stackDepth();
47 loopDepth_ = enclosingLoop ? enclosingLoop->loopDepth_ + 1 : 1;
50 bool LoopControl::emitContinueTarget(BytecodeEmitter* bce) {
51 // Note: this is always called after emitting the loop body so we must have
52 // emitted all 'continues' by now.
53 return bce->emitJumpTargetAndPatch(continues);
56 bool LoopControl::emitLoopHead(BytecodeEmitter* bce,
57 const Maybe<uint32_t>& nextPos) {
58 // Insert a Nop if needed to ensure the script does not start with a
59 // JSOp::LoopHead. This avoids JIT issues with prologue code + try notes
60 // or OSR. See bug 1602390 and bug 1602681.
61 if (bce->bytecodeSection().offset().toUint32() == 0) {
62 if (!bce->emit1(JSOp::Nop)) {
63 return false;
67 if (nextPos) {
68 if (!bce->updateSourceCoordNotes(*nextPos)) {
69 return false;
73 MOZ_ASSERT(loopDepth_ > 0);
75 head_ = {bce->bytecodeSection().offset()};
77 BytecodeOffset off;
78 if (!bce->emitJumpTargetOp(JSOp::LoopHead, &off)) {
79 return false;
81 SetLoopHeadDepthHint(bce->bytecodeSection().code(off), loopDepth_);
83 return true;
86 bool LoopControl::emitLoopEnd(BytecodeEmitter* bce, JSOp op,
87 TryNoteKind tryNoteKind) {
88 JumpList jump;
89 if (!bce->emitJumpNoFallthrough(op, &jump)) {
90 return false;
92 bce->patchJumpsToTarget(jump, head_);
94 // Create a fallthrough for closing iterators, and as a target for break
95 // statements.
96 JumpTarget breakTarget;
97 if (!bce->emitJumpTarget(&breakTarget)) {
98 return false;
100 if (!patchBreaks(bce)) {
101 return false;
103 if (!bce->addTryNote(tryNoteKind, bce->bytecodeSection().stackDepth(),
104 headOffset(), breakTarget.offset)) {
105 return false;
107 return true;
110 TryFinallyControl::TryFinallyControl(BytecodeEmitter* bce, StatementKind kind)
111 : NestableControl(bce, kind) {
112 MOZ_ASSERT(is<TryFinallyControl>());
115 bool TryFinallyControl::allocateContinuation(NestableControl* target,
116 NonLocalExitKind kind,
117 uint32_t* idx) {
118 for (uint32_t i = 0; i < continuations_.length(); i++) {
119 if (continuations_[i].target_ == target &&
120 continuations_[i].kind_ == kind) {
121 *idx = i + SpecialContinuations::Count;
122 return true;
125 *idx = continuations_.length() + SpecialContinuations::Count;
126 return continuations_.emplaceBack(target, kind);
129 bool TryFinallyControl::emitContinuations(BytecodeEmitter* bce) {
130 SwitchEmitter::TableGenerator tableGen(bce);
131 for (uint32_t i = 0; i < continuations_.length(); i++) {
132 if (!tableGen.addNumber(i + SpecialContinuations::Count)) {
133 return false;
136 tableGen.finish(continuations_.length());
137 MOZ_RELEASE_ASSERT(tableGen.isValid());
139 InternalSwitchEmitter se(bce);
140 if (!se.validateCaseCount(continuations_.length())) {
141 return false;
143 if (!se.emitTable(tableGen)) {
144 return false;
147 // Continuation index 0 is special-cased to be the fallthrough block.
148 // Non-default switch cases are numbered 1-N.
149 uint32_t caseIdx = SpecialContinuations::Count;
150 for (TryFinallyContinuation& continuation : continuations_) {
151 if (!se.emitCaseBody(caseIdx++, tableGen)) {
152 return false;
154 // Resume the non-local control flow that was intercepted by
155 // this finally.
156 NonLocalExitControl nle(bce, continuation.kind_);
157 if (!nle.emitNonLocalJump(continuation.target_, this)) {
158 return false;
162 // The only unhandled case is the fallthrough case, which is handled
163 // by the switch default.
164 if (!se.emitDefaultBody()) {
165 return false;
167 if (!se.emitEnd()) {
168 return false;
170 return true;
173 NonLocalExitControl::NonLocalExitControl(BytecodeEmitter* bce,
174 NonLocalExitKind kind)
175 : bce_(bce),
176 savedScopeNoteIndex_(bce->bytecodeSection().scopeNoteList().length()),
177 savedDepth_(bce->bytecodeSection().stackDepth()),
178 openScopeNoteIndex_(bce->innermostEmitterScope()->noteIndex()),
179 kind_(kind) {}
181 NonLocalExitControl::~NonLocalExitControl() {
182 for (uint32_t n = savedScopeNoteIndex_;
183 n < bce_->bytecodeSection().scopeNoteList().length(); n++) {
184 bce_->bytecodeSection().scopeNoteList().recordEnd(
185 n, bce_->bytecodeSection().offset());
187 bce_->bytecodeSection().setStackDepth(savedDepth_);
190 bool NonLocalExitControl::emitReturn(BytecodeOffset setRvalOffset) {
191 MOZ_ASSERT(kind_ == NonLocalExitKind::Return);
192 setRvalOffset_ = setRvalOffset;
193 return emitNonLocalJump(nullptr);
196 bool NonLocalExitControl::leaveScope(EmitterScope* es) {
197 if (!es->leave(bce_, /* nonLocal = */ true)) {
198 return false;
201 // As we pop each scope due to the non-local jump, emit notes that
202 // record the extent of the enclosing scope. These notes will have
203 // their ends recorded in ~NonLocalExitControl().
204 GCThingIndex enclosingScopeIndex = ScopeNote::NoScopeIndex;
205 if (es->enclosingInFrame()) {
206 enclosingScopeIndex = es->enclosingInFrame()->index();
208 if (!bce_->bytecodeSection().scopeNoteList().append(
209 enclosingScopeIndex, bce_->bytecodeSection().offset(),
210 openScopeNoteIndex_)) {
211 return false;
213 openScopeNoteIndex_ = bce_->bytecodeSection().scopeNoteList().length() - 1;
215 return true;
219 * Emit additional bytecode(s) for non-local jumps.
221 bool NonLocalExitControl::emitNonLocalJump(NestableControl* target,
222 NestableControl* startingAfter) {
223 NestableControl* startingControl = startingAfter
224 ? startingAfter->enclosing()
225 : bce_->innermostNestableControl;
226 EmitterScope* es = startingAfter ? startingAfter->emitterScope()
227 : bce_->innermostEmitterScope();
229 int npops = 0;
231 AutoCheckUnstableEmitterScope cues(bce_);
233 // We emit IteratorClose bytecode inline. 'continue' statements do
234 // not call IteratorClose for the loop they are continuing.
235 bool emitIteratorCloseAtTarget = kind_ != NonLocalExitKind::Continue;
237 auto flushPops = [&npops](BytecodeEmitter* bce) {
238 if (npops && !bce->emitPopN(npops)) {
239 return false;
241 npops = 0;
242 return true;
245 // If we are closing multiple for-of loops, the resulting
246 // TryNoteKind::ForOfIterClose trynotes must be appropriately nested. Each
247 // TryNoteKind::ForOfIterClose starts when we close the corresponding for-of
248 // iterator, and continues until the actual jump.
249 Vector<BytecodeOffset, 4> forOfIterCloseScopeStarts(bce_->fc);
251 // If we have to execute a finally block, then we will jump there now and
252 // continue the non-local jump from the end of the finally block.
253 bool jumpingToFinally = false;
255 // Walk the nestable control stack and patch jumps.
256 for (NestableControl* control = startingControl;
257 control != target && !jumpingToFinally; control = control->enclosing()) {
258 // Walk the scope stack and leave the scopes we entered. Leaving a scope
259 // may emit administrative ops like JSOp::PopLexicalEnv but never anything
260 // that manipulates the stack.
261 for (; es != control->emitterScope(); es = es->enclosingInFrame()) {
262 if (!leaveScope(es)) {
263 return false;
267 switch (control->kind()) {
268 case StatementKind::Finally: {
269 TryFinallyControl& finallyControl = control->as<TryFinallyControl>();
270 if (finallyControl.emittingSubroutine()) {
272 * There's a [resume-index-or-exception, throwing] pair on
273 * the stack that we need to pop. If the script is not a
274 * noScriptRval script, we also need to pop the cached rval.
276 if (bce_->sc->noScriptRval()) {
277 npops += 2;
278 } else {
279 npops += 3;
281 } else {
282 jumpingToFinally = true;
284 if (!flushPops(bce_)) {
285 return false;
287 uint32_t idx;
288 if (!finallyControl.allocateContinuation(target, kind_, &idx)) {
289 return false;
291 if (!bce_->emitJumpToFinally(&finallyControl.finallyJumps_, idx)) {
292 return false;
295 break;
298 case StatementKind::ForOfLoop: {
299 if (!flushPops(bce_)) {
300 return false;
302 BytecodeOffset tryNoteStart;
303 ForOfLoopControl& loopinfo = control->as<ForOfLoopControl>();
304 if (!loopinfo.emitPrepareForNonLocalJumpFromScope(
305 bce_, *es,
306 /* isTarget = */ false, &tryNoteStart)) {
307 // [stack] ...
308 return false;
310 if (!forOfIterCloseScopeStarts.append(tryNoteStart)) {
311 return false;
313 break;
316 case StatementKind::ForInLoop:
317 if (!flushPops(bce_)) {
318 return false;
321 // The iterator and the current value are on the stack.
322 if (!bce_->emit1(JSOp::EndIter)) {
323 // [stack] ...
324 return false;
326 break;
328 default:
329 break;
333 if (!flushPops(bce_)) {
334 return false;
337 if (!jumpingToFinally) {
338 if (target && emitIteratorCloseAtTarget && target->is<ForOfLoopControl>()) {
339 BytecodeOffset tryNoteStart;
340 ForOfLoopControl& loopinfo = target->as<ForOfLoopControl>();
341 if (!loopinfo.emitPrepareForNonLocalJumpFromScope(bce_, *es,
342 /* isTarget = */ true,
343 &tryNoteStart)) {
344 // [stack] ... UNDEF UNDEF UNDEF
345 return false;
347 if (!forOfIterCloseScopeStarts.append(tryNoteStart)) {
348 return false;
352 EmitterScope* targetEmitterScope =
353 target ? target->emitterScope() : bce_->varEmitterScope;
354 for (; es != targetEmitterScope; es = es->enclosingInFrame()) {
355 if (!leaveScope(es)) {
356 return false;
359 switch (kind_) {
360 case NonLocalExitKind::Continue: {
361 LoopControl* loop = &target->as<LoopControl>();
362 if (!bce_->emitJump(JSOp::Goto, &loop->continues)) {
363 return false;
365 break;
367 case NonLocalExitKind::Break: {
368 BreakableControl* breakable = &target->as<BreakableControl>();
369 if (!bce_->emitJump(JSOp::Goto, &breakable->breaks)) {
370 return false;
372 break;
374 case NonLocalExitKind::Return:
375 MOZ_ASSERT(!target);
376 if (!bce_->finishReturn(setRvalOffset_)) {
377 return false;
379 break;
383 // Close TryNoteKind::ForOfIterClose trynotes.
384 BytecodeOffset end = bce_->bytecodeSection().offset();
385 for (BytecodeOffset start : forOfIterCloseScopeStarts) {
386 if (!bce_->addTryNote(TryNoteKind::ForOfIterClose, 0, start, end)) {
387 return false;
391 return true;