Line data Source code
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 "mozilla/Attributes.h"
15 :
16 : #include "jsbytecode.h"
17 : #include "jstypes.h"
18 : #include "NamespaceImports.h"
19 :
20 : #include "frontend/SourceNotes.h"
21 : #include "js/UniquePtr.h"
22 : #include "vm/Opcodes.h"
23 : #include "vm/Printer.h"
24 :
25 : /*
26 : * JS operation bytecodes.
27 : */
28 : typedef enum JSOp {
29 : #define ENUMERATE_OPCODE(op, val, ...) op = val,
30 : FOR_EACH_OPCODE(ENUMERATE_OPCODE)
31 : #undef ENUMERATE_OPCODE
32 :
33 : JSOP_LIMIT
34 : } JSOp;
35 :
36 : /*
37 : * JS bytecode formats.
38 : */
39 : enum {
40 : JOF_BYTE = 0, /* single bytecode, no immediates */
41 : JOF_JUMP = 1, /* signed 16-bit jump offset immediate */
42 : JOF_ATOM = 2, /* unsigned 16-bit constant index */
43 : JOF_UINT16 = 3, /* unsigned 16-bit immediate operand */
44 : JOF_TABLESWITCH = 4, /* table switch */
45 : /* 5 is unused */
46 : JOF_QARG = 6, /* quickened get/set function argument ops */
47 : JOF_LOCAL = 7, /* var or block-local variable */
48 : JOF_DOUBLE = 8, /* uint32_t index for double value */
49 : JOF_UINT24 = 12, /* extended unsigned 24-bit literal (index) */
50 : JOF_UINT8 = 13, /* uint8_t immediate, e.g. top 8 bits of 24-bit
51 : atom index */
52 : JOF_INT32 = 14, /* int32_t immediate operand */
53 : JOF_UINT32 = 15, /* uint32_t immediate operand */
54 : JOF_OBJECT = 16, /* unsigned 32-bit object index */
55 : JOF_REGEXP = 17, /* unsigned 32-bit regexp index */
56 : JOF_INT8 = 18, /* int8_t immediate operand */
57 : JOF_ATOMOBJECT = 19, /* uint16_t constant index + object index */
58 : JOF_SCOPE = 20, /* unsigned 32-bit scope index */
59 : JOF_ENVCOORD = 21, /* embedded ScopeCoordinate immediate */
60 : JOF_TYPEMASK = 0x001f, /* mask for above immediate types */
61 :
62 : JOF_NAME = 1 << 5, /* name operation */
63 : JOF_PROP = 2 << 5, /* obj.prop operation */
64 : JOF_ELEM = 3 << 5, /* obj[index] operation */
65 : JOF_MODEMASK = 3 << 5, /* mask for above addressing modes */
66 : JOF_PROPSET = 1 << 7, /* property/element/name set operation */
67 : JOF_PROPINIT = 1 << 8, /* property/element/name init operation */
68 : /* 1 << 9 is unused */
69 : /* 1 << 10 is unused */
70 : /* 1 << 11 is unused */
71 : /* 1 << 12 is unused */
72 : /* 1 << 13 is unused */
73 : JOF_DETECTING = 1 << 14, /* object detection for warning-quelling */
74 : /* 1 << 15 is unused */
75 : JOF_LEFTASSOC = 1 << 16, /* left-associative operator */
76 : /* 1 << 17 is unused */
77 : /* 1 << 18 is unused */
78 : JOF_CHECKSLOPPY = 1 << 19, /* Op can only be generated in sloppy mode */
79 : JOF_CHECKSTRICT = 1 << 20, /* Op can only be generated in strict mode */
80 : JOF_INVOKE = 1 << 21, /* JSOP_CALL, JSOP_FUNCALL, JSOP_FUNAPPLY,
81 : JSOP_NEW, JSOP_EVAL, JSOP_CALLITER */
82 : /* 1 << 22 is unused */
83 : /* 1 << 23 is unused */
84 : /* 1 << 24 is unused */
85 : JOF_GNAME = 1 << 25, /* predicted global name */
86 : JOF_TYPESET = 1 << 26, /* has an entry in a script's type sets */
87 : JOF_ARITH = 1 << 27 /* unary or binary arithmetic opcode */
88 : };
89 :
90 : /* Shorthand for type from format. */
91 :
92 : static inline uint32_t
93 783870 : JOF_TYPE(uint32_t fmt)
94 : {
95 783870 : return fmt & JOF_TYPEMASK;
96 : }
97 :
98 : /* Shorthand for mode from format. */
99 :
100 : static inline uint32_t
101 : JOF_MODE(uint32_t fmt)
102 : {
103 : return fmt & JOF_MODEMASK;
104 : }
105 :
106 : /*
107 : * Immediate operand getters, setters, and bounds.
108 : */
109 :
110 : static MOZ_ALWAYS_INLINE uint8_t
111 19720 : GET_UINT8(jsbytecode* pc)
112 : {
113 19720 : return uint8_t(pc[1]);
114 : }
115 :
116 : static MOZ_ALWAYS_INLINE void
117 8054 : SET_UINT8(jsbytecode* pc, uint8_t u)
118 : {
119 8054 : pc[1] = jsbytecode(u);
120 8054 : }
121 :
122 : /* Common uint16_t immediate format helpers. */
123 :
124 : static inline jsbytecode
125 38739 : UINT16_HI(uint16_t i)
126 : {
127 38739 : return jsbytecode(i >> 8);
128 : }
129 :
130 : static inline jsbytecode
131 38739 : UINT16_LO(uint16_t i)
132 : {
133 38739 : return jsbytecode(i);
134 : }
135 :
136 : static MOZ_ALWAYS_INLINE uint16_t
137 128995 : GET_UINT16(const jsbytecode* pc)
138 : {
139 128995 : return uint16_t((pc[1] << 8) | pc[2]);
140 : }
141 :
142 : static MOZ_ALWAYS_INLINE void
143 14802 : SET_UINT16(jsbytecode* pc, uint16_t i)
144 : {
145 14802 : pc[1] = UINT16_HI(i);
146 14802 : pc[2] = UINT16_LO(i);
147 14802 : }
148 :
149 : static const unsigned UINT16_LEN = 2;
150 : static const unsigned UINT16_LIMIT = 1 << 16;
151 :
152 : /* Helpers for accessing the offsets of jump opcodes. */
153 : static const unsigned JUMP_OFFSET_LEN = 4;
154 : static const int32_t JUMP_OFFSET_MIN = INT32_MIN;
155 : static const int32_t JUMP_OFFSET_MAX = INT32_MAX;
156 :
157 : static MOZ_ALWAYS_INLINE int32_t
158 80574 : GET_JUMP_OFFSET(jsbytecode* pc)
159 : {
160 80574 : return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4];
161 : }
162 :
163 : static MOZ_ALWAYS_INLINE void
164 40054 : SET_JUMP_OFFSET(jsbytecode* pc, int32_t off)
165 : {
166 40054 : pc[1] = jsbytecode(off >> 24);
167 40054 : pc[2] = jsbytecode(off >> 16);
168 40054 : pc[3] = jsbytecode(off >> 8);
169 40054 : pc[4] = jsbytecode(off);
170 40054 : }
171 :
172 : static const unsigned UINT32_INDEX_LEN = 4;
173 :
174 : static MOZ_ALWAYS_INLINE uint32_t
175 145332 : GET_UINT32_INDEX(const jsbytecode* pc)
176 : {
177 145332 : return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4];
178 : }
179 :
180 : static MOZ_ALWAYS_INLINE void
181 82703 : SET_UINT32_INDEX(jsbytecode* pc, uint32_t index)
182 : {
183 82703 : pc[1] = jsbytecode(index >> 24);
184 82703 : pc[2] = jsbytecode(index >> 16);
185 82703 : pc[3] = jsbytecode(index >> 8);
186 82703 : pc[4] = jsbytecode(index);
187 82703 : }
188 :
189 : static inline jsbytecode
190 57930 : UINT24_HI(unsigned i)
191 : {
192 57930 : return jsbytecode(i >> 16);
193 : }
194 :
195 : static inline jsbytecode
196 57930 : UINT24_MID(unsigned i)
197 : {
198 57930 : return jsbytecode(i >> 8);
199 : }
200 :
201 : static inline jsbytecode
202 57930 : UINT24_LO(unsigned i)
203 : {
204 57930 : return jsbytecode(i);
205 : }
206 :
207 : static MOZ_ALWAYS_INLINE unsigned
208 98425 : GET_UINT24(const jsbytecode* pc)
209 : {
210 98425 : return unsigned((pc[1] << 16) | (pc[2] << 8) | pc[3]);
211 : }
212 :
213 : static MOZ_ALWAYS_INLINE void
214 57930 : SET_UINT24(jsbytecode* pc, unsigned i)
215 : {
216 57930 : MOZ_ASSERT(i < (1 << 24));
217 57930 : pc[1] = UINT24_HI(i);
218 57930 : pc[2] = UINT24_MID(i);
219 57930 : pc[3] = UINT24_LO(i);
220 57930 : }
221 :
222 : static MOZ_ALWAYS_INLINE int8_t
223 3549 : GET_INT8(const jsbytecode* pc)
224 : {
225 3549 : return int8_t(pc[1]);
226 : }
227 :
228 : static MOZ_ALWAYS_INLINE uint32_t
229 2729 : GET_UINT32(const jsbytecode* pc)
230 : {
231 5458 : return (uint32_t(pc[1]) << 24) |
232 5458 : (uint32_t(pc[2]) << 16) |
233 2729 : (uint32_t(pc[3]) << 8) |
234 2729 : uint32_t(pc[4]);
235 : }
236 :
237 : static MOZ_ALWAYS_INLINE void
238 2168 : SET_UINT32(jsbytecode* pc, uint32_t u)
239 : {
240 2168 : pc[1] = jsbytecode(u >> 24);
241 2168 : pc[2] = jsbytecode(u >> 16);
242 2168 : pc[3] = jsbytecode(u >> 8);
243 2168 : pc[4] = jsbytecode(u);
244 2168 : }
245 :
246 : static MOZ_ALWAYS_INLINE int32_t
247 30 : GET_INT32(const jsbytecode* pc)
248 : {
249 30 : return static_cast<int32_t>(GET_UINT32(pc));
250 : }
251 :
252 : static MOZ_ALWAYS_INLINE void
253 44 : SET_INT32(jsbytecode* pc, int32_t i)
254 : {
255 44 : SET_UINT32(pc, static_cast<uint32_t>(i));
256 44 : }
257 :
258 : /* Index limit is determined by SN_4BYTE_OFFSET_FLAG, see frontend/BytecodeEmitter.h. */
259 : static const unsigned INDEX_LIMIT_LOG2 = 31;
260 : static const uint32_t INDEX_LIMIT = uint32_t(1) << INDEX_LIMIT_LOG2;
261 :
262 : static inline jsbytecode
263 23429 : ARGC_HI(uint16_t argc)
264 : {
265 23429 : return UINT16_HI(argc);
266 : }
267 :
268 : static inline jsbytecode
269 23429 : ARGC_LO(uint16_t argc)
270 : {
271 23429 : return UINT16_LO(argc);
272 : }
273 :
274 : static inline uint16_t
275 103644 : GET_ARGC(const jsbytecode* pc)
276 : {
277 103644 : return GET_UINT16(pc);
278 : }
279 :
280 : static const unsigned ARGC_LIMIT = UINT16_LIMIT;
281 :
282 : static inline uint16_t
283 24842 : GET_ARGNO(const jsbytecode* pc)
284 : {
285 24842 : return GET_UINT16(pc);
286 : }
287 :
288 : static inline void
289 14802 : SET_ARGNO(jsbytecode* pc, uint16_t argno)
290 : {
291 14802 : SET_UINT16(pc, argno);
292 14802 : }
293 :
294 : static const unsigned ARGNO_LEN = 2;
295 : static const unsigned ARGNO_LIMIT = UINT16_LIMIT;
296 :
297 : static inline uint32_t
298 81768 : GET_LOCALNO(const jsbytecode* pc)
299 : {
300 81768 : return GET_UINT24(pc);
301 : }
302 :
303 : static inline void
304 47040 : SET_LOCALNO(jsbytecode* pc, uint32_t varno)
305 : {
306 47040 : SET_UINT24(pc, varno);
307 47040 : }
308 :
309 : static const unsigned LOCALNO_LEN = 3;
310 : static const unsigned LOCALNO_BITS = 24;
311 : static const uint32_t LOCALNO_LIMIT = 1 << LOCALNO_BITS;
312 :
313 : static inline unsigned
314 250 : LoopEntryDepthHint(jsbytecode* pc)
315 : {
316 250 : MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
317 250 : return GET_UINT8(pc) & 0x7f;
318 : }
319 :
320 : static inline bool
321 420 : LoopEntryCanIonOsr(jsbytecode* pc)
322 : {
323 420 : MOZ_ASSERT(*pc == JSOP_LOOPENTRY);
324 420 : return GET_UINT8(pc) & 0x80;
325 : }
326 :
327 : static inline uint8_t
328 1074 : PackLoopEntryDepthHintAndFlags(unsigned loopDepth, bool canIonOsr)
329 : {
330 1074 : return (loopDepth < 0x80 ? uint8_t(loopDepth) : 0x7f) | (canIonOsr ? 0x80 : 0);
331 : }
332 :
333 : /*
334 : * Describes the 'hops' component of a JOF_ENVCOORD opcode.
335 : *
336 : * Note: this component is only 8 bits wide, limiting the maximum number of
337 : * scopes between a use and def to roughly 255. This is a pretty small limit but
338 : * note that SpiderMonkey's recursive descent parser can only parse about this
339 : * many functions before hitting the C-stack recursion limit so this shouldn't
340 : * be a significant limitation in practice.
341 : */
342 :
343 : static inline uint8_t
344 13299 : GET_ENVCOORD_HOPS(jsbytecode* pc)
345 : {
346 13299 : return GET_UINT8(pc);
347 : }
348 :
349 : static inline void
350 8054 : SET_ENVCOORD_HOPS(jsbytecode* pc, uint8_t hops)
351 : {
352 8054 : SET_UINT8(pc, hops);
353 8054 : }
354 :
355 : static const unsigned ENVCOORD_HOPS_LEN = 1;
356 : static const unsigned ENVCOORD_HOPS_BITS = 8;
357 : static const unsigned ENVCOORD_HOPS_LIMIT = 1 << ENVCOORD_HOPS_BITS;
358 :
359 : /* Describes the 'slot' component of a JOF_ENVCOORD opcode. */
360 : static inline uint32_t
361 13299 : GET_ENVCOORD_SLOT(const jsbytecode* pc)
362 : {
363 13299 : return GET_UINT24(pc);
364 : }
365 :
366 : static inline void
367 8054 : SET_ENVCOORD_SLOT(jsbytecode* pc, uint32_t slot)
368 : {
369 8054 : SET_UINT24(pc, slot);
370 8054 : }
371 :
372 : static const unsigned ENVCOORD_SLOT_LEN = 3;
373 : static const unsigned ENVCOORD_SLOT_BITS = 24;
374 : static const uint32_t ENVCOORD_SLOT_LIMIT = 1 << ENVCOORD_SLOT_BITS;
375 :
376 : struct JSCodeSpec {
377 : int8_t length; /* length including opcode byte */
378 : int8_t nuses; /* arity, -1 if variadic */
379 : int8_t ndefs; /* number of stack results */
380 : uint32_t format; /* immediate operand format */
381 :
382 1098 : uint32_t type() const { return JOF_TYPE(format); }
383 : };
384 :
385 : /* Silence unreferenced formal parameter warnings */
386 : #ifdef _MSC_VER
387 : #pragma warning(push)
388 : #pragma warning(disable:4100)
389 : #endif
390 :
391 : namespace js {
392 :
393 : extern const JSCodeSpec CodeSpec[];
394 : extern const unsigned NumCodeSpecs;
395 : extern const char * const CodeName[];
396 :
397 : /* Shorthand for type from opcode. */
398 :
399 : static inline uint32_t
400 269051 : JOF_OPTYPE(JSOp op)
401 : {
402 269051 : return JOF_TYPE(CodeSpec[op].format);
403 : }
404 :
405 : static inline bool
406 513721 : IsJumpOpcode(JSOp op)
407 : {
408 513721 : uint32_t type = JOF_TYPE(CodeSpec[op].format);
409 :
410 : /*
411 : * LABEL opcodes have type JOF_JUMP but are no-ops, don't treat them as
412 : * jumps to avoid degrading precision.
413 : */
414 513721 : return type == JOF_JUMP && op != JSOP_LABEL;
415 : }
416 :
417 : static inline bool
418 143323 : BytecodeFallsThrough(JSOp op)
419 : {
420 143323 : switch (op) {
421 : case JSOP_GOTO:
422 : case JSOP_DEFAULT:
423 : case JSOP_RETURN:
424 : case JSOP_RETRVAL:
425 : case JSOP_FINALYIELDRVAL:
426 : case JSOP_THROW:
427 : case JSOP_THROWMSG:
428 : case JSOP_TABLESWITCH:
429 16801 : return false;
430 : case JSOP_GOSUB:
431 : /* These fall through indirectly, after executing a 'finally'. */
432 157 : return true;
433 : default:
434 126365 : return true;
435 : }
436 : }
437 :
438 : static inline bool
439 146635 : BytecodeIsJumpTarget(JSOp op)
440 : {
441 146635 : switch (op) {
442 : case JSOP_JUMPTARGET:
443 : case JSOP_LOOPHEAD:
444 : case JSOP_LOOPENTRY:
445 : case JSOP_ENDITER:
446 : case JSOP_TRY:
447 79569 : return true;
448 : default:
449 67066 : return false;
450 : }
451 : }
452 :
453 : class SrcNoteLineScanner
454 : {
455 : /* offset of the current JSOp in the bytecode */
456 : ptrdiff_t offset;
457 :
458 : /* next src note to process */
459 : jssrcnote* sn;
460 :
461 : /* line number of the current JSOp */
462 : uint32_t lineno;
463 :
464 : /*
465 : * Is the current op the first one after a line change directive? Note that
466 : * multiple ops may be "first" if a line directive is used to return to a
467 : * previous line (eg, with a for loop increment expression.)
468 : */
469 : bool lineHeader;
470 :
471 : public:
472 0 : SrcNoteLineScanner(jssrcnote* sn, uint32_t lineno)
473 0 : : offset(0), sn(sn), lineno(lineno)
474 : {
475 0 : }
476 :
477 : /*
478 : * This is called repeatedly with always-advancing relpc values. The src
479 : * notes are tuples of <PC offset from prev src note, type, args>. Scan
480 : * through, updating the lineno, until the next src note is for a later
481 : * bytecode.
482 : *
483 : * When looking at the desired PC offset ('relpc'), the op is first in that
484 : * line iff there is a SRC_SETLINE or SRC_NEWLINE src note for that exact
485 : * bytecode.
486 : *
487 : * Note that a single bytecode may have multiple line-modifying notes (even
488 : * though only one should ever be needed.)
489 : */
490 0 : void advanceTo(ptrdiff_t relpc) {
491 : // Must always advance! If the same or an earlier PC is erroneously
492 : // passed in, we will already be past the relevant src notes
493 0 : MOZ_ASSERT_IF(offset > 0, relpc > offset);
494 :
495 : // Next src note should be for after the current offset
496 0 : MOZ_ASSERT_IF(offset > 0, SN_IS_TERMINATOR(sn) || SN_DELTA(sn) > 0);
497 :
498 : // The first PC requested is always considered to be a line header
499 0 : lineHeader = (offset == 0);
500 :
501 0 : if (SN_IS_TERMINATOR(sn))
502 0 : return;
503 :
504 : ptrdiff_t nextOffset;
505 0 : while ((nextOffset = offset + SN_DELTA(sn)) <= relpc && !SN_IS_TERMINATOR(sn)) {
506 0 : offset = nextOffset;
507 0 : SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
508 0 : if (type == SRC_SETLINE || type == SRC_NEWLINE) {
509 0 : if (type == SRC_SETLINE)
510 0 : lineno = GetSrcNoteOffset(sn, 0);
511 : else
512 0 : lineno++;
513 :
514 0 : if (offset == relpc)
515 0 : lineHeader = true;
516 : }
517 :
518 0 : sn = SN_NEXT(sn);
519 : }
520 : }
521 :
522 0 : bool isLineHeader() const {
523 0 : return lineHeader;
524 : }
525 :
526 0 : uint32_t getLine() const { return lineno; }
527 : };
528 :
529 : extern unsigned
530 : StackUses(JSScript* script, jsbytecode* pc);
531 :
532 : extern unsigned
533 : StackDefs(JSScript* script, jsbytecode* pc);
534 :
535 : #ifdef DEBUG
536 : /*
537 : * Given bytecode address pc in script's main program code, compute the operand
538 : * stack depth just before (JSOp) *pc executes. If *pc is not reachable, return
539 : * false.
540 : */
541 : extern bool
542 : ReconstructStackDepth(JSContext* cx, JSScript* script, jsbytecode* pc, uint32_t* depth, bool* reachablePC);
543 : #endif
544 :
545 : } /* namespace js */
546 :
547 : #ifdef _MSC_VER
548 : #pragma warning(pop)
549 : #endif
550 :
551 : #define JSDVG_IGNORE_STACK 0
552 : #define JSDVG_SEARCH_STACK 1
553 :
554 : namespace js {
555 :
556 : /*
557 : * Get the length of variable-length bytecode like JSOP_TABLESWITCH.
558 : */
559 : extern size_t
560 : GetVariableBytecodeLength(jsbytecode* pc);
561 :
562 : /*
563 : * Find the source expression that resulted in v, and return a newly allocated
564 : * C-string containing it. Fall back on v's string conversion (fallback) if we
565 : * can't find the bytecode that generated and pushed v on the operand stack.
566 : *
567 : * Search the current stack frame if spindex is JSDVG_SEARCH_STACK. Don't
568 : * look for v on the stack if spindex is JSDVG_IGNORE_STACK. Otherwise,
569 : * spindex is the negative index of v, measured from cx->fp->sp, or from a
570 : * lower frame's sp if cx->fp is native.
571 : *
572 : * The optional argument skipStackHits can be used to skip a hit in the stack
573 : * frame. This can be useful in self-hosted code that wants to report value
574 : * errors containing decompiled values that are useful for the user, instead of
575 : * values used internally by the self-hosted code.
576 : *
577 : * The caller must call JS_free on the result after a successful call.
578 : */
579 : UniqueChars
580 : DecompileValueGenerator(JSContext* cx, int spindex, HandleValue v,
581 : HandleString fallback, int skipStackHits = 0);
582 :
583 : /*
584 : * Decompile the formal argument at formalIndex in the nearest non-builtin
585 : * stack frame, falling back with converting v to source.
586 : */
587 : char*
588 : DecompileArgument(JSContext* cx, int formalIndex, HandleValue v);
589 :
590 : extern bool
591 : CallResultEscapes(jsbytecode* pc);
592 :
593 : static inline unsigned
594 : GetDecomposeLength(jsbytecode* pc, size_t len)
595 : {
596 : /*
597 : * The last byte of a DECOMPOSE op stores the decomposed length. This is a
598 : * constant: perhaps we should just hardcode values instead?
599 : */
600 : MOZ_ASSERT(size_t(CodeSpec[*pc].length) == len);
601 : return (unsigned) pc[len - 1];
602 : }
603 :
604 : static inline unsigned
605 1262593 : GetBytecodeLength(jsbytecode* pc)
606 : {
607 1262593 : JSOp op = (JSOp)*pc;
608 1262593 : MOZ_ASSERT(op < JSOP_LIMIT);
609 :
610 1262593 : if (CodeSpec[op].length != -1)
611 1258625 : return CodeSpec[op].length;
612 3968 : return GetVariableBytecodeLength(pc);
613 : }
614 :
615 : static inline bool
616 3742 : BytecodeIsPopped(jsbytecode* pc)
617 : {
618 3742 : jsbytecode* next = pc + GetBytecodeLength(pc);
619 3742 : return JSOp(*next) == JSOP_POP;
620 : }
621 :
622 : static inline bool
623 1067 : BytecodeFlowsToBitop(jsbytecode* pc)
624 : {
625 : // Look for simple bytecode for integer conversions like (x | 0) or (x & -1).
626 1067 : jsbytecode* next = pc + GetBytecodeLength(pc);
627 1067 : if (*next == JSOP_BITOR || *next == JSOP_BITAND)
628 0 : return true;
629 1067 : if (*next == JSOP_INT8 && GET_INT8(next) == -1) {
630 0 : next += GetBytecodeLength(next);
631 0 : if (*next == JSOP_BITAND)
632 0 : return true;
633 0 : return false;
634 : }
635 1067 : if (*next == JSOP_ONE) {
636 0 : next += GetBytecodeLength(next);
637 0 : if (*next == JSOP_NEG) {
638 0 : next += GetBytecodeLength(next);
639 0 : if (*next == JSOP_BITAND)
640 0 : return true;
641 : }
642 0 : return false;
643 : }
644 1067 : if (*next == JSOP_ZERO) {
645 14 : next += GetBytecodeLength(next);
646 14 : if (*next == JSOP_BITOR)
647 0 : return true;
648 14 : return false;
649 : }
650 1053 : return false;
651 : }
652 :
653 : extern bool
654 : IsValidBytecodeOffset(JSContext* cx, JSScript* script, size_t offset);
655 :
656 : inline bool
657 0 : FlowsIntoNext(JSOp op)
658 : {
659 : // JSOP_YIELD/JSOP_AWAIT is considered to flow into the next instruction,
660 : // like JSOP_CALL.
661 0 : switch (op) {
662 : case JSOP_RETRVAL:
663 : case JSOP_RETURN:
664 : case JSOP_THROW:
665 : case JSOP_GOTO:
666 : case JSOP_RETSUB:
667 : case JSOP_FINALYIELDRVAL:
668 0 : return false;
669 : default:
670 0 : return true;
671 : }
672 : }
673 :
674 : inline bool
675 38739 : IsArgOp(JSOp op)
676 : {
677 38739 : return JOF_OPTYPE(op) == JOF_QARG;
678 : }
679 :
680 : inline bool
681 71023 : IsLocalOp(JSOp op)
682 : {
683 71023 : return JOF_OPTYPE(op) == JOF_LOCAL;
684 : }
685 :
686 : inline bool
687 0 : IsAliasedVarOp(JSOp op)
688 : {
689 0 : return JOF_OPTYPE(op) == JOF_ENVCOORD;
690 : }
691 :
692 : inline bool
693 16530 : IsGlobalOp(JSOp op)
694 : {
695 16530 : return CodeSpec[op].format & JOF_GNAME;
696 : }
697 :
698 : inline bool
699 1970 : IsPropertySetOp(JSOp op)
700 : {
701 1970 : return CodeSpec[op].format & JOF_PROPSET;
702 : }
703 :
704 : inline bool
705 166 : IsPropertyInitOp(JSOp op)
706 : {
707 166 : return CodeSpec[op].format & JOF_PROPINIT;
708 : }
709 :
710 : inline bool
711 3868 : IsEqualityOp(JSOp op)
712 : {
713 3868 : return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
714 : }
715 :
716 : inline bool
717 367346 : IsCheckStrictOp(JSOp op)
718 : {
719 367346 : return CodeSpec[op].format & JOF_CHECKSTRICT;
720 : }
721 :
722 : #ifdef DEBUG
723 : inline bool
724 367346 : IsCheckSloppyOp(JSOp op)
725 : {
726 367346 : return CodeSpec[op].format & JOF_CHECKSLOPPY;
727 : }
728 : #endif
729 :
730 : inline bool
731 0 : IsAtomOp(JSOp op)
732 : {
733 0 : return JOF_OPTYPE(op) == JOF_ATOM;
734 : }
735 :
736 : inline bool
737 0 : IsGetPropPC(jsbytecode* pc)
738 : {
739 0 : JSOp op = JSOp(*pc);
740 0 : return op == JSOP_LENGTH || op == JSOP_GETPROP || op == JSOP_CALLPROP;
741 : }
742 :
743 : inline bool
744 5633 : IsHiddenInitOp(JSOp op)
745 : {
746 5633 : return op == JSOP_INITHIDDENPROP || op == JSOP_INITHIDDENELEM ||
747 5609 : op == JSOP_INITHIDDENPROP_GETTER || op == JSOP_INITHIDDENELEM_GETTER ||
748 11241 : op == JSOP_INITHIDDENPROP_SETTER || op == JSOP_INITHIDDENELEM_SETTER;
749 : }
750 :
751 : inline bool
752 56 : IsStrictSetPC(jsbytecode* pc)
753 : {
754 56 : JSOp op = JSOp(*pc);
755 15 : return op == JSOP_STRICTSETPROP ||
756 15 : op == JSOP_STRICTSETNAME ||
757 68 : op == JSOP_STRICTSETGNAME ||
758 56 : op == JSOP_STRICTSETELEM;
759 : }
760 :
761 : inline bool
762 0 : IsSetPropPC(jsbytecode* pc)
763 : {
764 0 : JSOp op = JSOp(*pc);
765 0 : return op == JSOP_SETPROP || op == JSOP_STRICTSETPROP ||
766 0 : op == JSOP_SETNAME || op == JSOP_STRICTSETNAME ||
767 0 : op == JSOP_SETGNAME || op == JSOP_STRICTSETGNAME;
768 : }
769 :
770 : inline bool
771 : IsGetElemPC(jsbytecode* pc)
772 : {
773 : JSOp op = JSOp(*pc);
774 : return op == JSOP_GETELEM || op == JSOP_CALLELEM;
775 : }
776 :
777 : inline bool
778 42 : IsSetElemPC(jsbytecode* pc)
779 : {
780 42 : JSOp op = JSOp(*pc);
781 42 : return op == JSOP_SETELEM ||
782 42 : op == JSOP_STRICTSETELEM;
783 : }
784 :
785 : inline bool
786 6 : IsElemPC(jsbytecode* pc)
787 : {
788 6 : return CodeSpec[*pc].format & JOF_ELEM;
789 : }
790 :
791 : inline bool
792 4407 : IsCallPC(jsbytecode* pc)
793 : {
794 4407 : return CodeSpec[*pc].format & JOF_INVOKE;
795 : }
796 :
797 : inline bool
798 2 : IsStrictEvalPC(jsbytecode* pc)
799 : {
800 2 : JSOp op = JSOp(*pc);
801 2 : return op == JSOP_STRICTEVAL || op == JSOP_STRICTSPREADEVAL;
802 : }
803 :
804 : inline bool
805 0 : IsConstructorCallPC(jsbytecode* pc)
806 : {
807 0 : JSOp op = JSOp(*pc);
808 0 : return op == JSOP_NEW ||
809 0 : op == JSOP_SUPERCALL ||
810 0 : op == JSOP_SPREADNEW ||
811 0 : op == JSOP_SPREADSUPERCALL;
812 : }
813 :
814 : inline bool
815 37 : IsSpreadCallPC(jsbytecode* pc)
816 : {
817 37 : JSOp op = JSOp(*pc);
818 37 : return op == JSOP_SPREADCALL ||
819 37 : op == JSOP_SPREADNEW ||
820 37 : op == JSOP_SPREADSUPERCALL ||
821 74 : op == JSOP_SPREADEVAL ||
822 37 : op == JSOP_STRICTSPREADEVAL;
823 : }
824 :
825 : static inline int32_t
826 0 : GetBytecodeInteger(jsbytecode* pc)
827 : {
828 0 : switch (JSOp(*pc)) {
829 0 : case JSOP_ZERO: return 0;
830 0 : case JSOP_ONE: return 1;
831 0 : case JSOP_UINT16: return GET_UINT16(pc);
832 0 : case JSOP_UINT24: return GET_UINT24(pc);
833 0 : case JSOP_INT8: return GET_INT8(pc);
834 0 : case JSOP_INT32: return GET_INT32(pc);
835 : default:
836 0 : MOZ_CRASH("Bad op");
837 : }
838 : }
839 :
840 : /*
841 : * Counts accumulated for a single opcode in a script. The counts tracked vary
842 : * between opcodes, and this structure ensures that counts are accessed in a
843 : * coherent fashion.
844 : */
845 : class PCCounts
846 : {
847 : /*
848 : * Offset of the pc inside the script. This fields is used to lookup opcode
849 : * which have annotations.
850 : */
851 : size_t pcOffset_;
852 :
853 : /*
854 : * Record the number of execution of one instruction, or the number of
855 : * throws executed.
856 : */
857 : uint64_t numExec_;
858 :
859 : public:
860 20933 : explicit PCCounts(size_t off)
861 20933 : : pcOffset_(off),
862 20933 : numExec_(0)
863 20933 : {}
864 :
865 14535 : size_t pcOffset() const {
866 14535 : return pcOffset_;
867 : }
868 :
869 : // Used for sorting and searching.
870 51204 : bool operator<(const PCCounts& rhs) const {
871 51204 : return pcOffset_ < rhs.pcOffset_;
872 : }
873 :
874 10765 : uint64_t& numExec() {
875 10765 : return numExec_;
876 : }
877 920 : uint64_t numExec() const {
878 920 : return numExec_;
879 : }
880 :
881 : static const char* numExecName;
882 : };
883 :
884 : static inline jsbytecode*
885 464992 : GetNextPc(jsbytecode* pc)
886 : {
887 464992 : return pc + GetBytecodeLength(pc);
888 : }
889 :
890 : #if defined(DEBUG)
891 : /*
892 : * Disassemblers, for debugging only.
893 : */
894 : extern MOZ_MUST_USE bool
895 : Disassemble(JSContext* cx, JS::Handle<JSScript*> script, bool lines, Sprinter* sp);
896 :
897 : unsigned
898 : Disassemble1(JSContext* cx, JS::Handle<JSScript*> script, jsbytecode* pc, unsigned loc,
899 : bool lines, Sprinter* sp);
900 :
901 : #endif
902 :
903 : extern MOZ_MUST_USE bool
904 : DumpCompartmentPCCounts(JSContext* cx);
905 :
906 : } // namespace js
907 :
908 : #endif /* jsopcode_h */
|