|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
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/. */ |
|
6 |
|
7 #ifndef frontend_BytecodeEmitter_h |
|
8 #define frontend_BytecodeEmitter_h |
|
9 |
|
10 /* |
|
11 * JS bytecode generation. |
|
12 */ |
|
13 |
|
14 #include "jscntxt.h" |
|
15 #include "jsopcode.h" |
|
16 #include "jsscript.h" |
|
17 |
|
18 #include "frontend/ParseMaps.h" |
|
19 #include "frontend/SourceNotes.h" |
|
20 |
|
21 namespace js { |
|
22 namespace frontend { |
|
23 |
|
24 class FullParseHandler; |
|
25 class ObjectBox; |
|
26 class ParseNode; |
|
27 template <typename ParseHandler> class Parser; |
|
28 class SharedContext; |
|
29 class TokenStream; |
|
30 |
|
31 class CGConstList { |
|
32 Vector<Value> list; |
|
33 public: |
|
34 CGConstList(ExclusiveContext *cx) : list(cx) {} |
|
35 bool append(Value v) { JS_ASSERT_IF(v.isString(), v.toString()->isAtom()); return list.append(v); } |
|
36 size_t length() const { return list.length(); } |
|
37 void finish(ConstArray *array); |
|
38 }; |
|
39 |
|
40 struct CGObjectList { |
|
41 uint32_t length; /* number of emitted so far objects */ |
|
42 ObjectBox *lastbox; /* last emitted object */ |
|
43 |
|
44 CGObjectList() : length(0), lastbox(nullptr) {} |
|
45 |
|
46 unsigned add(ObjectBox *objbox); |
|
47 unsigned indexOf(JSObject *obj); |
|
48 void finish(ObjectArray *array); |
|
49 ObjectBox* find(uint32_t index); |
|
50 }; |
|
51 |
|
52 struct CGTryNoteList { |
|
53 Vector<JSTryNote> list; |
|
54 CGTryNoteList(ExclusiveContext *cx) : list(cx) {} |
|
55 |
|
56 bool append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end); |
|
57 size_t length() const { return list.length(); } |
|
58 void finish(TryNoteArray *array); |
|
59 }; |
|
60 |
|
61 struct CGBlockScopeList { |
|
62 Vector<BlockScopeNote> list; |
|
63 CGBlockScopeList(ExclusiveContext *cx) : list(cx) {} |
|
64 |
|
65 bool append(uint32_t scopeObject, uint32_t offset, uint32_t parent); |
|
66 uint32_t findEnclosingScope(uint32_t index); |
|
67 void recordEnd(uint32_t index, uint32_t offset); |
|
68 size_t length() const { return list.length(); } |
|
69 void finish(BlockScopeArray *array); |
|
70 }; |
|
71 |
|
72 struct StmtInfoBCE; |
|
73 |
|
74 // Use zero inline elements because these go on the stack and affect how many |
|
75 // nested functions are possible. |
|
76 typedef Vector<jsbytecode, 0> BytecodeVector; |
|
77 typedef Vector<jssrcnote, 0> SrcNotesVector; |
|
78 |
|
79 struct BytecodeEmitter |
|
80 { |
|
81 typedef StmtInfoBCE StmtInfo; |
|
82 |
|
83 SharedContext *const sc; /* context shared between parsing and bytecode generation */ |
|
84 |
|
85 BytecodeEmitter *const parent; /* enclosing function or global context */ |
|
86 |
|
87 Rooted<JSScript*> script; /* the JSScript we're ultimately producing */ |
|
88 |
|
89 struct EmitSection { |
|
90 BytecodeVector code; /* bytecode */ |
|
91 SrcNotesVector notes; /* source notes, see below */ |
|
92 ptrdiff_t lastNoteOffset; /* code offset for last source note */ |
|
93 uint32_t currentLine; /* line number for tree-based srcnote gen */ |
|
94 uint32_t lastColumn; /* zero-based column index on currentLine of |
|
95 last SRC_COLSPAN-annotated opcode */ |
|
96 |
|
97 EmitSection(ExclusiveContext *cx, uint32_t lineNum) |
|
98 : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0) |
|
99 {} |
|
100 }; |
|
101 EmitSection prolog, main, *current; |
|
102 |
|
103 /* the parser */ |
|
104 Parser<FullParseHandler> *const parser; |
|
105 |
|
106 HandleScript evalCaller; /* scripted caller info for eval and dbgapi */ |
|
107 |
|
108 StmtInfoBCE *topStmt; /* top of statement info stack */ |
|
109 StmtInfoBCE *topScopeStmt; /* top lexical scope statement */ |
|
110 Rooted<NestedScopeObject *> staticScope; |
|
111 /* compile time scope chain */ |
|
112 |
|
113 OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */ |
|
114 unsigned firstLine; /* first line, for JSScript::initFromEmitter */ |
|
115 |
|
116 int32_t stackDepth; /* current stack depth in script frame */ |
|
117 uint32_t maxStackDepth; /* maximum stack depth so far */ |
|
118 |
|
119 uint32_t arrayCompDepth; /* stack depth of array in comprehension */ |
|
120 |
|
121 unsigned emitLevel; /* js::frontend::EmitTree recursion level */ |
|
122 |
|
123 CGConstList constList; /* constants to be included with the script */ |
|
124 |
|
125 CGObjectList objectList; /* list of emitted objects */ |
|
126 CGObjectList regexpList; /* list of emitted regexp that will be |
|
127 cloned during execution */ |
|
128 CGTryNoteList tryNoteList; /* list of emitted try notes */ |
|
129 CGBlockScopeList blockScopeList;/* list of emitted block scope notes */ |
|
130 |
|
131 uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */ |
|
132 |
|
133 bool hasSingletons:1; /* script contains singleton initializer JSOP_OBJECT */ |
|
134 |
|
135 bool emittingForInit:1; /* true while emitting init expr of for; exclude 'in' */ |
|
136 |
|
137 bool emittingRunOnceLambda:1; /* true while emitting a lambda which is only |
|
138 expected to run once. */ |
|
139 bool lazyRunOnceLambda:1; /* true while lazily emitting a script for |
|
140 * a lambda which is only expected to run once. */ |
|
141 |
|
142 bool isRunOnceLambda(); |
|
143 |
|
144 bool insideEval:1; /* True if compiling an eval-expression or a function |
|
145 nested inside an eval. */ |
|
146 |
|
147 const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the |
|
148 global object */ |
|
149 |
|
150 enum EmitterMode { |
|
151 Normal, |
|
152 |
|
153 /* |
|
154 * Emit JSOP_GETINTRINSIC instead of JSOP_NAME and assert that |
|
155 * JSOP_NAME and JSOP_*GNAME don't ever get emitted. See the comment |
|
156 * for the field |selfHostingMode| in Parser.h for details. |
|
157 */ |
|
158 SelfHosting, |
|
159 |
|
160 /* |
|
161 * Check the static scope chain of the root function for resolving free |
|
162 * variable accesses in the script. |
|
163 */ |
|
164 LazyFunction |
|
165 }; |
|
166 |
|
167 const EmitterMode emitterMode; |
|
168 |
|
169 /* |
|
170 * Note that BytecodeEmitters are magic: they own the arena "top-of-stack" |
|
171 * space above their tempMark points. This means that you cannot alloc from |
|
172 * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter |
|
173 * destruction. |
|
174 */ |
|
175 BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc, |
|
176 HandleScript script, bool insideEval, HandleScript evalCaller, |
|
177 bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode = Normal); |
|
178 bool init(); |
|
179 |
|
180 bool isAliasedName(ParseNode *pn); |
|
181 |
|
182 MOZ_ALWAYS_INLINE |
|
183 bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) { |
|
184 AtomIndexAddPtr p = atomIndices->lookupForAdd(atom); |
|
185 if (p) { |
|
186 *indexp = p.value(); |
|
187 return true; |
|
188 } |
|
189 |
|
190 jsatomid index = atomIndices->count(); |
|
191 if (!atomIndices->add(p, atom, index)) |
|
192 return false; |
|
193 |
|
194 *indexp = index; |
|
195 return true; |
|
196 } |
|
197 |
|
198 bool isInLoop(); |
|
199 bool checkSingletonContext(); |
|
200 |
|
201 bool needsImplicitThis(); |
|
202 |
|
203 void tellDebuggerAboutCompiledScript(ExclusiveContext *cx); |
|
204 |
|
205 inline TokenStream *tokenStream(); |
|
206 |
|
207 BytecodeVector &code() const { return current->code; } |
|
208 jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; } |
|
209 ptrdiff_t offset() const { return current->code.end() - current->code.begin(); } |
|
210 ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); } |
|
211 void switchToMain() { current = &main; } |
|
212 void switchToProlog() { current = &prolog; } |
|
213 |
|
214 SrcNotesVector ¬es() const { return current->notes; } |
|
215 ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; } |
|
216 unsigned currentLine() const { return current->currentLine; } |
|
217 unsigned lastColumn() const { return current->lastColumn; } |
|
218 |
|
219 bool reportError(ParseNode *pn, unsigned errorNumber, ...); |
|
220 bool reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...); |
|
221 bool reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...); |
|
222 }; |
|
223 |
|
224 /* |
|
225 * Emit one bytecode. |
|
226 */ |
|
227 ptrdiff_t |
|
228 Emit1(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op); |
|
229 |
|
230 /* |
|
231 * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). |
|
232 */ |
|
233 ptrdiff_t |
|
234 Emit2(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1); |
|
235 |
|
236 /* |
|
237 * Emit three bytecodes, an opcode with two bytes of immediate operands. |
|
238 */ |
|
239 ptrdiff_t |
|
240 Emit3(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2); |
|
241 |
|
242 /* |
|
243 * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. |
|
244 */ |
|
245 ptrdiff_t |
|
246 EmitN(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra); |
|
247 |
|
248 /* |
|
249 * Emit code into bce for the tree rooted at pn. |
|
250 */ |
|
251 bool |
|
252 EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn); |
|
253 |
|
254 /* |
|
255 * Emit function code using bce for the tree rooted at body. |
|
256 */ |
|
257 bool |
|
258 EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body); |
|
259 |
|
260 /* |
|
261 * Append a new source note of the given type (and therefore size) to bce's |
|
262 * notes dynamic array, updating bce->noteCount. Return the new note's index |
|
263 * within the array pointed at by bce->current->notes. Return -1 if out of |
|
264 * memory. |
|
265 */ |
|
266 int |
|
267 NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type); |
|
268 |
|
269 int |
|
270 NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset); |
|
271 |
|
272 int |
|
273 NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1, |
|
274 ptrdiff_t offset2); |
|
275 |
|
276 /* NB: this function can add at most one extra extended delta note. */ |
|
277 bool |
|
278 AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta); |
|
279 |
|
280 bool |
|
281 FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t *out); |
|
282 |
|
283 void |
|
284 CopySrcNotes(BytecodeEmitter *bce, jssrcnote *destination, uint32_t nsrcnotes); |
|
285 |
|
286 } /* namespace frontend */ |
|
287 } /* namespace js */ |
|
288 |
|
289 #endif /* frontend_BytecodeEmitter_h */ |