|
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 jsopcode_h |
|
8 #define jsopcode_h |
|
9 |
|
10 /* |
|
11 * JS bytecode definitions. |
|
12 */ |
|
13 |
|
14 #include "jsbytecode.h" |
|
15 #include "jstypes.h" |
|
16 #include "NamespaceImports.h" |
|
17 |
|
18 #include "frontend/SourceNotes.h" |
|
19 #include "vm/Opcodes.h" |
|
20 |
|
21 /* |
|
22 * JS operation bytecodes. |
|
23 */ |
|
24 typedef enum JSOp { |
|
25 #define ENUMERATE_OPCODE(op, val, ...) op = val, |
|
26 FOR_EACH_OPCODE(ENUMERATE_OPCODE) |
|
27 #undef ENUMERATE_OPCODE |
|
28 |
|
29 JSOP_LIMIT, |
|
30 |
|
31 /* |
|
32 * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETPROP, |
|
33 * JSOP_SETELEM, and comprehension-tails, respectively. They are never |
|
34 * stored in bytecode, so they don't preempt valid opcodes. |
|
35 */ |
|
36 JSOP_GETPROP2 = JSOP_LIMIT, |
|
37 JSOP_GETELEM2 = JSOP_LIMIT + 1, |
|
38 JSOP_FORLOCAL = JSOP_LIMIT + 2, |
|
39 JSOP_FAKE_LIMIT = JSOP_FORLOCAL |
|
40 } JSOp; |
|
41 |
|
42 /* |
|
43 * JS bytecode formats. |
|
44 */ |
|
45 #define JOF_BYTE 0 /* single bytecode, no immediates */ |
|
46 #define JOF_JUMP 1 /* signed 16-bit jump offset immediate */ |
|
47 #define JOF_ATOM 2 /* unsigned 16-bit constant index */ |
|
48 #define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ |
|
49 #define JOF_TABLESWITCH 4 /* table switch */ |
|
50 /* 5 is unused */ |
|
51 #define JOF_QARG 6 /* quickened get/set function argument ops */ |
|
52 #define JOF_LOCAL 7 /* var or block-local variable */ |
|
53 #define JOF_DOUBLE 8 /* uint32_t index for double value */ |
|
54 #define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ |
|
55 #define JOF_UINT8 13 /* uint8_t immediate, e.g. top 8 bits of 24-bit |
|
56 atom index */ |
|
57 #define JOF_INT32 14 /* int32_t immediate operand */ |
|
58 #define JOF_OBJECT 15 /* unsigned 16-bit object index */ |
|
59 /* 16 is unused */ |
|
60 #define JOF_REGEXP 17 /* unsigned 32-bit regexp index */ |
|
61 #define JOF_INT8 18 /* int8_t immediate operand */ |
|
62 #define JOF_ATOMOBJECT 19 /* uint16_t constant index + object index */ |
|
63 /* 20 is unused */ |
|
64 #define JOF_SCOPECOORD 21 /* embedded ScopeCoordinate immediate */ |
|
65 #define JOF_TYPEMASK 0x001f /* mask for above immediate types */ |
|
66 |
|
67 #define JOF_NAME (1U<<5) /* name operation */ |
|
68 #define JOF_PROP (2U<<5) /* obj.prop operation */ |
|
69 #define JOF_ELEM (3U<<5) /* obj[index] operation */ |
|
70 #define JOF_MODEMASK (7U<<5) /* mask for above addressing modes */ |
|
71 #define JOF_SET (1U<<8) /* set (i.e., assignment) operation */ |
|
72 /* (1U<<9) is unused*/ |
|
73 /* (1U<<10) is unused*/ |
|
74 /* (1U<<11) is unused*/ |
|
75 /* (1U<<12) is unused*/ |
|
76 /* (1U<<13) is unused*/ |
|
77 #define JOF_DETECTING (1U<<14) /* object detection for warning-quelling */ |
|
78 /* (1U<<15) is unused*/ |
|
79 #define JOF_LEFTASSOC (1U<<16) /* left-associative operator */ |
|
80 /* (1U<<17) is unused */ |
|
81 /* (1U<<18) is unused */ |
|
82 /* (1U<<19) is unused*/ |
|
83 /* (1U<<20) is unused*/ |
|
84 #define JOF_INVOKE (1U<<21) /* JSOP_CALL, JSOP_FUNCALL, JSOP_FUNAPPLY, |
|
85 JSOP_NEW, JSOP_EVAL */ |
|
86 #define JOF_TMPSLOT (1U<<22) /* interpreter uses extra temporary slot |
|
87 to root intermediate objects besides |
|
88 the slots opcode uses */ |
|
89 #define JOF_TMPSLOT2 (2U<<22) /* interpreter uses extra 2 temporary slot |
|
90 besides the slots opcode uses */ |
|
91 #define JOF_TMPSLOT3 (3U<<22) /* interpreter uses extra 3 temporary slot |
|
92 besides the slots opcode uses */ |
|
93 #define JOF_TMPSLOT_SHIFT 22 |
|
94 #define JOF_TMPSLOT_MASK (JS_BITMASK(2) << JOF_TMPSLOT_SHIFT) |
|
95 |
|
96 /* (1U<<24) is unused */ |
|
97 #define JOF_GNAME (1U<<25) /* predicted global name */ |
|
98 #define JOF_TYPESET (1U<<26) /* has an entry in a script's type sets */ |
|
99 #define JOF_ARITH (1U<<27) /* unary or binary arithmetic opcode */ |
|
100 |
|
101 /* Shorthands for type from format and type from opcode. */ |
|
102 #define JOF_TYPE(fmt) ((fmt) & JOF_TYPEMASK) |
|
103 #define JOF_OPTYPE(op) JOF_TYPE(js_CodeSpec[op].format) |
|
104 |
|
105 /* Shorthands for mode from format and mode from opcode. */ |
|
106 #define JOF_MODE(fmt) ((fmt) & JOF_MODEMASK) |
|
107 #define JOF_OPMODE(op) JOF_MODE(js_CodeSpec[op].format) |
|
108 |
|
109 /* |
|
110 * Immediate operand getters, setters, and bounds. |
|
111 */ |
|
112 |
|
113 static MOZ_ALWAYS_INLINE uint8_t |
|
114 GET_UINT8(jsbytecode *pc) |
|
115 { |
|
116 return (uint8_t) pc[1]; |
|
117 } |
|
118 |
|
119 static MOZ_ALWAYS_INLINE void |
|
120 SET_UINT8(jsbytecode *pc, uint8_t u) |
|
121 { |
|
122 pc[1] = (jsbytecode) u; |
|
123 } |
|
124 |
|
125 /* Common uint16_t immediate format helpers. */ |
|
126 #define UINT16_LEN 2 |
|
127 #define UINT16_HI(i) ((jsbytecode)((i) >> 8)) |
|
128 #define UINT16_LO(i) ((jsbytecode)(i)) |
|
129 #define GET_UINT16(pc) ((unsigned)(((pc)[1] << 8) | (pc)[2])) |
|
130 #define SET_UINT16(pc,i) ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i)) |
|
131 #define UINT16_LIMIT ((unsigned)1 << 16) |
|
132 |
|
133 /* Helpers for accessing the offsets of jump opcodes. */ |
|
134 #define JUMP_OFFSET_LEN 4 |
|
135 #define JUMP_OFFSET_MIN INT32_MIN |
|
136 #define JUMP_OFFSET_MAX INT32_MAX |
|
137 |
|
138 static MOZ_ALWAYS_INLINE int32_t |
|
139 GET_JUMP_OFFSET(jsbytecode *pc) |
|
140 { |
|
141 return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4]; |
|
142 } |
|
143 |
|
144 static MOZ_ALWAYS_INLINE void |
|
145 SET_JUMP_OFFSET(jsbytecode *pc, int32_t off) |
|
146 { |
|
147 pc[1] = (jsbytecode)(off >> 24); |
|
148 pc[2] = (jsbytecode)(off >> 16); |
|
149 pc[3] = (jsbytecode)(off >> 8); |
|
150 pc[4] = (jsbytecode)off; |
|
151 } |
|
152 |
|
153 #define UINT32_INDEX_LEN 4 |
|
154 |
|
155 static MOZ_ALWAYS_INLINE uint32_t |
|
156 GET_UINT32_INDEX(const jsbytecode *pc) |
|
157 { |
|
158 return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4]; |
|
159 } |
|
160 |
|
161 static MOZ_ALWAYS_INLINE void |
|
162 SET_UINT32_INDEX(jsbytecode *pc, uint32_t index) |
|
163 { |
|
164 pc[1] = (jsbytecode)(index >> 24); |
|
165 pc[2] = (jsbytecode)(index >> 16); |
|
166 pc[3] = (jsbytecode)(index >> 8); |
|
167 pc[4] = (jsbytecode)index; |
|
168 } |
|
169 |
|
170 #define UINT24_HI(i) ((jsbytecode)((i) >> 16)) |
|
171 #define UINT24_MID(i) ((jsbytecode)((i) >> 8)) |
|
172 #define UINT24_LO(i) ((jsbytecode)(i)) |
|
173 #define GET_UINT24(pc) ((unsigned)(((pc)[1] << 16) | \ |
|
174 ((pc)[2] << 8) | \ |
|
175 (pc)[3])) |
|
176 #define SET_UINT24(pc,i) ((pc)[1] = UINT24_HI(i), \ |
|
177 (pc)[2] = UINT24_MID(i), \ |
|
178 (pc)[3] = UINT24_LO(i)) |
|
179 |
|
180 #define GET_INT8(pc) (int8_t((pc)[1])) |
|
181 |
|
182 #define GET_INT32(pc) (((uint32_t((pc)[1]) << 24) | \ |
|
183 (uint32_t((pc)[2]) << 16) | \ |
|
184 (uint32_t((pc)[3]) << 8) | \ |
|
185 uint32_t((pc)[4]))) |
|
186 #define SET_INT32(pc,i) ((pc)[1] = (jsbytecode)(uint32_t(i) >> 24), \ |
|
187 (pc)[2] = (jsbytecode)(uint32_t(i) >> 16), \ |
|
188 (pc)[3] = (jsbytecode)(uint32_t(i) >> 8), \ |
|
189 (pc)[4] = (jsbytecode)uint32_t(i)) |
|
190 |
|
191 /* Index limit is determined by SN_4BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */ |
|
192 #define INDEX_LIMIT_LOG2 31 |
|
193 #define INDEX_LIMIT (uint32_t(1) << INDEX_LIMIT_LOG2) |
|
194 |
|
195 #define ARGC_HI(argc) UINT16_HI(argc) |
|
196 #define ARGC_LO(argc) UINT16_LO(argc) |
|
197 #define GET_ARGC(pc) GET_UINT16(pc) |
|
198 #define ARGC_LIMIT UINT16_LIMIT |
|
199 |
|
200 #define GET_ARGNO(pc) GET_UINT16(pc) |
|
201 #define SET_ARGNO(pc,argno) SET_UINT16(pc,argno) |
|
202 #define ARGNO_LEN 2 |
|
203 #define ARGNO_LIMIT UINT16_LIMIT |
|
204 |
|
205 #define GET_LOCALNO(pc) GET_UINT24(pc) |
|
206 #define SET_LOCALNO(pc,varno) SET_UINT24(pc,varno) |
|
207 #define LOCALNO_LEN 3 |
|
208 #define LOCALNO_BITS 24 |
|
209 #define LOCALNO_LIMIT (1 << LOCALNO_BITS) |
|
210 |
|
211 static inline unsigned |
|
212 LoopEntryDepthHint(jsbytecode *pc) |
|
213 { |
|
214 JS_ASSERT(*pc == JSOP_LOOPENTRY); |
|
215 return GET_UINT8(pc) & 0x7f; |
|
216 } |
|
217 static inline bool |
|
218 LoopEntryCanIonOsr(jsbytecode *pc) |
|
219 { |
|
220 JS_ASSERT(*pc == JSOP_LOOPENTRY); |
|
221 return GET_UINT8(pc) & 0x80; |
|
222 } |
|
223 static inline uint8_t |
|
224 PackLoopEntryDepthHintAndFlags(unsigned loopDepth, bool canIonOsr) |
|
225 { |
|
226 return (loopDepth < 0x80 ? uint8_t(loopDepth) : 0x7f) | (canIonOsr ? 0x80 : 0); |
|
227 } |
|
228 |
|
229 /* |
|
230 * Describes the 'hops' component of a JOF_SCOPECOORD opcode. |
|
231 * |
|
232 * Note: this component is only 8 bits wide, limiting the maximum number of |
|
233 * scopes between a use and def to roughly 255. This is a pretty small limit but |
|
234 * note that SpiderMonkey's recursive descent parser can only parse about this |
|
235 * many functions before hitting the C-stack recursion limit so this shouldn't |
|
236 * be a significant limitation in practice. |
|
237 */ |
|
238 #define GET_SCOPECOORD_HOPS(pc) GET_UINT8(pc) |
|
239 #define SET_SCOPECOORD_HOPS(pc,hops) SET_UINT8(pc,hops) |
|
240 #define SCOPECOORD_HOPS_LEN 1 |
|
241 #define SCOPECOORD_HOPS_BITS 8 |
|
242 #define SCOPECOORD_HOPS_LIMIT (1 << SCOPECOORD_HOPS_BITS) |
|
243 |
|
244 /* Describes the 'slot' component of a JOF_SCOPECOORD opcode. */ |
|
245 #define GET_SCOPECOORD_SLOT(pc) GET_UINT24(pc) |
|
246 #define SET_SCOPECOORD_SLOT(pc,slot) SET_UINT24(pc,slot) |
|
247 #define SCOPECOORD_SLOT_LEN 3 |
|
248 #define SCOPECOORD_SLOT_BITS 24 |
|
249 #define SCOPECOORD_SLOT_LIMIT (1 << SCOPECOORD_SLOT_BITS) |
|
250 |
|
251 struct JSCodeSpec { |
|
252 int8_t length; /* length including opcode byte */ |
|
253 int8_t nuses; /* arity, -1 if variadic */ |
|
254 int8_t ndefs; /* number of stack results */ |
|
255 uint32_t format; /* immediate operand format */ |
|
256 |
|
257 uint32_t type() const { return JOF_TYPE(format); } |
|
258 }; |
|
259 |
|
260 extern const JSCodeSpec js_CodeSpec[]; |
|
261 extern const unsigned js_NumCodeSpecs; |
|
262 extern const char * const js_CodeName[]; |
|
263 extern const char js_EscapeMap[]; |
|
264 |
|
265 /* Silence unreferenced formal parameter warnings */ |
|
266 #ifdef _MSC_VER |
|
267 #pragma warning(push) |
|
268 #pragma warning(disable:4100) |
|
269 #endif |
|
270 |
|
271 /* |
|
272 * Return a GC'ed string containing the chars in str, with any non-printing |
|
273 * chars or quotes (' or " as specified by the quote argument) escaped, and |
|
274 * with the quote character at the beginning and end of the result string. |
|
275 */ |
|
276 extern JSString * |
|
277 js_QuoteString(js::ExclusiveContext *cx, JSString *str, jschar quote); |
|
278 |
|
279 namespace js { |
|
280 |
|
281 static inline bool |
|
282 IsJumpOpcode(JSOp op) |
|
283 { |
|
284 uint32_t type = JOF_TYPE(js_CodeSpec[op].format); |
|
285 |
|
286 /* |
|
287 * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as |
|
288 * jumps to avoid degrading precision. |
|
289 */ |
|
290 return type == JOF_JUMP && op != JSOP_LABEL; |
|
291 } |
|
292 |
|
293 static inline bool |
|
294 BytecodeFallsThrough(JSOp op) |
|
295 { |
|
296 switch (op) { |
|
297 case JSOP_GOTO: |
|
298 case JSOP_DEFAULT: |
|
299 case JSOP_RETURN: |
|
300 case JSOP_RETRVAL: |
|
301 case JSOP_THROW: |
|
302 case JSOP_TABLESWITCH: |
|
303 return false; |
|
304 case JSOP_GOSUB: |
|
305 /* These fall through indirectly, after executing a 'finally'. */ |
|
306 return true; |
|
307 default: |
|
308 return true; |
|
309 } |
|
310 } |
|
311 |
|
312 class SrcNoteLineScanner |
|
313 { |
|
314 /* offset of the current JSOp in the bytecode */ |
|
315 ptrdiff_t offset; |
|
316 |
|
317 /* next src note to process */ |
|
318 jssrcnote *sn; |
|
319 |
|
320 /* line number of the current JSOp */ |
|
321 uint32_t lineno; |
|
322 |
|
323 /* |
|
324 * Is the current op the first one after a line change directive? Note that |
|
325 * multiple ops may be "first" if a line directive is used to return to a |
|
326 * previous line (eg, with a for loop increment expression.) |
|
327 */ |
|
328 bool lineHeader; |
|
329 |
|
330 public: |
|
331 SrcNoteLineScanner(jssrcnote *sn, uint32_t lineno) |
|
332 : offset(0), sn(sn), lineno(lineno) |
|
333 { |
|
334 } |
|
335 |
|
336 /* |
|
337 * This is called repeatedly with always-advancing relpc values. The src |
|
338 * notes are tuples of <PC offset from prev src note, type, args>. Scan |
|
339 * through, updating the lineno, until the next src note is for a later |
|
340 * bytecode. |
|
341 * |
|
342 * When looking at the desired PC offset ('relpc'), the op is first in that |
|
343 * line iff there is a SRC_SETLINE or SRC_NEWLINE src note for that exact |
|
344 * bytecode. |
|
345 * |
|
346 * Note that a single bytecode may have multiple line-modifying notes (even |
|
347 * though only one should ever be needed.) |
|
348 */ |
|
349 void advanceTo(ptrdiff_t relpc) { |
|
350 // Must always advance! If the same or an earlier PC is erroneously |
|
351 // passed in, we will already be past the relevant src notes |
|
352 JS_ASSERT_IF(offset > 0, relpc > offset); |
|
353 |
|
354 // Next src note should be for after the current offset |
|
355 JS_ASSERT_IF(offset > 0, SN_IS_TERMINATOR(sn) || SN_DELTA(sn) > 0); |
|
356 |
|
357 // The first PC requested is always considered to be a line header |
|
358 lineHeader = (offset == 0); |
|
359 |
|
360 if (SN_IS_TERMINATOR(sn)) |
|
361 return; |
|
362 |
|
363 ptrdiff_t nextOffset; |
|
364 while ((nextOffset = offset + SN_DELTA(sn)) <= relpc && !SN_IS_TERMINATOR(sn)) { |
|
365 offset = nextOffset; |
|
366 SrcNoteType type = (SrcNoteType) SN_TYPE(sn); |
|
367 if (type == SRC_SETLINE || type == SRC_NEWLINE) { |
|
368 if (type == SRC_SETLINE) |
|
369 lineno = js_GetSrcNoteOffset(sn, 0); |
|
370 else |
|
371 lineno++; |
|
372 |
|
373 if (offset == relpc) |
|
374 lineHeader = true; |
|
375 } |
|
376 |
|
377 sn = SN_NEXT(sn); |
|
378 } |
|
379 } |
|
380 |
|
381 bool isLineHeader() const { |
|
382 return lineHeader; |
|
383 } |
|
384 |
|
385 uint32_t getLine() const { return lineno; } |
|
386 }; |
|
387 |
|
388 extern unsigned |
|
389 StackUses(JSScript *script, jsbytecode *pc); |
|
390 |
|
391 extern unsigned |
|
392 StackDefs(JSScript *script, jsbytecode *pc); |
|
393 |
|
394 #ifdef DEBUG |
|
395 /* |
|
396 * Given bytecode address pc in script's main program code, compute the operand |
|
397 * stack depth just before (JSOp) *pc executes. If *pc is not reachable, return |
|
398 * false. |
|
399 */ |
|
400 extern bool |
|
401 ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc, uint32_t *depth, bool *reachablePC); |
|
402 #endif |
|
403 |
|
404 } /* namespace js */ |
|
405 |
|
406 #ifdef _MSC_VER |
|
407 #pragma warning(pop) |
|
408 #endif |
|
409 |
|
410 #define JSDVG_IGNORE_STACK 0 |
|
411 #define JSDVG_SEARCH_STACK 1 |
|
412 |
|
413 /* |
|
414 * Get the length of variable-length bytecode like JSOP_TABLESWITCH. |
|
415 */ |
|
416 extern size_t |
|
417 js_GetVariableBytecodeLength(jsbytecode *pc); |
|
418 |
|
419 namespace js { |
|
420 |
|
421 /* |
|
422 * Find the source expression that resulted in v, and return a newly allocated |
|
423 * C-string containing it. Fall back on v's string conversion (fallback) if we |
|
424 * can't find the bytecode that generated and pushed v on the operand stack. |
|
425 * |
|
426 * Search the current stack frame if spindex is JSDVG_SEARCH_STACK. Don't |
|
427 * look for v on the stack if spindex is JSDVG_IGNORE_STACK. Otherwise, |
|
428 * spindex is the negative index of v, measured from cx->fp->sp, or from a |
|
429 * lower frame's sp if cx->fp is native. |
|
430 * |
|
431 * The optional argument skipStackHits can be used to skip a hit in the stack |
|
432 * frame. This can be useful in self-hosted code that wants to report value |
|
433 * errors containing decompiled values that are useful for the user, instead of |
|
434 * values used internally by the self-hosted code. |
|
435 * |
|
436 * The caller must call JS_free on the result after a successful call. |
|
437 */ |
|
438 char * |
|
439 DecompileValueGenerator(JSContext *cx, int spindex, HandleValue v, |
|
440 HandleString fallback, int skipStackHits = 0); |
|
441 |
|
442 /* |
|
443 * Decompile the formal argument at formalIndex in the nearest non-builtin |
|
444 * stack frame, falling back with converting v to source. |
|
445 */ |
|
446 char * |
|
447 DecompileArgument(JSContext *cx, int formalIndex, HandleValue v); |
|
448 |
|
449 /* |
|
450 * Sprintf, but with unlimited and automatically allocated buffering. |
|
451 */ |
|
452 class Sprinter |
|
453 { |
|
454 public: |
|
455 struct InvariantChecker |
|
456 { |
|
457 const Sprinter *parent; |
|
458 |
|
459 explicit InvariantChecker(const Sprinter *p) : parent(p) { |
|
460 parent->checkInvariants(); |
|
461 } |
|
462 |
|
463 ~InvariantChecker() { |
|
464 parent->checkInvariants(); |
|
465 } |
|
466 }; |
|
467 |
|
468 ExclusiveContext *context; /* context executing the decompiler */ |
|
469 |
|
470 private: |
|
471 static const size_t DefaultSize; |
|
472 #ifdef DEBUG |
|
473 bool initialized; /* true if this is initialized, use for debug builds */ |
|
474 #endif |
|
475 char *base; /* malloc'd buffer address */ |
|
476 size_t size; /* size of buffer allocated at base */ |
|
477 ptrdiff_t offset; /* offset of next free char in buffer */ |
|
478 bool reportedOOM; /* this sprinter has reported OOM in string ops */ |
|
479 |
|
480 bool realloc_(size_t newSize); |
|
481 |
|
482 public: |
|
483 explicit Sprinter(ExclusiveContext *cx); |
|
484 ~Sprinter(); |
|
485 |
|
486 /* Initialize this sprinter, returns false on error */ |
|
487 bool init(); |
|
488 |
|
489 void checkInvariants() const; |
|
490 |
|
491 const char *string() const; |
|
492 const char *stringEnd() const; |
|
493 /* Returns the string at offset |off| */ |
|
494 char *stringAt(ptrdiff_t off) const; |
|
495 /* Returns the char at offset |off| */ |
|
496 char &operator[](size_t off); |
|
497 |
|
498 /* |
|
499 * Attempt to reserve len + 1 space (for a trailing nullptr byte). If the |
|
500 * attempt succeeds, return a pointer to the start of that space and adjust the |
|
501 * internal content. The caller *must* completely fill this space on success. |
|
502 */ |
|
503 char *reserve(size_t len); |
|
504 |
|
505 /* |
|
506 * Puts |len| characters from |s| at the current position and return an offset to |
|
507 * the beginning of this new data |
|
508 */ |
|
509 ptrdiff_t put(const char *s, size_t len); |
|
510 ptrdiff_t put(const char *s); |
|
511 ptrdiff_t putString(JSString *str); |
|
512 |
|
513 /* Prints a formatted string into the buffer */ |
|
514 int printf(const char *fmt, ...); |
|
515 |
|
516 ptrdiff_t getOffset() const; |
|
517 |
|
518 /* |
|
519 * Report that a string operation failed to get the memory it requested. The |
|
520 * first call to this function calls JS_ReportOutOfMemory, and sets this |
|
521 * Sprinter's outOfMemory flag; subsequent calls do nothing. |
|
522 */ |
|
523 void reportOutOfMemory(); |
|
524 |
|
525 /* Return true if this Sprinter ran out of memory. */ |
|
526 bool hadOutOfMemory() const; |
|
527 }; |
|
528 |
|
529 extern ptrdiff_t |
|
530 Sprint(Sprinter *sp, const char *format, ...); |
|
531 |
|
532 extern bool |
|
533 CallResultEscapes(jsbytecode *pc); |
|
534 |
|
535 static inline unsigned |
|
536 GetDecomposeLength(jsbytecode *pc, size_t len) |
|
537 { |
|
538 /* |
|
539 * The last byte of a DECOMPOSE op stores the decomposed length. This is a |
|
540 * constant: perhaps we should just hardcode values instead? |
|
541 */ |
|
542 JS_ASSERT(size_t(js_CodeSpec[*pc].length) == len); |
|
543 return (unsigned) pc[len - 1]; |
|
544 } |
|
545 |
|
546 static inline unsigned |
|
547 GetBytecodeLength(jsbytecode *pc) |
|
548 { |
|
549 JSOp op = (JSOp)*pc; |
|
550 JS_ASSERT(op < JSOP_LIMIT); |
|
551 |
|
552 if (js_CodeSpec[op].length != -1) |
|
553 return js_CodeSpec[op].length; |
|
554 return js_GetVariableBytecodeLength(pc); |
|
555 } |
|
556 |
|
557 static inline bool |
|
558 BytecodeIsPopped(jsbytecode *pc) |
|
559 { |
|
560 jsbytecode *next = pc + GetBytecodeLength(pc); |
|
561 return JSOp(*next) == JSOP_POP; |
|
562 } |
|
563 |
|
564 static inline bool |
|
565 BytecodeFlowsToBitop(jsbytecode *pc) |
|
566 { |
|
567 // Look for simple bytecode for integer conversions like (x | 0) or (x & -1). |
|
568 jsbytecode *next = pc + GetBytecodeLength(pc); |
|
569 if (*next == JSOP_BITOR || *next == JSOP_BITAND) |
|
570 return true; |
|
571 if (*next == JSOP_INT8 && GET_INT8(next) == -1) { |
|
572 next += GetBytecodeLength(next); |
|
573 if (*next == JSOP_BITAND) |
|
574 return true; |
|
575 return false; |
|
576 } |
|
577 if (*next == JSOP_ONE) { |
|
578 next += GetBytecodeLength(next); |
|
579 if (*next == JSOP_NEG) { |
|
580 next += GetBytecodeLength(next); |
|
581 if (*next == JSOP_BITAND) |
|
582 return true; |
|
583 } |
|
584 return false; |
|
585 } |
|
586 if (*next == JSOP_ZERO) { |
|
587 next += GetBytecodeLength(next); |
|
588 if (*next == JSOP_BITOR) |
|
589 return true; |
|
590 return false; |
|
591 } |
|
592 return false; |
|
593 } |
|
594 |
|
595 extern bool |
|
596 IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset); |
|
597 |
|
598 inline bool |
|
599 FlowsIntoNext(JSOp op) |
|
600 { |
|
601 /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */ |
|
602 return op != JSOP_RETRVAL && op != JSOP_RETURN && op != JSOP_THROW && |
|
603 op != JSOP_GOTO && op != JSOP_RETSUB; |
|
604 } |
|
605 |
|
606 inline bool |
|
607 IsArgOp(JSOp op) |
|
608 { |
|
609 return JOF_OPTYPE(op) == JOF_QARG; |
|
610 } |
|
611 |
|
612 inline bool |
|
613 IsLocalOp(JSOp op) |
|
614 { |
|
615 return JOF_OPTYPE(op) == JOF_LOCAL; |
|
616 } |
|
617 |
|
618 inline bool |
|
619 IsAliasedVarOp(JSOp op) |
|
620 { |
|
621 return JOF_OPTYPE(op) == JOF_SCOPECOORD; |
|
622 } |
|
623 |
|
624 inline bool |
|
625 IsGlobalOp(JSOp op) |
|
626 { |
|
627 return js_CodeSpec[op].format & JOF_GNAME; |
|
628 } |
|
629 |
|
630 inline bool |
|
631 IsEqualityOp(JSOp op) |
|
632 { |
|
633 return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE; |
|
634 } |
|
635 |
|
636 inline bool |
|
637 IsGetPropPC(jsbytecode *pc) |
|
638 { |
|
639 JSOp op = JSOp(*pc); |
|
640 return op == JSOP_LENGTH || op == JSOP_GETPROP || op == JSOP_CALLPROP; |
|
641 } |
|
642 |
|
643 inline bool |
|
644 IsSetPropPC(jsbytecode *pc) |
|
645 { |
|
646 JSOp op = JSOp(*pc); |
|
647 return op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETGNAME; |
|
648 } |
|
649 |
|
650 inline bool |
|
651 IsGetElemPC(jsbytecode *pc) |
|
652 { |
|
653 JSOp op = JSOp(*pc); |
|
654 return op == JSOP_GETELEM || op == JSOP_CALLELEM; |
|
655 } |
|
656 |
|
657 inline bool |
|
658 IsSetElemPC(jsbytecode *pc) |
|
659 { |
|
660 JSOp op = JSOp(*pc); |
|
661 return op == JSOP_SETELEM; |
|
662 } |
|
663 |
|
664 inline bool |
|
665 IsCallPC(jsbytecode *pc) |
|
666 { |
|
667 return js_CodeSpec[*pc].format & JOF_INVOKE; |
|
668 } |
|
669 |
|
670 static inline int32_t |
|
671 GetBytecodeInteger(jsbytecode *pc) |
|
672 { |
|
673 switch (JSOp(*pc)) { |
|
674 case JSOP_ZERO: return 0; |
|
675 case JSOP_ONE: return 1; |
|
676 case JSOP_UINT16: return GET_UINT16(pc); |
|
677 case JSOP_UINT24: return GET_UINT24(pc); |
|
678 case JSOP_INT8: return GET_INT8(pc); |
|
679 case JSOP_INT32: return GET_INT32(pc); |
|
680 default: |
|
681 MOZ_ASSUME_UNREACHABLE("Bad op"); |
|
682 } |
|
683 } |
|
684 |
|
685 /* |
|
686 * Counts accumulated for a single opcode in a script. The counts tracked vary |
|
687 * between opcodes, and this structure ensures that counts are accessed in a |
|
688 * coherent fashion. |
|
689 */ |
|
690 class PCCounts |
|
691 { |
|
692 friend class ::JSScript; |
|
693 double *counts; |
|
694 #ifdef DEBUG |
|
695 size_t capacity; |
|
696 #elif JS_BITS_PER_WORD == 32 |
|
697 void *padding; |
|
698 #endif |
|
699 |
|
700 public: |
|
701 |
|
702 enum BaseCounts { |
|
703 BASE_INTERP = 0, |
|
704 |
|
705 BASE_LIMIT |
|
706 }; |
|
707 |
|
708 enum AccessCounts { |
|
709 ACCESS_MONOMORPHIC = BASE_LIMIT, |
|
710 ACCESS_DIMORPHIC, |
|
711 ACCESS_POLYMORPHIC, |
|
712 |
|
713 ACCESS_BARRIER, |
|
714 ACCESS_NOBARRIER, |
|
715 |
|
716 ACCESS_UNDEFINED, |
|
717 ACCESS_NULL, |
|
718 ACCESS_BOOLEAN, |
|
719 ACCESS_INT32, |
|
720 ACCESS_DOUBLE, |
|
721 ACCESS_STRING, |
|
722 ACCESS_OBJECT, |
|
723 |
|
724 ACCESS_LIMIT |
|
725 }; |
|
726 |
|
727 static bool accessOp(JSOp op) { |
|
728 /* |
|
729 * Access ops include all name, element and property reads, as well as |
|
730 * SETELEM and SETPROP (for ElementCounts/PropertyCounts alignment). |
|
731 */ |
|
732 if (op == JSOP_SETELEM || op == JSOP_SETPROP) |
|
733 return true; |
|
734 int format = js_CodeSpec[op].format; |
|
735 return !!(format & (JOF_NAME | JOF_GNAME | JOF_ELEM | JOF_PROP)) |
|
736 && !(format & JOF_SET); |
|
737 } |
|
738 |
|
739 enum ElementCounts { |
|
740 ELEM_ID_INT = ACCESS_LIMIT, |
|
741 ELEM_ID_DOUBLE, |
|
742 ELEM_ID_OTHER, |
|
743 ELEM_ID_UNKNOWN, |
|
744 |
|
745 ELEM_OBJECT_TYPED, |
|
746 ELEM_OBJECT_PACKED, |
|
747 ELEM_OBJECT_DENSE, |
|
748 ELEM_OBJECT_OTHER, |
|
749 |
|
750 ELEM_LIMIT |
|
751 }; |
|
752 |
|
753 static bool elementOp(JSOp op) { |
|
754 return accessOp(op) && (JOF_MODE(js_CodeSpec[op].format) == JOF_ELEM); |
|
755 } |
|
756 |
|
757 enum PropertyCounts { |
|
758 PROP_STATIC = ACCESS_LIMIT, |
|
759 PROP_DEFINITE, |
|
760 PROP_OTHER, |
|
761 |
|
762 PROP_LIMIT |
|
763 }; |
|
764 |
|
765 static bool propertyOp(JSOp op) { |
|
766 return accessOp(op) && (JOF_MODE(js_CodeSpec[op].format) == JOF_PROP); |
|
767 } |
|
768 |
|
769 enum ArithCounts { |
|
770 ARITH_INT = BASE_LIMIT, |
|
771 ARITH_DOUBLE, |
|
772 ARITH_OTHER, |
|
773 ARITH_UNKNOWN, |
|
774 |
|
775 ARITH_LIMIT |
|
776 }; |
|
777 |
|
778 static bool arithOp(JSOp op) { |
|
779 return !!(js_CodeSpec[op].format & JOF_ARITH); |
|
780 } |
|
781 |
|
782 static size_t numCounts(JSOp op) |
|
783 { |
|
784 if (accessOp(op)) { |
|
785 if (elementOp(op)) |
|
786 return ELEM_LIMIT; |
|
787 if (propertyOp(op)) |
|
788 return PROP_LIMIT; |
|
789 return ACCESS_LIMIT; |
|
790 } |
|
791 if (arithOp(op)) |
|
792 return ARITH_LIMIT; |
|
793 return BASE_LIMIT; |
|
794 } |
|
795 |
|
796 static const char *countName(JSOp op, size_t which); |
|
797 |
|
798 double *rawCounts() const { return counts; } |
|
799 |
|
800 double& get(size_t which) { |
|
801 JS_ASSERT(which < capacity); |
|
802 return counts[which]; |
|
803 } |
|
804 |
|
805 /* Boolean conversion, for 'if (counters) ...' */ |
|
806 operator void*() const { |
|
807 return counts; |
|
808 } |
|
809 }; |
|
810 |
|
811 /* Necessary for alignment with the script. */ |
|
812 JS_STATIC_ASSERT(sizeof(PCCounts) % sizeof(Value) == 0); |
|
813 |
|
814 static inline jsbytecode * |
|
815 GetNextPc(jsbytecode *pc) |
|
816 { |
|
817 return pc + GetBytecodeLength(pc); |
|
818 } |
|
819 |
|
820 } /* namespace js */ |
|
821 |
|
822 #if defined(DEBUG) |
|
823 /* |
|
824 * Disassemblers, for debugging only. |
|
825 */ |
|
826 bool |
|
827 js_Disassemble(JSContext *cx, JS::Handle<JSScript*> script, bool lines, js::Sprinter *sp); |
|
828 |
|
829 unsigned |
|
830 js_Disassemble1(JSContext *cx, JS::Handle<JSScript*> script, jsbytecode *pc, unsigned loc, |
|
831 bool lines, js::Sprinter *sp); |
|
832 |
|
833 #endif |
|
834 |
|
835 void |
|
836 js_DumpPCCounts(JSContext *cx, JS::Handle<JSScript*> script, js::Sprinter *sp); |
|
837 |
|
838 #ifdef JS_ION |
|
839 namespace js { |
|
840 namespace jit { struct IonScriptCounts; } |
|
841 void |
|
842 DumpIonScriptCounts(js::Sprinter *sp, jit::IonScriptCounts *ionCounts); |
|
843 } |
|
844 #endif |
|
845 |
|
846 #endif /* jsopcode_h */ |