Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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/. */
7 #ifndef jsopcode_h
8 #define jsopcode_h
10 /*
11 * JS bytecode definitions.
12 */
14 #include "jsbytecode.h"
15 #include "jstypes.h"
16 #include "NamespaceImports.h"
18 #include "frontend/SourceNotes.h"
19 #include "vm/Opcodes.h"
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
29 JSOP_LIMIT,
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;
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 */
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)
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 */
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)
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)
109 /*
110 * Immediate operand getters, setters, and bounds.
111 */
113 static MOZ_ALWAYS_INLINE uint8_t
114 GET_UINT8(jsbytecode *pc)
115 {
116 return (uint8_t) pc[1];
117 }
119 static MOZ_ALWAYS_INLINE void
120 SET_UINT8(jsbytecode *pc, uint8_t u)
121 {
122 pc[1] = (jsbytecode) u;
123 }
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)
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
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 }
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 }
153 #define UINT32_INDEX_LEN 4
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 }
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 }
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))
180 #define GET_INT8(pc) (int8_t((pc)[1]))
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))
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)
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
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
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)
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 }
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)
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)
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 */
257 uint32_t type() const { return JOF_TYPE(format); }
258 };
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[];
265 /* Silence unreferenced formal parameter warnings */
266 #ifdef _MSC_VER
267 #pragma warning(push)
268 #pragma warning(disable:4100)
269 #endif
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);
279 namespace js {
281 static inline bool
282 IsJumpOpcode(JSOp op)
283 {
284 uint32_t type = JOF_TYPE(js_CodeSpec[op].format);
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 }
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 }
312 class SrcNoteLineScanner
313 {
314 /* offset of the current JSOp in the bytecode */
315 ptrdiff_t offset;
317 /* next src note to process */
318 jssrcnote *sn;
320 /* line number of the current JSOp */
321 uint32_t lineno;
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;
330 public:
331 SrcNoteLineScanner(jssrcnote *sn, uint32_t lineno)
332 : offset(0), sn(sn), lineno(lineno)
333 {
334 }
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);
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);
357 // The first PC requested is always considered to be a line header
358 lineHeader = (offset == 0);
360 if (SN_IS_TERMINATOR(sn))
361 return;
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++;
373 if (offset == relpc)
374 lineHeader = true;
375 }
377 sn = SN_NEXT(sn);
378 }
379 }
381 bool isLineHeader() const {
382 return lineHeader;
383 }
385 uint32_t getLine() const { return lineno; }
386 };
388 extern unsigned
389 StackUses(JSScript *script, jsbytecode *pc);
391 extern unsigned
392 StackDefs(JSScript *script, jsbytecode *pc);
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
404 } /* namespace js */
406 #ifdef _MSC_VER
407 #pragma warning(pop)
408 #endif
410 #define JSDVG_IGNORE_STACK 0
411 #define JSDVG_SEARCH_STACK 1
413 /*
414 * Get the length of variable-length bytecode like JSOP_TABLESWITCH.
415 */
416 extern size_t
417 js_GetVariableBytecodeLength(jsbytecode *pc);
419 namespace js {
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);
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);
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;
459 explicit InvariantChecker(const Sprinter *p) : parent(p) {
460 parent->checkInvariants();
461 }
463 ~InvariantChecker() {
464 parent->checkInvariants();
465 }
466 };
468 ExclusiveContext *context; /* context executing the decompiler */
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 */
480 bool realloc_(size_t newSize);
482 public:
483 explicit Sprinter(ExclusiveContext *cx);
484 ~Sprinter();
486 /* Initialize this sprinter, returns false on error */
487 bool init();
489 void checkInvariants() const;
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);
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);
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);
513 /* Prints a formatted string into the buffer */
514 int printf(const char *fmt, ...);
516 ptrdiff_t getOffset() const;
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();
525 /* Return true if this Sprinter ran out of memory. */
526 bool hadOutOfMemory() const;
527 };
529 extern ptrdiff_t
530 Sprint(Sprinter *sp, const char *format, ...);
532 extern bool
533 CallResultEscapes(jsbytecode *pc);
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 }
546 static inline unsigned
547 GetBytecodeLength(jsbytecode *pc)
548 {
549 JSOp op = (JSOp)*pc;
550 JS_ASSERT(op < JSOP_LIMIT);
552 if (js_CodeSpec[op].length != -1)
553 return js_CodeSpec[op].length;
554 return js_GetVariableBytecodeLength(pc);
555 }
557 static inline bool
558 BytecodeIsPopped(jsbytecode *pc)
559 {
560 jsbytecode *next = pc + GetBytecodeLength(pc);
561 return JSOp(*next) == JSOP_POP;
562 }
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 }
595 extern bool
596 IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset);
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 }
606 inline bool
607 IsArgOp(JSOp op)
608 {
609 return JOF_OPTYPE(op) == JOF_QARG;
610 }
612 inline bool
613 IsLocalOp(JSOp op)
614 {
615 return JOF_OPTYPE(op) == JOF_LOCAL;
616 }
618 inline bool
619 IsAliasedVarOp(JSOp op)
620 {
621 return JOF_OPTYPE(op) == JOF_SCOPECOORD;
622 }
624 inline bool
625 IsGlobalOp(JSOp op)
626 {
627 return js_CodeSpec[op].format & JOF_GNAME;
628 }
630 inline bool
631 IsEqualityOp(JSOp op)
632 {
633 return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
634 }
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 }
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 }
650 inline bool
651 IsGetElemPC(jsbytecode *pc)
652 {
653 JSOp op = JSOp(*pc);
654 return op == JSOP_GETELEM || op == JSOP_CALLELEM;
655 }
657 inline bool
658 IsSetElemPC(jsbytecode *pc)
659 {
660 JSOp op = JSOp(*pc);
661 return op == JSOP_SETELEM;
662 }
664 inline bool
665 IsCallPC(jsbytecode *pc)
666 {
667 return js_CodeSpec[*pc].format & JOF_INVOKE;
668 }
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 }
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
700 public:
702 enum BaseCounts {
703 BASE_INTERP = 0,
705 BASE_LIMIT
706 };
708 enum AccessCounts {
709 ACCESS_MONOMORPHIC = BASE_LIMIT,
710 ACCESS_DIMORPHIC,
711 ACCESS_POLYMORPHIC,
713 ACCESS_BARRIER,
714 ACCESS_NOBARRIER,
716 ACCESS_UNDEFINED,
717 ACCESS_NULL,
718 ACCESS_BOOLEAN,
719 ACCESS_INT32,
720 ACCESS_DOUBLE,
721 ACCESS_STRING,
722 ACCESS_OBJECT,
724 ACCESS_LIMIT
725 };
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 }
739 enum ElementCounts {
740 ELEM_ID_INT = ACCESS_LIMIT,
741 ELEM_ID_DOUBLE,
742 ELEM_ID_OTHER,
743 ELEM_ID_UNKNOWN,
745 ELEM_OBJECT_TYPED,
746 ELEM_OBJECT_PACKED,
747 ELEM_OBJECT_DENSE,
748 ELEM_OBJECT_OTHER,
750 ELEM_LIMIT
751 };
753 static bool elementOp(JSOp op) {
754 return accessOp(op) && (JOF_MODE(js_CodeSpec[op].format) == JOF_ELEM);
755 }
757 enum PropertyCounts {
758 PROP_STATIC = ACCESS_LIMIT,
759 PROP_DEFINITE,
760 PROP_OTHER,
762 PROP_LIMIT
763 };
765 static bool propertyOp(JSOp op) {
766 return accessOp(op) && (JOF_MODE(js_CodeSpec[op].format) == JOF_PROP);
767 }
769 enum ArithCounts {
770 ARITH_INT = BASE_LIMIT,
771 ARITH_DOUBLE,
772 ARITH_OTHER,
773 ARITH_UNKNOWN,
775 ARITH_LIMIT
776 };
778 static bool arithOp(JSOp op) {
779 return !!(js_CodeSpec[op].format & JOF_ARITH);
780 }
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 }
796 static const char *countName(JSOp op, size_t which);
798 double *rawCounts() const { return counts; }
800 double& get(size_t which) {
801 JS_ASSERT(which < capacity);
802 return counts[which];
803 }
805 /* Boolean conversion, for 'if (counters) ...' */
806 operator void*() const {
807 return counts;
808 }
809 };
811 /* Necessary for alignment with the script. */
812 JS_STATIC_ASSERT(sizeof(PCCounts) % sizeof(Value) == 0);
814 static inline jsbytecode *
815 GetNextPc(jsbytecode *pc)
816 {
817 return pc + GetBytecodeLength(pc);
818 }
820 } /* namespace js */
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);
829 unsigned
830 js_Disassemble1(JSContext *cx, JS::Handle<JSScript*> script, jsbytecode *pc, unsigned loc,
831 bool lines, js::Sprinter *sp);
833 #endif
835 void
836 js_DumpPCCounts(JSContext *cx, JS::Handle<JSScript*> script, js::Sprinter *sp);
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
846 #endif /* jsopcode_h */