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
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()) {
34 MOZ_ASSERT(numYieldsAtBeginCodeNeedingIterClose_
== UINT32_MAX
);
35 numYieldsAtBeginCodeNeedingIterClose_
= bce
->bytecodeSection().numYields();
40 bool ForOfLoopControl::emitEndCodeNeedingIteratorClose(BytecodeEmitter
* bce
) {
41 if (!tryCatch_
->emitCatch()) {
42 // [stack] ITER ... EXCEPTION
46 unsigned slotFromTop
= bce
->bytecodeSection().stackDepth() - iterDepth_
;
47 if (!bce
->emitDupAt(slotFromTop
)) {
48 // [stack] ITER ... EXCEPTION ITER
51 if (!emitIteratorCloseInInnermostScopeWithTryNote(bce
,
52 CompletionKind::Throw
)) {
53 return false; // ITER ... EXCEPTION
56 if (!bce
->emit1(JSOp::Throw
)) {
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()) {
72 // [stack] ITER ... FVALUE FTYPE
73 InternalIfEmitter
ifGeneratorClosing(bce
);
74 if (!bce
->emit1(JSOp::Swap
)) {
75 // [stack] ITER ... FTYPE FVALUE
78 if (!bce
->emit1(JSOp::IsGenClosing
)) {
79 // [stack] ITER ... FTYPE FVALUE CLOSING
82 if (!ifGeneratorClosing
.emitThen()) {
83 // [stack] ITER ... FTYPE FVALUE
86 if (!bce
->emitDupAt(slotFromTop
+ 1)) {
87 // [stack] ITER ... FTYPE FVALUE ITER
90 if (!emitIteratorCloseInInnermostScopeWithTryNote(bce
,
91 CompletionKind::Normal
)) {
92 // [stack] ITER ... FTYPE FVALUE
95 if (!ifGeneratorClosing
.emitEnd()) {
96 // [stack] ITER ... FTYPE FVALUE
99 if (!bce
->emit1(JSOp::Swap
)) {
100 // [stack] ITER ... FVALUE FTYPE
105 if (!tryCatch_
->emitEnd()) {
110 numYieldsAtBeginCodeNeedingIterClose_
= UINT32_MAX
;
115 bool ForOfLoopControl::emitIteratorCloseInInnermostScopeWithTryNote(
116 BytecodeEmitter
* bce
,
117 CompletionKind completionKind
/* = CompletionKind::Normal */) {
118 BytecodeOffset start
= bce
->bytecodeSection().offset();
119 if (!emitIteratorCloseInScope(bce
, *bce
->innermostEmitterScope(),
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
,
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
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
150 if (!bce
->emit1(JSOp::Pop
)) {
155 // Pop the iterator's next method.
156 if (!bce
->emit1(JSOp::Swap
)) {
160 if (!bce
->emit1(JSOp::Pop
)) {
165 if (!bce
->emit1(JSOp::Dup
)) {
170 *tryNoteStart
= bce
->bytecodeSection().offset();
171 if (!emitIteratorCloseInScope(bce
, currentScope
, CompletionKind::Normal
)) {
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
184 if (!bce
->emit1(JSOp::Undefined
)) {
185 // [stack] ITER UNDEF UNDEF
189 if (!bce
->emit1(JSOp::Pop
)) {