1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef frontend_BytecodeControlStructures_h
#define frontend_BytecodeControlStructures_h
#include "mozilla/Attributes.h"
#include "mozilla/Maybe.h"
#include <stddef.h>
#include <stdint.h>
#include "ds/Nestable.h"
#include "frontend/JumpList.h"
#include "frontend/SharedContext.h"
#include "frontend/TDZCheckCache.h"
#include "gc/Rooting.h"
#include "vm/BytecodeUtil.h"
namespace js {
namespace frontend {
struct BytecodeEmitter;
class EmitterScope;
class NestableControl : public Nestable<NestableControl> {
StatementKind kind_;
// The innermost scope when this was pushed.
EmitterScope* emitterScope_;
protected:
NestableControl(BytecodeEmitter* bce, StatementKind kind);
public:
using Nestable<NestableControl>::enclosing;
using Nestable<NestableControl>::findNearest;
StatementKind kind() const { return kind_; }
EmitterScope* emitterScope() const { return emitterScope_; }
template <typename T>
bool is() const;
template <typename T>
T& as() {
MOZ_ASSERT(this->is<T>());
return static_cast<T&>(*this);
}
};
class BreakableControl : public NestableControl {
public:
// Offset of the last break.
JumpList breaks;
BreakableControl(BytecodeEmitter* bce, StatementKind kind);
MOZ_MUST_USE bool patchBreaks(BytecodeEmitter* bce);
};
template <>
inline bool NestableControl::is<BreakableControl>() const {
return StatementKindIsUnlabeledBreakTarget(kind_) ||
kind_ == StatementKind::Label;
}
class LabelControl : public BreakableControl {
RootedAtom label_;
// The code offset when this was pushed. Used for effectfulness checking.
ptrdiff_t startOffset_;
public:
LabelControl(BytecodeEmitter* bce, JSAtom* label, ptrdiff_t startOffset);
HandleAtom label() const { return label_; }
ptrdiff_t startOffset() const { return startOffset_; }
};
template <>
inline bool NestableControl::is<LabelControl>() const {
return kind_ == StatementKind::Label;
}
class LoopControl : public BreakableControl {
// Loops' children are emitted in dominance order, so they can always
// have a TDZCheckCache.
TDZCheckCache tdzCache_;
// Here's the basic structure of a loop:
//
// # Entry jump
// JSOP_GOTO entry
//
// head:
// JSOP_LOOPHEAD
// {loop body after branch}
//
// entry:
// JSOP_ENTRY
// {loop body before branch}
//
// # Loop end, backward jump
// JSOP_GOTO/JSOP_IFNE head
//
// breakTarget:
//
// `continueTarget` can be placed in arbitrary place by calling
// `setContinueTarget` or `emitContinueTarget` (see comment above them for
// more details).
// The offset of backward jump at the end of loop.
ptrdiff_t loopEndOffset_ = -1;
// The jump into JSOP_LOOPENTRY.
JumpList entryJump_;
// The bytecode offset of JSOP_LOOPHEAD.
JumpTarget head_ = {-1};
// The target of break statement jumps.
JumpTarget breakTarget_ = {-1};
// The target of continue statement jumps, e.g., the update portion of a
// for(;;) loop.
JumpTarget continueTarget_ = {-1};
// Stack depth when this loop was pushed on the control stack.
int32_t stackDepth_;
// The loop nesting depth. Used as a hint to Ion.
uint32_t loopDepth_;
// Can we OSR into Ion from here? True unless there is non-loop state on the
// stack.
bool canIonOsr_;
public:
// Offset of the last continue in the loop.
JumpList continues;
LoopControl(BytecodeEmitter* bce, StatementKind loopKind);
ptrdiff_t headOffset() const { return head_.offset; }
ptrdiff_t loopEndOffset() const { return loopEndOffset_; }
ptrdiff_t breakTargetOffset() const { return breakTarget_.offset; }
ptrdiff_t continueTargetOffset() const { return continueTarget_.offset; }
// The offset of the backward jump at the loop end from the loop's top, in
// case there was an entry jump.
ptrdiff_t loopEndOffsetFromEntryJump() const {
return loopEndOffset_ - entryJump_.offset;
}
// The offset of the backward jump at the loop end from the loop's top, in
// case there was no entry jump.
ptrdiff_t loopEndOffsetFromLoopHead() const {
return loopEndOffset_ - head_.offset;
}
// The offset of the continue target from the loop's top, in case there was
// no entry jump.
ptrdiff_t continueTargetOffsetFromLoopHead() const {
return continueTarget_.offset - head_.offset;
}
// A continue target can be specified by the following 2 ways:
// * Use the existing JUMPTARGET by calling `setContinueTarget` with
// the offset of the JUMPTARGET
// * Generate a new JUMPTARGETby calling `emitContinueTarget`
void setContinueTarget(ptrdiff_t offset) { continueTarget_.offset = offset; }
MOZ_MUST_USE bool emitContinueTarget(BytecodeEmitter* bce);
// Emit a jump to break target from the top level of the loop.
MOZ_MUST_USE bool emitSpecialBreakForDone(BytecodeEmitter* bce);
MOZ_MUST_USE bool emitEntryJump(BytecodeEmitter* bce);
// `nextPos` is the offset in the source code for the character that
// corresponds to the next instruction after JSOP_LOOPHEAD.
// Can be Nothing() if not available.
MOZ_MUST_USE bool emitLoopHead(BytecodeEmitter* bce,
const mozilla::Maybe<uint32_t>& nextPos);
// `nextPos` is the offset in the source code for the character that
// corresponds to the next instruction after JSOP_LOOPENTRY.
// Can be Nothing() if not available.
MOZ_MUST_USE bool emitLoopEntry(BytecodeEmitter* bce,
const mozilla::Maybe<uint32_t>& nextPos);
MOZ_MUST_USE bool emitLoopEnd(BytecodeEmitter* bce, JSOp op);
MOZ_MUST_USE bool patchBreaksAndContinues(BytecodeEmitter* bce);
};
template <>
inline bool NestableControl::is<LoopControl>() const {
return StatementKindIsLoop(kind_);
}
class TryFinallyControl : public NestableControl {
bool emittingSubroutine_;
public:
// The subroutine when emitting a finally block.
JumpList gosubs;
TryFinallyControl(BytecodeEmitter* bce, StatementKind kind);
void setEmittingSubroutine() { emittingSubroutine_ = true; }
bool emittingSubroutine() const { return emittingSubroutine_; }
};
template <>
inline bool NestableControl::is<TryFinallyControl>() const {
return kind_ == StatementKind::Try || kind_ == StatementKind::Finally;
}
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_BytecodeControlStructures_h */