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
16 using namespace js::frontend
;
20 NestableControl::NestableControl(BytecodeEmitter
* bce
, StatementKind kind
)
21 : Nestable
<NestableControl
>(&bce
->innermostNestableControl
),
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
),
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
)) {
68 if (!bce
->updateSourceCoordNotes(*nextPos
)) {
73 MOZ_ASSERT(loopDepth_
> 0);
75 head_
= {bce
->bytecodeSection().offset()};
78 if (!bce
->emitJumpTargetOp(JSOp::LoopHead
, &off
)) {
81 SetLoopHeadDepthHint(bce
->bytecodeSection().code(off
), loopDepth_
);
86 bool LoopControl::emitLoopEnd(BytecodeEmitter
* bce
, JSOp op
,
87 TryNoteKind tryNoteKind
) {
89 if (!bce
->emitJumpNoFallthrough(op
, &jump
)) {
92 bce
->patchJumpsToTarget(jump
, head_
);
94 // Create a fallthrough for closing iterators, and as a target for break
96 JumpTarget breakTarget
;
97 if (!bce
->emitJumpTarget(&breakTarget
)) {
100 if (!patchBreaks(bce
)) {
103 if (!bce
->addTryNote(tryNoteKind
, bce
->bytecodeSection().stackDepth(),
104 headOffset(), breakTarget
.offset
)) {
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
,
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
;
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
)) {
136 tableGen
.finish(continuations_
.length());
137 MOZ_RELEASE_ASSERT(tableGen
.isValid());
139 InternalSwitchEmitter
se(bce
);
140 if (!se
.validateCaseCount(continuations_
.length())) {
143 if (!se
.emitTable(tableGen
)) {
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
)) {
154 // Resume the non-local control flow that was intercepted by
156 NonLocalExitControl
nle(bce
, continuation
.kind_
);
157 if (!nle
.emitNonLocalJump(continuation
.target_
, this)) {
162 // The only unhandled case is the fallthrough case, which is handled
163 // by the switch default.
164 if (!se
.emitDefaultBody()) {
173 NonLocalExitControl::NonLocalExitControl(BytecodeEmitter
* bce
,
174 NonLocalExitKind kind
)
176 savedScopeNoteIndex_(bce
->bytecodeSection().scopeNoteList().length()),
177 savedDepth_(bce
->bytecodeSection().stackDepth()),
178 openScopeNoteIndex_(bce
->innermostEmitterScope()->noteIndex()),
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)) {
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_
)) {
213 openScopeNoteIndex_
= bce_
->bytecodeSection().scopeNoteList().length() - 1;
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();
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
)) {
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
)) {
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()) {
282 jumpingToFinally
= true;
284 if (!flushPops(bce_
)) {
288 if (!finallyControl
.allocateContinuation(target
, kind_
, &idx
)) {
291 if (!bce_
->emitJumpToFinally(&finallyControl
.finallyJumps_
, idx
)) {
298 case StatementKind::ForOfLoop
: {
299 if (!flushPops(bce_
)) {
302 BytecodeOffset tryNoteStart
;
303 ForOfLoopControl
& loopinfo
= control
->as
<ForOfLoopControl
>();
304 if (!loopinfo
.emitPrepareForNonLocalJumpFromScope(
306 /* isTarget = */ false, &tryNoteStart
)) {
310 if (!forOfIterCloseScopeStarts
.append(tryNoteStart
)) {
316 case StatementKind::ForInLoop
:
317 if (!flushPops(bce_
)) {
321 // The iterator and the current value are on the stack.
322 if (!bce_
->emit1(JSOp::EndIter
)) {
333 if (!flushPops(bce_
)) {
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,
344 // [stack] ... UNDEF UNDEF UNDEF
347 if (!forOfIterCloseScopeStarts
.append(tryNoteStart
)) {
352 EmitterScope
* targetEmitterScope
=
353 target
? target
->emitterScope() : bce_
->varEmitterScope
;
354 for (; es
!= targetEmitterScope
; es
= es
->enclosingInFrame()) {
355 if (!leaveScope(es
)) {
360 case NonLocalExitKind::Continue
: {
361 LoopControl
* loop
= &target
->as
<LoopControl
>();
362 if (!bce_
->emitJump(JSOp::Goto
, &loop
->continues
)) {
367 case NonLocalExitKind::Break
: {
368 BreakableControl
* breakable
= &target
->as
<BreakableControl
>();
369 if (!bce_
->emitJump(JSOp::Goto
, &breakable
->breaks
)) {
374 case NonLocalExitKind::Return
:
376 if (!bce_
->finishReturn(setRvalOffset_
)) {
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
)) {