1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* vim: set ts=8 sw=4 et tw=79: |
3 |
* |
4 |
* ***** BEGIN LICENSE BLOCK ***** |
5 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
6 |
* |
7 |
* The contents of this file are subject to the Mozilla Public License Version |
8 |
* 1.1 (the "License"); you may not use this file except in compliance with |
9 |
* the License. You may obtain a copy of the License at |
10 |
* http://www.mozilla.org/MPL/ |
11 |
* |
12 |
* Software distributed under the License is distributed on an "AS IS" basis, |
13 |
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
14 |
* for the specific language governing rights and limitations under the |
15 |
* License. |
16 |
* |
17 |
* The Original Code is Mozilla Communicator client code, released |
18 |
* March 31, 1998. |
19 |
* |
20 |
* The Initial Developer of the Original Code is |
21 |
* Netscape Communications Corporation. |
22 |
* Portions created by the Initial Developer are Copyright (C) 1998 |
23 |
* the Initial Developer. All Rights Reserved. |
24 |
* |
25 |
* Contributor(s): |
26 |
* |
27 |
* Alternatively, the contents of this file may be used under the terms of |
28 |
* either of the GNU General Public License Version 2 or later (the "GPL"), |
29 |
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), |
30 |
* in which case the provisions of the GPL or the LGPL are applicable instead |
31 |
* of those above. If you wish to allow use of your version of this file only |
32 |
* under the terms of either the GPL or the LGPL, and not to allow others to |
33 |
* use your version of this file under the terms of the MPL, indicate your |
34 |
* decision by deleting the provisions above and replace them with the notice |
35 |
* and other provisions required by the GPL or the LGPL. If you do not delete |
36 |
* the provisions above, a recipient may use your version of this file under |
37 |
* the terms of any one of the MPL, the GPL or the LGPL. |
38 |
* |
39 |
* ***** END LICENSE BLOCK ***** */ |
40 |
|
41 |
#ifndef jsemit_h___ |
42 |
#define jsemit_h___ |
43 |
/* |
44 |
* JS bytecode generation. |
45 |
*/ |
46 |
|
47 |
#include "jsstddef.h" |
48 |
#include "jstypes.h" |
49 |
#include "jsatom.h" |
50 |
#include "jsopcode.h" |
51 |
#include "jsscript.h" |
52 |
#include "jsprvtd.h" |
53 |
#include "jspubtd.h" |
54 |
|
55 |
JS_BEGIN_EXTERN_C |
56 |
|
57 |
/* |
58 |
* NB: If you add enumerators for scope statements, add them between STMT_WITH |
59 |
* and STMT_CATCH, or you will break the STMT_TYPE_IS_SCOPE macro. If you add |
60 |
* non-looping statement enumerators, add them before STMT_DO_LOOP or you will |
61 |
* break the STMT_TYPE_IS_LOOP macro. |
62 |
* |
63 |
* Also remember to keep the statementName array in jsemit.c in sync. |
64 |
*/ |
65 |
typedef enum JSStmtType { |
66 |
STMT_LABEL, /* labeled statement: L: s */ |
67 |
STMT_IF, /* if (then) statement */ |
68 |
STMT_ELSE, /* else clause of if statement */ |
69 |
STMT_SEQ, /* synthetic sequence of statements */ |
70 |
STMT_BLOCK, /* compound statement: { s1[;... sN] } */ |
71 |
STMT_SWITCH, /* switch statement */ |
72 |
STMT_WITH, /* with statement */ |
73 |
STMT_CATCH, /* catch block */ |
74 |
STMT_TRY, /* try block */ |
75 |
STMT_FINALLY, /* finally block */ |
76 |
STMT_SUBROUTINE, /* gosub-target subroutine body */ |
77 |
STMT_DO_LOOP, /* do/while loop statement */ |
78 |
STMT_FOR_LOOP, /* for loop statement */ |
79 |
STMT_FOR_IN_LOOP, /* for/in loop statement */ |
80 |
STMT_WHILE_LOOP, /* while loop statement */ |
81 |
STMT_LIMIT |
82 |
} JSStmtType; |
83 |
|
84 |
#define STMT_TYPE_IN_RANGE(t,b,e) ((uint)((t) - (b)) <= (uintN)((e) - (b))) |
85 |
|
86 |
/* |
87 |
* A comment on the encoding of the JSStmtType enum and type-testing macros: |
88 |
* |
89 |
* STMT_TYPE_MAYBE_SCOPE tells whether a statement type is always, or may |
90 |
* become, a lexical scope. It therefore includes block and switch (the two |
91 |
* low-numbered "maybe" scope types) and excludes with (with has dynamic scope |
92 |
* pending the "reformed with" in ES4/JS2). It includes all try-catch-finally |
93 |
* types, which are high-numbered maybe-scope types. |
94 |
* |
95 |
* STMT_TYPE_LINKS_SCOPE tells whether a JSStmtInfo of the given type eagerly |
96 |
* links to other scoping statement info records. It excludes the two early |
97 |
* "maybe" types, block and switch, as well as the try and both finally types, |
98 |
* since try and the other trailing maybe-scope types don't need block scope |
99 |
* unless they contain let declarations. |
100 |
* |
101 |
* We treat WITH as a static scope because it prevents lexical binding from |
102 |
* continuing further up the static scope chain. With the lost "reformed with" |
103 |
* proposal for ES4, we would be able to model it statically, too. |
104 |
*/ |
105 |
#define STMT_TYPE_MAYBE_SCOPE(type) \ |
106 |
(type != STMT_WITH && \ |
107 |
STMT_TYPE_IN_RANGE(type, STMT_BLOCK, STMT_SUBROUTINE)) |
108 |
|
109 |
#define STMT_TYPE_LINKS_SCOPE(type) \ |
110 |
STMT_TYPE_IN_RANGE(type, STMT_WITH, STMT_CATCH) |
111 |
|
112 |
#define STMT_TYPE_IS_TRYING(type) \ |
113 |
STMT_TYPE_IN_RANGE(type, STMT_TRY, STMT_SUBROUTINE) |
114 |
|
115 |
#define STMT_TYPE_IS_LOOP(type) ((type) >= STMT_DO_LOOP) |
116 |
|
117 |
#define STMT_MAYBE_SCOPE(stmt) STMT_TYPE_MAYBE_SCOPE((stmt)->type) |
118 |
#define STMT_LINKS_SCOPE(stmt) (STMT_TYPE_LINKS_SCOPE((stmt)->type) || \ |
119 |
((stmt)->flags & SIF_SCOPE)) |
120 |
#define STMT_IS_TRYING(stmt) STMT_TYPE_IS_TRYING((stmt)->type) |
121 |
#define STMT_IS_LOOP(stmt) STMT_TYPE_IS_LOOP((stmt)->type) |
122 |
|
123 |
typedef struct JSStmtInfo JSStmtInfo; |
124 |
|
125 |
struct JSStmtInfo { |
126 |
uint16 type; /* statement type */ |
127 |
uint16 flags; /* flags, see below */ |
128 |
ptrdiff_t update; /* loop update offset (top if none) */ |
129 |
ptrdiff_t breaks; /* offset of last break in loop */ |
130 |
ptrdiff_t continues; /* offset of last continue in loop */ |
131 |
union { |
132 |
JSAtom *label; /* name of LABEL */ |
133 |
JSObject *blockObj; /* block scope object */ |
134 |
} u; |
135 |
JSStmtInfo *down; /* info for enclosing statement */ |
136 |
JSStmtInfo *downScope; /* next enclosing lexical scope */ |
137 |
}; |
138 |
|
139 |
#define SIF_SCOPE 0x0001 /* statement has its own lexical scope */ |
140 |
#define SIF_BODY_BLOCK 0x0002 /* STMT_BLOCK type is a function body */ |
141 |
#define SIF_FOR_BLOCK 0x0004 /* for (let ...) induced block scope */ |
142 |
|
143 |
/* |
144 |
* To reuse space in JSStmtInfo, rename breaks and continues for use during |
145 |
* try/catch/finally code generation and backpatching. To match most common |
146 |
* use cases, the macro argument is a struct, not a struct pointer. Only a |
147 |
* loop, switch, or label statement info record can have breaks and continues, |
148 |
* and only a for loop has an update backpatch chain, so it's safe to overlay |
149 |
* these for the "trying" JSStmtTypes. |
150 |
*/ |
151 |
#define CATCHNOTE(stmt) ((stmt).update) |
152 |
#define GOSUBS(stmt) ((stmt).breaks) |
153 |
#define GUARDJUMP(stmt) ((stmt).continues) |
154 |
|
155 |
#define AT_TOP_LEVEL(tc) \ |
156 |
(!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK)) |
157 |
|
158 |
#define SET_STATEMENT_TOP(stmt, top) \ |
159 |
((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) |
160 |
|
161 |
struct JSTreeContext { /* tree context for semantic checks */ |
162 |
uint16 flags; /* statement state flags, see below */ |
163 |
uint16 ngvars; /* max. no. of global variables/regexps */ |
164 |
JSStmtInfo *topStmt; /* top of statement info stack */ |
165 |
JSStmtInfo *topScopeStmt; /* top lexical scope statement */ |
166 |
JSObject *blockChain; /* compile time block scope chain (NB: one |
167 |
deeper than the topScopeStmt/downScope |
168 |
chain when in head of let block/expr) */ |
169 |
JSParseNode *blockNode; /* parse node for a lexical scope. |
170 |
XXX combine with blockChain? */ |
171 |
JSAtomList decls; /* function, const, and var declarations */ |
172 |
JSParseContext *parseContext; |
173 |
|
174 |
union { |
175 |
|
176 |
JSFunction *fun; /* function to store argument and variable |
177 |
names when flags & TCF_IN_FUNCTION */ |
178 |
JSObject *scopeChain; /* scope chain object for the script */ |
179 |
} u; |
180 |
|
181 |
#ifdef JS_SCOPE_DEPTH_METER |
182 |
uint16 scopeDepth; /* current lexical scope chain depth */ |
183 |
uint16 maxScopeDepth; /* maximum lexical scope chain depth */ |
184 |
#endif |
185 |
}; |
186 |
|
187 |
#define TCF_IN_FUNCTION 0x01 /* parsing inside function body */ |
188 |
#define TCF_RETURN_EXPR 0x02 /* function has 'return expr;' */ |
189 |
#define TCF_RETURN_VOID 0x04 /* function has 'return;' */ |
190 |
#define TCF_IN_FOR_INIT 0x08 /* parsing init expr of for; exclude 'in' */ |
191 |
#define TCF_FUN_CLOSURE_VS_VAR 0x10 /* function and var with same name */ |
192 |
#define TCF_FUN_USES_NONLOCALS 0x20 /* function refers to non-local names */ |
193 |
#define TCF_FUN_HEAVYWEIGHT 0x40 /* function needs Call object per call */ |
194 |
#define TCF_FUN_IS_GENERATOR 0x80 /* parsed yield statement in function */ |
195 |
#define TCF_HAS_DEFXMLNS 0x100 /* default xml namespace = ...; parsed */ |
196 |
#define TCF_HAS_FUNCTION_STMT 0x200 /* block contains a function statement */ |
197 |
#define TCF_GENEXP_LAMBDA 0x400 /* flag lambda from generator expression */ |
198 |
#define TCF_COMPILE_N_GO 0x800 /* compiler-and-go mode of script, can |
199 |
optimize name references based on scope |
200 |
chain */ |
201 |
#define TCF_NO_SCRIPT_RVAL 0x1000 /* API caller does not want result value |
202 |
from global script */ |
203 |
/* |
204 |
* Flags to propagate out of the blocks. |
205 |
*/ |
206 |
#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID) |
207 |
|
208 |
/* |
209 |
* Flags to propagate from FunctionBody. |
210 |
*/ |
211 |
#define TCF_FUN_FLAGS (TCF_FUN_IS_GENERATOR | \ |
212 |
TCF_FUN_HEAVYWEIGHT | \ |
213 |
TCF_FUN_USES_NONLOCALS | \ |
214 |
TCF_FUN_CLOSURE_VS_VAR) |
215 |
|
216 |
/* |
217 |
* Flags field, not stored in JSTreeContext.flags, for passing staticDepth |
218 |
* into js_CompileScript. |
219 |
*/ |
220 |
#define TCF_STATIC_DEPTH_MASK 0xffff0000 |
221 |
#define TCF_GET_STATIC_DEPTH(f) ((uint32)(f) >> 16) |
222 |
#define TCF_PUT_STATIC_DEPTH(d) ((uint16)(d) << 16) |
223 |
|
224 |
#ifdef JS_SCOPE_DEPTH_METER |
225 |
# define JS_SCOPE_DEPTH_METERING(code) ((void) (code)) |
226 |
#else |
227 |
# define JS_SCOPE_DEPTH_METERING(code) ((void) 0) |
228 |
#endif |
229 |
|
230 |
#define TREE_CONTEXT_INIT(tc, pc) \ |
231 |
((tc)->flags = (tc)->ngvars = 0, \ |
232 |
(tc)->topStmt = (tc)->topScopeStmt = NULL, \ |
233 |
(tc)->blockChain = NULL, \ |
234 |
ATOM_LIST_INIT(&(tc)->decls), \ |
235 |
(tc)->blockNode = NULL, \ |
236 |
(tc)->parseContext = (pc), \ |
237 |
(tc)->u.scopeChain = NULL, \ |
238 |
JS_SCOPE_DEPTH_METERING((tc)->scopeDepth = (tc)->maxScopeDepth = 0)) |
239 |
|
240 |
/* |
241 |
* For functions TREE_CONTEXT_FINISH is called the second time to finish the |
242 |
* extra tc created during code generation. We skip stats update in such |
243 |
* cases. |
244 |
*/ |
245 |
#define TREE_CONTEXT_FINISH(cx, tc) \ |
246 |
JS_SCOPE_DEPTH_METERING( \ |
247 |
(tc)->maxScopeDepth == (uintN) -1 || \ |
248 |
JS_BASIC_STATS_ACCUM(&(cx)->runtime->lexicalScopeDepthStats, \ |
249 |
(tc)->maxScopeDepth)) |
250 |
|
251 |
/* |
252 |
* Span-dependent instructions are jumps whose span (from the jump bytecode to |
253 |
* the jump target) may require 2 or 4 bytes of immediate operand. |
254 |
*/ |
255 |
typedef struct JSSpanDep JSSpanDep; |
256 |
typedef struct JSJumpTarget JSJumpTarget; |
257 |
|
258 |
struct JSSpanDep { |
259 |
ptrdiff_t top; /* offset of first bytecode in an opcode */ |
260 |
ptrdiff_t offset; /* offset - 1 within opcode of jump operand */ |
261 |
ptrdiff_t before; /* original offset - 1 of jump operand */ |
262 |
JSJumpTarget *target; /* tagged target pointer or backpatch delta */ |
263 |
}; |
264 |
|
265 |
/* |
266 |
* Jump targets are stored in an AVL tree, for O(log(n)) lookup with targets |
267 |
* sorted by offset from left to right, so that targets after a span-dependent |
268 |
* instruction whose jump offset operand must be extended can be found quickly |
269 |
* and adjusted upward (toward higher offsets). |
270 |
*/ |
271 |
struct JSJumpTarget { |
272 |
ptrdiff_t offset; /* offset of span-dependent jump target */ |
273 |
int balance; /* AVL tree balance number */ |
274 |
JSJumpTarget *kids[2]; /* left and right AVL tree child pointers */ |
275 |
}; |
276 |
|
277 |
#define JT_LEFT 0 |
278 |
#define JT_RIGHT 1 |
279 |
#define JT_OTHER_DIR(dir) (1 - (dir)) |
280 |
#define JT_IMBALANCE(dir) (((dir) << 1) - 1) |
281 |
#define JT_DIR(imbalance) (((imbalance) + 1) >> 1) |
282 |
|
283 |
/* |
284 |
* Backpatch deltas are encoded in JSSpanDep.target if JT_TAG_BIT is clear, |
285 |
* so we can maintain backpatch chains when using span dependency records to |
286 |
* hold jump offsets that overflow 16 bits. |
287 |
*/ |
288 |
#define JT_TAG_BIT ((jsword) 1) |
289 |
#define JT_UNTAG_SHIFT 1 |
290 |
#define JT_SET_TAG(jt) ((JSJumpTarget *)((jsword)(jt) | JT_TAG_BIT)) |
291 |
#define JT_CLR_TAG(jt) ((JSJumpTarget *)((jsword)(jt) & ~JT_TAG_BIT)) |
292 |
#define JT_HAS_TAG(jt) ((jsword)(jt) & JT_TAG_BIT) |
293 |
|
294 |
#define BITS_PER_PTRDIFF (sizeof(ptrdiff_t) * JS_BITS_PER_BYTE) |
295 |
#define BITS_PER_BPDELTA (BITS_PER_PTRDIFF - 1 - JT_UNTAG_SHIFT) |
296 |
#define BPDELTA_MAX (((ptrdiff_t)1 << BITS_PER_BPDELTA) - 1) |
297 |
#define BPDELTA_TO_JT(bp) ((JSJumpTarget *)((bp) << JT_UNTAG_SHIFT)) |
298 |
#define JT_TO_BPDELTA(jt) ((ptrdiff_t)((jsword)(jt) >> JT_UNTAG_SHIFT)) |
299 |
|
300 |
#define SD_SET_TARGET(sd,jt) ((sd)->target = JT_SET_TAG(jt)) |
301 |
#define SD_GET_TARGET(sd) (JS_ASSERT(JT_HAS_TAG((sd)->target)), \ |
302 |
JT_CLR_TAG((sd)->target)) |
303 |
#define SD_SET_BPDELTA(sd,bp) ((sd)->target = BPDELTA_TO_JT(bp)) |
304 |
#define SD_GET_BPDELTA(sd) (JS_ASSERT(!JT_HAS_TAG((sd)->target)), \ |
305 |
JT_TO_BPDELTA((sd)->target)) |
306 |
|
307 |
/* Avoid asserting twice by expanding SD_GET_TARGET in the "then" clause. */ |
308 |
#define SD_SPAN(sd,pivot) (SD_GET_TARGET(sd) \ |
309 |
? JT_CLR_TAG((sd)->target)->offset - (pivot) \ |
310 |
: 0) |
311 |
|
312 |
typedef struct JSTryNode JSTryNode; |
313 |
|
314 |
struct JSTryNode { |
315 |
JSTryNote note; |
316 |
JSTryNode *prev; |
317 |
}; |
318 |
|
319 |
typedef struct JSEmittedObjectList { |
320 |
uint32 length; /* number of emitted so far objects */ |
321 |
JSParsedObjectBox *lastPob; /* last emitted object */ |
322 |
} JSEmittedObjectList; |
323 |
|
324 |
extern void |
325 |
FinishParsedObjects(JSEmittedObjectList *emittedList, JSObjectArray *objectMap); |
326 |
|
327 |
struct JSCodeGenerator { |
328 |
JSTreeContext treeContext; /* base state: statement info stack, etc. */ |
329 |
|
330 |
JSArenaPool *codePool; /* pointer to thread code arena pool */ |
331 |
JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ |
332 |
void *codeMark; /* low watermark in cg->codePool */ |
333 |
void *noteMark; /* low watermark in cg->notePool */ |
334 |
|
335 |
struct { |
336 |
jsbytecode *base; /* base of JS bytecode vector */ |
337 |
jsbytecode *limit; /* one byte beyond end of bytecode */ |
338 |
jsbytecode *next; /* pointer to next free bytecode */ |
339 |
jssrcnote *notes; /* source notes, see below */ |
340 |
uintN noteCount; /* number of source notes so far */ |
341 |
uintN noteMask; /* growth increment for notes */ |
342 |
ptrdiff_t lastNoteOffset; /* code offset for last source note */ |
343 |
uintN currentLine; /* line number for tree-based srcnote gen */ |
344 |
} prolog, main, *current; |
345 |
|
346 |
JSAtomList atomList; /* literals indexed for mapping */ |
347 |
uintN firstLine; /* first line, for js_NewScriptFromCG */ |
348 |
|
349 |
intN stackDepth; /* current stack depth in script frame */ |
350 |
uintN maxStackDepth; /* maximum stack depth so far */ |
351 |
|
352 |
uintN ntrynotes; /* number of allocated so far try notes */ |
353 |
JSTryNode *lastTryNode; /* the last allocated try node */ |
354 |
|
355 |
JSSpanDep *spanDeps; /* span dependent instruction records */ |
356 |
JSJumpTarget *jumpTargets; /* AVL tree of jump target offsets */ |
357 |
JSJumpTarget *jtFreeList; /* JT_LEFT-linked list of free structs */ |
358 |
uintN numSpanDeps; /* number of span dependencies */ |
359 |
uintN numJumpTargets; /* number of jump targets */ |
360 |
ptrdiff_t spanDepTodo; /* offset from main.base of potentially |
361 |
unoptimized spandeps */ |
362 |
|
363 |
uintN arrayCompDepth; /* stack depth of array in comprehension */ |
364 |
|
365 |
uintN emitLevel; /* js_EmitTree recursion level */ |
366 |
JSAtomList constList; /* compile time constants */ |
367 |
|
368 |
JSEmittedObjectList objectList; /* list of emitted so far objects */ |
369 |
JSEmittedObjectList regexpList; /* list of emitted so far regexp |
370 |
that will be cloned during execution */ |
371 |
|
372 |
uintN staticDepth; /* static frame chain depth */ |
373 |
JSAtomList upvarList; /* map of atoms to upvar indexes */ |
374 |
JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */ |
375 |
JSCodeGenerator *parent; /* enclosing function or global context */ |
376 |
}; |
377 |
|
378 |
#define CG_TS(cg) TS((cg)->treeContext.parseContext) |
379 |
|
380 |
#define CG_BASE(cg) ((cg)->current->base) |
381 |
#define CG_LIMIT(cg) ((cg)->current->limit) |
382 |
#define CG_NEXT(cg) ((cg)->current->next) |
383 |
#define CG_CODE(cg,offset) (CG_BASE(cg) + (offset)) |
384 |
#define CG_OFFSET(cg) PTRDIFF(CG_NEXT(cg), CG_BASE(cg), jsbytecode) |
385 |
|
386 |
#define CG_NOTES(cg) ((cg)->current->notes) |
387 |
#define CG_NOTE_COUNT(cg) ((cg)->current->noteCount) |
388 |
#define CG_NOTE_MASK(cg) ((cg)->current->noteMask) |
389 |
#define CG_LAST_NOTE_OFFSET(cg) ((cg)->current->lastNoteOffset) |
390 |
#define CG_CURRENT_LINE(cg) ((cg)->current->currentLine) |
391 |
|
392 |
#define CG_PROLOG_BASE(cg) ((cg)->prolog.base) |
393 |
#define CG_PROLOG_LIMIT(cg) ((cg)->prolog.limit) |
394 |
#define CG_PROLOG_NEXT(cg) ((cg)->prolog.next) |
395 |
#define CG_PROLOG_CODE(cg,poff) (CG_PROLOG_BASE(cg) + (poff)) |
396 |
#define CG_PROLOG_OFFSET(cg) PTRDIFF(CG_PROLOG_NEXT(cg), CG_PROLOG_BASE(cg),\ |
397 |
jsbytecode) |
398 |
|
399 |
#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) |
400 |
#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) |
401 |
|
402 |
/* |
403 |
* Initialize cg to allocate bytecode space from codePool, source note space |
404 |
* from notePool, and all other arena-allocated temporaries from cx->tempPool. |
405 |
*/ |
406 |
extern JS_FRIEND_API(void) |
407 |
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, JSParseContext *pc, |
408 |
JSArenaPool *codePool, JSArenaPool *notePool, |
409 |
uintN lineno); |
410 |
|
411 |
/* |
412 |
* Release cg->codePool, cg->notePool, and cx->tempPool to marks set by |
413 |
* js_InitCodeGenerator. Note that cgs are magic: they own the arena pool |
414 |
* "tops-of-stack" space above their codeMark, noteMark, and tempMark points. |
415 |
* This means you cannot alloc from tempPool and save the pointer beyond the |
416 |
* next JS_FinishCodeGenerator. |
417 |
*/ |
418 |
extern JS_FRIEND_API(void) |
419 |
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg); |
420 |
|
421 |
/* |
422 |
* Emit one bytecode. |
423 |
*/ |
424 |
extern ptrdiff_t |
425 |
js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op); |
426 |
|
427 |
/* |
428 |
* Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). |
429 |
*/ |
430 |
extern ptrdiff_t |
431 |
js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1); |
432 |
|
433 |
/* |
434 |
* Emit three bytecodes, an opcode with two bytes of immediate operands. |
435 |
*/ |
436 |
extern ptrdiff_t |
437 |
js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1, |
438 |
jsbytecode op2); |
439 |
|
440 |
/* |
441 |
* Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. |
442 |
*/ |
443 |
extern ptrdiff_t |
444 |
js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra); |
445 |
|
446 |
/* |
447 |
* Unsafe macro to call js_SetJumpOffset and return false if it does. |
448 |
*/ |
449 |
#define CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx,cg,pc,off,BAD_EXIT) \ |
450 |
JS_BEGIN_MACRO \ |
451 |
if (!js_SetJumpOffset(cx, cg, pc, off)) { \ |
452 |
BAD_EXIT; \ |
453 |
} \ |
454 |
JS_END_MACRO |
455 |
|
456 |
#define CHECK_AND_SET_JUMP_OFFSET(cx,cg,pc,off) \ |
457 |
CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx,cg,pc,off,return JS_FALSE) |
458 |
|
459 |
#define CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx,cg,off,BAD_EXIT) \ |
460 |
CHECK_AND_SET_JUMP_OFFSET_CUSTOM(cx, cg, CG_CODE(cg,off), \ |
461 |
CG_OFFSET(cg) - (off), BAD_EXIT) |
462 |
|
463 |
#define CHECK_AND_SET_JUMP_OFFSET_AT(cx,cg,off) \ |
464 |
CHECK_AND_SET_JUMP_OFFSET_AT_CUSTOM(cx, cg, off, return JS_FALSE) |
465 |
|
466 |
extern JSBool |
467 |
js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, |
468 |
ptrdiff_t off); |
469 |
|
470 |
/* Test whether we're in a statement of given type. */ |
471 |
extern JSBool |
472 |
js_InStatement(JSTreeContext *tc, JSStmtType type); |
473 |
|
474 |
/* Test whether we're in a with statement. */ |
475 |
#define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH) |
476 |
|
477 |
/* |
478 |
* Push the C-stack-allocated struct at stmt onto the stmtInfo stack. |
479 |
*/ |
480 |
extern void |
481 |
js_PushStatement(JSTreeContext *tc, JSStmtInfo *stmt, JSStmtType type, |
482 |
ptrdiff_t top); |
483 |
|
484 |
/* |
485 |
* Push a block scope statement and link blockObj into tc->blockChain. To pop |
486 |
* this statement info record, use js_PopStatement as usual, or if appropriate |
487 |
* (if generating code), js_PopStatementCG. |
488 |
*/ |
489 |
extern void |
490 |
js_PushBlockScope(JSTreeContext *tc, JSStmtInfo *stmt, JSObject *blockObj, |
491 |
ptrdiff_t top); |
492 |
|
493 |
/* |
494 |
* Pop tc->topStmt. If the top JSStmtInfo struct is not stack-allocated, it |
495 |
* is up to the caller to free it. |
496 |
*/ |
497 |
extern void |
498 |
js_PopStatement(JSTreeContext *tc); |
499 |
|
500 |
/* |
501 |
* Like js_PopStatement(&cg->treeContext), also patch breaks and continues |
502 |
* unless the top statement info record represents a try-catch-finally suite. |
503 |
* May fail if a jump offset overflows. |
504 |
*/ |
505 |
extern JSBool |
506 |
js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); |
507 |
|
508 |
/* |
509 |
* Define and lookup a primitive jsval associated with the const named by atom. |
510 |
* js_DefineCompileTimeConstant analyzes the constant-folded initializer at pn |
511 |
* and saves the const's value in cg->constList, if it can be used at compile |
512 |
* time. It returns true unless an error occurred. |
513 |
* |
514 |
* If the initializer's value could not be saved, js_DefineCompileTimeConstant |
515 |
* calls will return the undefined value. js_DefineCompileTimeConstant tries |
516 |
* to find a const value memorized for atom, returning true with *vp set to a |
517 |
* value other than undefined if the constant was found, true with *vp set to |
518 |
* JSVAL_VOID if not found, and false on error. |
519 |
*/ |
520 |
extern JSBool |
521 |
js_DefineCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, |
522 |
JSParseNode *pn); |
523 |
|
524 |
/* |
525 |
* Find a lexically scoped variable (one declared by let, catch, or an array |
526 |
* comprehension) named by atom, looking in tc's compile-time scopes. |
527 |
* |
528 |
* If a WITH statement is reached along the scope stack, return its statement |
529 |
* info record, so callers can tell that atom is ambiguous. If slotp is not |
530 |
* null, then if atom is found, set *slotp to its stack slot, otherwise to -1. |
531 |
* This means that if slotp is not null, all the block objects on the lexical |
532 |
* scope chain must have had their depth slots computed by the code generator, |
533 |
* so the caller must be under js_EmitTree. |
534 |
* |
535 |
* In any event, directly return the statement info record in which atom was |
536 |
* found. Otherwise return null. |
537 |
*/ |
538 |
extern JSStmtInfo * |
539 |
js_LexicalLookup(JSTreeContext *tc, JSAtom *atom, jsint *slotp); |
540 |
|
541 |
/* |
542 |
* Emit code into cg for the tree rooted at pn. |
543 |
*/ |
544 |
extern JSBool |
545 |
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); |
546 |
|
547 |
/* |
548 |
* Emit function code using cg for the tree rooted at body. |
549 |
*/ |
550 |
extern JSBool |
551 |
js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); |
552 |
|
553 |
/* |
554 |
* Source notes generated along with bytecode for decompiling and debugging. |
555 |
* A source note is a uint8 with 5 bits of type and 3 of offset from the pc of |
556 |
* the previous note. If 3 bits of offset aren't enough, extended delta notes |
557 |
* (SRC_XDELTA) consisting of 2 set high order bits followed by 6 offset bits |
558 |
* are emitted before the next note. Some notes have operand offsets encoded |
559 |
* immediately after them, in note bytes or byte-triples. |
560 |
* |
561 |
* Source Note Extended Delta |
562 |
* +7-6-5-4-3+2-1-0+ +7-6-5+4-3-2-1-0+ |
563 |
* |note-type|delta| |1 1| ext-delta | |
564 |
* +---------+-----+ +---+-----------+ |
565 |
* |
566 |
* At most one "gettable" note (i.e., a note of type other than SRC_NEWLINE, |
567 |
* SRC_SETLINE, and SRC_XDELTA) applies to a given bytecode. |
568 |
* |
569 |
* NB: the js_SrcNoteSpec array in jsemit.c is indexed by this enum, so its |
570 |
* initializers need to match the order here. |
571 |
* |
572 |
* Note on adding new source notes: every pair of bytecodes (A, B) where A and |
573 |
* B have disjoint sets of source notes that could apply to each bytecode may |
574 |
* reuse the same note type value for two notes (snA, snB) that have the same |
575 |
* arity, offsetBias, and isSpanDep initializers in js_SrcNoteSpec. This is |
576 |
* why SRC_IF and SRC_INITPROP have the same value below. For bad historical |
577 |
* reasons, some bytecodes below that could be overlayed have not been, but |
578 |
* before using SRC_EXTENDED, consider compressing the existing note types. |
579 |
* |
580 |
* Don't forget to update JSXDR_BYTECODE_VERSION in jsxdrapi.h for all such |
581 |
* incompatible source note or other bytecode changes. |
582 |
*/ |
583 |
typedef enum JSSrcNoteType { |
584 |
SRC_NULL = 0, /* terminates a note vector */ |
585 |
SRC_IF = 1, /* JSOP_IFEQ bytecode is from an if-then */ |
586 |
SRC_BREAK = 1, /* JSOP_GOTO is a break */ |
587 |
SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or |
588 |
to an index label in a regular (structuring) |
589 |
or a destructuring object initialiser */ |
590 |
SRC_GENEXP = 1, /* JSOP_ANONFUNOBJ from generator expression */ |
591 |
SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ |
592 |
SRC_FOR_IN = 2, /* JSOP_GOTO to for-in loop condition from |
593 |
before loop (same arity as SRC_IF_ELSE) */ |
594 |
SRC_FOR = 3, /* JSOP_NOP or JSOP_POP in for(;;) loop head */ |
595 |
SRC_WHILE = 4, /* JSOP_GOTO to for or while loop condition |
596 |
from before loop, else JSOP_NOP at top of |
597 |
do-while loop */ |
598 |
SRC_CONTINUE = 5, /* JSOP_GOTO is a continue, not a break; |
599 |
also used on JSOP_ENDINIT if extra comma |
600 |
at end of array literal: [1,2,,] */ |
601 |
SRC_DECL = 6, /* type of a declaration (var, const, let*) */ |
602 |
SRC_DESTRUCT = 6, /* JSOP_DUP starting a destructuring assignment |
603 |
operation, with SRC_DECL_* offset operand */ |
604 |
SRC_PCDELTA = 7, /* distance forward from comma-operator to |
605 |
next POP, or from CONDSWITCH to first CASE |
606 |
opcode, etc. -- always a forward delta */ |
607 |
SRC_GROUPASSIGN = 7, /* SRC_DESTRUCT variant for [a, b] = [c, d] */ |
608 |
SRC_ASSIGNOP = 8, /* += or another assign-op follows */ |
609 |
SRC_COND = 9, /* JSOP_IFEQ is from conditional ?: operator */ |
610 |
SRC_BRACE = 10, /* mandatory brace, for scope or to avoid |
611 |
dangling else */ |
612 |
SRC_HIDDEN = 11, /* opcode shouldn't be decompiled */ |
613 |
SRC_PCBASE = 12, /* distance back from annotated getprop or |
614 |
setprop op to left-most obj.prop.subprop |
615 |
bytecode -- always a backward delta */ |
616 |
SRC_LABEL = 13, /* JSOP_NOP for label: with atomid immediate */ |
617 |
SRC_LABELBRACE = 14, /* JSOP_NOP for label: {...} begin brace */ |
618 |
SRC_ENDBRACE = 15, /* JSOP_NOP for label: {...} end brace */ |
619 |
SRC_BREAK2LABEL = 16, /* JSOP_GOTO for 'break label' with atomid */ |
620 |
SRC_CONT2LABEL = 17, /* JSOP_GOTO for 'continue label' with atomid */ |
621 |
SRC_SWITCH = 18, /* JSOP_*SWITCH with offset to end of switch, |
622 |
2nd off to first JSOP_CASE if condswitch */ |
623 |
SRC_FUNCDEF = 19, /* JSOP_NOP for function f() with atomid */ |
624 |
SRC_CATCH = 20, /* catch block has guard */ |
625 |
SRC_EXTENDED = 21, /* extended source note, 32-159, in next byte */ |
626 |
SRC_NEWLINE = 22, /* bytecode follows a source newline */ |
627 |
SRC_SETLINE = 23, /* a file-absolute source line number note */ |
628 |
SRC_XDELTA = 24 /* 24-31 are for extended delta notes */ |
629 |
} JSSrcNoteType; |
630 |
|
631 |
/* |
632 |
* Constants for the SRC_DECL source note. Note that span-dependent bytecode |
633 |
* selection means that any SRC_DECL offset greater than SRC_DECL_LET may need |
634 |
* to be adjusted, but these "offsets" are too small to span a span-dependent |
635 |
* instruction, so can be used to denote distinct declaration syntaxes to the |
636 |
* decompiler. |
637 |
* |
638 |
* NB: the var_prefix array in jsopcode.c depends on these dense indexes from |
639 |
* SRC_DECL_VAR through SRC_DECL_LET. |
640 |
*/ |
641 |
#define SRC_DECL_VAR 0 |
642 |
#define SRC_DECL_CONST 1 |
643 |
#define SRC_DECL_LET 2 |
644 |
#define SRC_DECL_NONE 3 |
645 |
|
646 |
#define SN_TYPE_BITS 5 |
647 |
#define SN_DELTA_BITS 3 |
648 |
#define SN_XDELTA_BITS 6 |
649 |
#define SN_TYPE_MASK (JS_BITMASK(SN_TYPE_BITS) << SN_DELTA_BITS) |
650 |
#define SN_DELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_DELTA_BITS)) |
651 |
#define SN_XDELTA_MASK ((ptrdiff_t)JS_BITMASK(SN_XDELTA_BITS)) |
652 |
|
653 |
#define SN_MAKE_NOTE(sn,t,d) (*(sn) = (jssrcnote) \ |
654 |
(((t) << SN_DELTA_BITS) \ |
655 |
| ((d) & SN_DELTA_MASK))) |
656 |
#define SN_MAKE_XDELTA(sn,d) (*(sn) = (jssrcnote) \ |
657 |
((SRC_XDELTA << SN_DELTA_BITS) \ |
658 |
| ((d) & SN_XDELTA_MASK))) |
659 |
|
660 |
#define SN_IS_XDELTA(sn) ((*(sn) >> SN_DELTA_BITS) >= SRC_XDELTA) |
661 |
#define SN_TYPE(sn) ((JSSrcNoteType)(SN_IS_XDELTA(sn) \ |
662 |
? SRC_XDELTA \ |
663 |
: *(sn) >> SN_DELTA_BITS)) |
664 |
#define SN_SET_TYPE(sn,type) SN_MAKE_NOTE(sn, type, SN_DELTA(sn)) |
665 |
#define SN_IS_GETTABLE(sn) (SN_TYPE(sn) < SRC_NEWLINE) |
666 |
|
667 |
#define SN_DELTA(sn) ((ptrdiff_t)(SN_IS_XDELTA(sn) \ |
668 |
? *(sn) & SN_XDELTA_MASK \ |
669 |
: *(sn) & SN_DELTA_MASK)) |
670 |
#define SN_SET_DELTA(sn,delta) (SN_IS_XDELTA(sn) \ |
671 |
? SN_MAKE_XDELTA(sn, delta) \ |
672 |
: SN_MAKE_NOTE(sn, SN_TYPE(sn), delta)) |
673 |
|
674 |
#define SN_DELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_DELTA_BITS)) |
675 |
#define SN_XDELTA_LIMIT ((ptrdiff_t)JS_BIT(SN_XDELTA_BITS)) |
676 |
|
677 |
/* |
678 |
* Offset fields follow certain notes and are frequency-encoded: an offset in |
679 |
* [0,0x7f] consumes one byte, an offset in [0x80,0x7fffff] takes three, and |
680 |
* the high bit of the first byte is set. |
681 |
*/ |
682 |
#define SN_3BYTE_OFFSET_FLAG 0x80 |
683 |
#define SN_3BYTE_OFFSET_MASK 0x7f |
684 |
|
685 |
typedef struct JSSrcNoteSpec { |
686 |
const char *name; /* name for disassembly/debugging output */ |
687 |
int8 arity; /* number of offset operands */ |
688 |
uint8 offsetBias; /* bias of offset(s) from annotated pc */ |
689 |
int8 isSpanDep; /* 1 or -1 if offsets could span extended ops, |
690 |
0 otherwise; sign tells span direction */ |
691 |
} JSSrcNoteSpec; |
692 |
|
693 |
extern JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[]; |
694 |
extern JS_FRIEND_API(uintN) js_SrcNoteLength(jssrcnote *sn); |
695 |
|
696 |
#define SN_LENGTH(sn) ((js_SrcNoteSpec[SN_TYPE(sn)].arity == 0) ? 1 \ |
697 |
: js_SrcNoteLength(sn)) |
698 |
#define SN_NEXT(sn) ((sn) + SN_LENGTH(sn)) |
699 |
|
700 |
/* A source note array is terminated by an all-zero element. */ |
701 |
#define SN_MAKE_TERMINATOR(sn) (*(sn) = SRC_NULL) |
702 |
#define SN_IS_TERMINATOR(sn) (*(sn) == SRC_NULL) |
703 |
|
704 |
/* |
705 |
* Append a new source note of the given type (and therefore size) to cg's |
706 |
* notes dynamic array, updating cg->noteCount. Return the new note's index |
707 |
* within the array pointed at by cg->current->notes. Return -1 if out of |
708 |
* memory. |
709 |
*/ |
710 |
extern intN |
711 |
js_NewSrcNote(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type); |
712 |
|
713 |
extern intN |
714 |
js_NewSrcNote2(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, |
715 |
ptrdiff_t offset); |
716 |
|
717 |
extern intN |
718 |
js_NewSrcNote3(JSContext *cx, JSCodeGenerator *cg, JSSrcNoteType type, |
719 |
ptrdiff_t offset1, ptrdiff_t offset2); |
720 |
|
721 |
/* |
722 |
* NB: this function can add at most one extra extended delta note. |
723 |
*/ |
724 |
extern jssrcnote * |
725 |
js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn, |
726 |
ptrdiff_t delta); |
727 |
|
728 |
/* |
729 |
* Get and set the offset operand identified by which (0 for the first, etc.). |
730 |
*/ |
731 |
extern JS_FRIEND_API(ptrdiff_t) |
732 |
js_GetSrcNoteOffset(jssrcnote *sn, uintN which); |
733 |
|
734 |
extern JSBool |
735 |
js_SetSrcNoteOffset(JSContext *cx, JSCodeGenerator *cg, uintN index, |
736 |
uintN which, ptrdiff_t offset); |
737 |
|
738 |
/* |
739 |
* Finish taking source notes in cx's notePool, copying final notes to the new |
740 |
* stable store allocated by the caller and passed in via notes. Return false |
741 |
* on malloc failure, which means this function reported an error. |
742 |
* |
743 |
* To compute the number of jssrcnotes to allocate and pass in via notes, use |
744 |
* the CG_COUNT_FINAL_SRCNOTES macro. This macro knows a lot about details of |
745 |
* js_FinishTakingSrcNotes, SO DON'T CHANGE jsemit.c's js_FinishTakingSrcNotes |
746 |
* FUNCTION WITHOUT CHECKING WHETHER THIS MACRO NEEDS CORRESPONDING CHANGES! |
747 |
*/ |
748 |
#define CG_COUNT_FINAL_SRCNOTES(cg, cnt) \ |
749 |
JS_BEGIN_MACRO \ |
750 |
ptrdiff_t diff_ = CG_PROLOG_OFFSET(cg) - (cg)->prolog.lastNoteOffset; \ |
751 |
cnt = (cg)->prolog.noteCount + (cg)->main.noteCount + 1; \ |
752 |
if ((cg)->prolog.noteCount && \ |
753 |
(cg)->prolog.currentLine != (cg)->firstLine) { \ |
754 |
if (diff_ > SN_DELTA_MASK) \ |
755 |
cnt += JS_HOWMANY(diff_ - SN_DELTA_MASK, SN_XDELTA_MASK); \ |
756 |
cnt += 2 + (((cg)->firstLine > SN_3BYTE_OFFSET_MASK) << 1); \ |
757 |
} else if (diff_ > 0) { \ |
758 |
if (cg->main.noteCount) { \ |
759 |
jssrcnote *sn_ = (cg)->main.notes; \ |
760 |
diff_ -= SN_IS_XDELTA(sn_) \ |
761 |
? SN_XDELTA_MASK - (*sn_ & SN_XDELTA_MASK) \ |
762 |
: SN_DELTA_MASK - (*sn_ & SN_DELTA_MASK); \ |
763 |
} \ |
764 |
if (diff_ > 0) \ |
765 |
cnt += JS_HOWMANY(diff_, SN_XDELTA_MASK); \ |
766 |
} \ |
767 |
JS_END_MACRO |
768 |
|
769 |
extern JSBool |
770 |
js_FinishTakingSrcNotes(JSContext *cx, JSCodeGenerator *cg, jssrcnote *notes); |
771 |
|
772 |
extern void |
773 |
js_FinishTakingTryNotes(JSCodeGenerator *cg, JSTryNoteArray *array); |
774 |
|
775 |
JS_END_EXTERN_C |
776 |
|
777 |
#endif /* jsemit_h___ */ |