js/src/jsopcode.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial