js/src/vm/Stack.h

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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 vm_Stack_h
michael@0 8 #define vm_Stack_h
michael@0 9
michael@0 10 #include "mozilla/MemoryReporting.h"
michael@0 11
michael@0 12 #include "jsfun.h"
michael@0 13 #include "jsscript.h"
michael@0 14
michael@0 15 #include "jit/AsmJSLink.h"
michael@0 16 #include "jit/JitFrameIterator.h"
michael@0 17 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 18 #include "jit/Registers.h" // for RegisterDump
michael@0 19 #endif
michael@0 20 #include "js/OldDebugAPI.h"
michael@0 21
michael@0 22 struct JSCompartment;
michael@0 23 struct JSGenerator;
michael@0 24
michael@0 25 namespace js {
michael@0 26
michael@0 27 class ArgumentsObject;
michael@0 28 class AsmJSModule;
michael@0 29 class InterpreterRegs;
michael@0 30 class ScopeObject;
michael@0 31 class ScriptFrameIter;
michael@0 32 class SPSProfiler;
michael@0 33 class InterpreterFrame;
michael@0 34 class StaticBlockObject;
michael@0 35
michael@0 36 struct ScopeCoordinate;
michael@0 37
michael@0 38 // VM stack layout
michael@0 39 //
michael@0 40 // A JSRuntime's stack consists of a linked list of activations. Every activation
michael@0 41 // contains a number of scripted frames that are either running in the interpreter
michael@0 42 // (InterpreterActivation) or JIT code (JitActivation). The frames inside a single
michael@0 43 // activation are contiguous: whenever C++ calls back into JS, a new activation is
michael@0 44 // pushed.
michael@0 45 //
michael@0 46 // Every activation is tied to a single JSContext and JSCompartment. This means we
michael@0 47 // can reconstruct a given context's stack by skipping activations belonging to other
michael@0 48 // contexts. This happens whenever an embedding enters the JS engine on cx1 and
michael@0 49 // then, from a native called by the JS engine, reenters the VM on cx2.
michael@0 50
michael@0 51 // Interpreter frames (InterpreterFrame)
michael@0 52 //
michael@0 53 // Each interpreter script activation (global or function code) is given a
michael@0 54 // fixed-size header (js::InterpreterFrame). The frame contains bookkeeping
michael@0 55 // information about the activation and links to the previous frame.
michael@0 56 //
michael@0 57 // The values after an InterpreterFrame in memory are its locals followed by its
michael@0 58 // expression stack. InterpreterFrame::argv_ points to the frame's arguments.
michael@0 59 // Missing formal arguments are padded with |undefined|, so the number of
michael@0 60 // arguments is always >= the number of formals.
michael@0 61 //
michael@0 62 // The top of an activation's current frame's expression stack is pointed to by
michael@0 63 // the activation's "current regs", which contains the stack pointer 'sp'. In
michael@0 64 // the interpreter, sp is adjusted as individual values are pushed and popped
michael@0 65 // from the stack and the InterpreterRegs struct (pointed to by the
michael@0 66 // InterpreterActivation) is a local var of js::Interpret.
michael@0 67
michael@0 68 enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
michael@0 69
michael@0 70 /*****************************************************************************/
michael@0 71
michael@0 72 #ifdef DEBUG
michael@0 73 extern void
michael@0 74 CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i);
michael@0 75 #endif
michael@0 76
michael@0 77 namespace jit {
michael@0 78 class BaselineFrame;
michael@0 79 class RematerializedFrame;
michael@0 80 }
michael@0 81
michael@0 82 /*
michael@0 83 * Pointer to either a ScriptFrameIter::Data, an InterpreterFrame, or a Baseline
michael@0 84 * JIT frame.
michael@0 85 *
michael@0 86 * The Debugger may cache ScriptFrameIter::Data as a bookmark to reconstruct a
michael@0 87 * ScriptFrameIter without doing a full stack walk.
michael@0 88 *
michael@0 89 * There is no way to directly create such an AbstractFramePtr. To do so, the
michael@0 90 * user must call ScriptFrameIter::copyDataAsAbstractFramePtr().
michael@0 91 *
michael@0 92 * ScriptFrameIter::abstractFramePtr() will never return an AbstractFramePtr
michael@0 93 * that is in fact a ScriptFrameIter::Data.
michael@0 94 *
michael@0 95 * To recover a ScriptFrameIter settled at the location pointed to by an
michael@0 96 * AbstractFramePtr, use the THIS_FRAME_ITER macro in Debugger.cpp. As an
michael@0 97 * aside, no asScriptFrameIterData() is provided because C++ is stupid and
michael@0 98 * cannot forward declare inner classes.
michael@0 99 */
michael@0 100
michael@0 101 class AbstractFramePtr
michael@0 102 {
michael@0 103 friend class FrameIter;
michael@0 104
michael@0 105 uintptr_t ptr_;
michael@0 106
michael@0 107 enum {
michael@0 108 Tag_ScriptFrameIterData = 0x0,
michael@0 109 Tag_InterpreterFrame = 0x1,
michael@0 110 Tag_BaselineFrame = 0x2,
michael@0 111 Tag_RematerializedFrame = 0x3,
michael@0 112 TagMask = 0x3
michael@0 113 };
michael@0 114
michael@0 115 public:
michael@0 116 AbstractFramePtr()
michael@0 117 : ptr_(0)
michael@0 118 {}
michael@0 119
michael@0 120 AbstractFramePtr(InterpreterFrame *fp)
michael@0 121 : ptr_(fp ? uintptr_t(fp) | Tag_InterpreterFrame : 0)
michael@0 122 {
michael@0 123 MOZ_ASSERT_IF(fp, asInterpreterFrame() == fp);
michael@0 124 }
michael@0 125
michael@0 126 AbstractFramePtr(jit::BaselineFrame *fp)
michael@0 127 : ptr_(fp ? uintptr_t(fp) | Tag_BaselineFrame : 0)
michael@0 128 {
michael@0 129 MOZ_ASSERT_IF(fp, asBaselineFrame() == fp);
michael@0 130 }
michael@0 131
michael@0 132 AbstractFramePtr(jit::RematerializedFrame *fp)
michael@0 133 : ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0)
michael@0 134 {
michael@0 135 MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp);
michael@0 136 }
michael@0 137
michael@0 138 explicit AbstractFramePtr(JSAbstractFramePtr frame)
michael@0 139 : ptr_(uintptr_t(frame.raw()))
michael@0 140 {
michael@0 141 }
michael@0 142
michael@0 143 static AbstractFramePtr FromRaw(void *raw) {
michael@0 144 AbstractFramePtr frame;
michael@0 145 frame.ptr_ = uintptr_t(raw);
michael@0 146 return frame;
michael@0 147 }
michael@0 148
michael@0 149 bool isScriptFrameIterData() const {
michael@0 150 return !!ptr_ && (ptr_ & TagMask) == Tag_ScriptFrameIterData;
michael@0 151 }
michael@0 152 bool isInterpreterFrame() const {
michael@0 153 return (ptr_ & TagMask) == Tag_InterpreterFrame;
michael@0 154 }
michael@0 155 InterpreterFrame *asInterpreterFrame() const {
michael@0 156 JS_ASSERT(isInterpreterFrame());
michael@0 157 InterpreterFrame *res = (InterpreterFrame *)(ptr_ & ~TagMask);
michael@0 158 JS_ASSERT(res);
michael@0 159 return res;
michael@0 160 }
michael@0 161 bool isBaselineFrame() const {
michael@0 162 return (ptr_ & TagMask) == Tag_BaselineFrame;
michael@0 163 }
michael@0 164 jit::BaselineFrame *asBaselineFrame() const {
michael@0 165 JS_ASSERT(isBaselineFrame());
michael@0 166 jit::BaselineFrame *res = (jit::BaselineFrame *)(ptr_ & ~TagMask);
michael@0 167 JS_ASSERT(res);
michael@0 168 return res;
michael@0 169 }
michael@0 170 bool isRematerializedFrame() const {
michael@0 171 return (ptr_ & TagMask) == Tag_RematerializedFrame;
michael@0 172 }
michael@0 173 jit::RematerializedFrame *asRematerializedFrame() const {
michael@0 174 JS_ASSERT(isRematerializedFrame());
michael@0 175 jit::RematerializedFrame *res = (jit::RematerializedFrame *)(ptr_ & ~TagMask);
michael@0 176 JS_ASSERT(res);
michael@0 177 return res;
michael@0 178 }
michael@0 179
michael@0 180 void *raw() const { return reinterpret_cast<void *>(ptr_); }
michael@0 181
michael@0 182 bool operator ==(const AbstractFramePtr &other) const { return ptr_ == other.ptr_; }
michael@0 183 bool operator !=(const AbstractFramePtr &other) const { return ptr_ != other.ptr_; }
michael@0 184
michael@0 185 operator bool() const { return !!ptr_; }
michael@0 186
michael@0 187 inline JSObject *scopeChain() const;
michael@0 188 inline CallObject &callObj() const;
michael@0 189 inline bool initFunctionScopeObjects(JSContext *cx);
michael@0 190 inline void pushOnScopeChain(ScopeObject &scope);
michael@0 191
michael@0 192 inline JSCompartment *compartment() const;
michael@0 193
michael@0 194 inline bool hasCallObj() const;
michael@0 195 inline bool isGeneratorFrame() const;
michael@0 196 inline bool isYielding() const;
michael@0 197 inline bool isFunctionFrame() const;
michael@0 198 inline bool isGlobalFrame() const;
michael@0 199 inline bool isEvalFrame() const;
michael@0 200 inline bool isFramePushedByExecute() const;
michael@0 201 inline bool isDebuggerFrame() const;
michael@0 202
michael@0 203 inline JSScript *script() const;
michael@0 204 inline JSFunction *fun() const;
michael@0 205 inline JSFunction *maybeFun() const;
michael@0 206 inline JSFunction *callee() const;
michael@0 207 inline Value calleev() const;
michael@0 208 inline Value &thisValue() const;
michael@0 209
michael@0 210 inline bool isNonEvalFunctionFrame() const;
michael@0 211 inline bool isNonStrictDirectEvalFrame() const;
michael@0 212 inline bool isStrictEvalFrame() const;
michael@0 213
michael@0 214 inline unsigned numActualArgs() const;
michael@0 215 inline unsigned numFormalArgs() const;
michael@0 216
michael@0 217 inline Value *argv() const;
michael@0 218
michael@0 219 inline bool hasArgs() const;
michael@0 220 inline bool hasArgsObj() const;
michael@0 221 inline ArgumentsObject &argsObj() const;
michael@0 222 inline void initArgsObj(ArgumentsObject &argsobj) const;
michael@0 223 inline bool useNewType() const;
michael@0 224
michael@0 225 inline bool copyRawFrameSlots(AutoValueVector *vec) const;
michael@0 226
michael@0 227 inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
michael@0 228 inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
michael@0 229 inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
michael@0 230 inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
michael@0 231 template <class Op> inline void unaliasedForEachActual(JSContext *cx, Op op);
michael@0 232
michael@0 233 inline bool prevUpToDate() const;
michael@0 234 inline void setPrevUpToDate() const;
michael@0 235
michael@0 236 JSObject *evalPrevScopeChain(JSContext *cx) const;
michael@0 237
michael@0 238 inline void *maybeHookData() const;
michael@0 239 inline void setHookData(void *data) const;
michael@0 240 inline HandleValue returnValue() const;
michael@0 241 inline void setReturnValue(const Value &rval) const;
michael@0 242
michael@0 243 bool hasPushedSPSFrame() const;
michael@0 244
michael@0 245 inline void popBlock(JSContext *cx) const;
michael@0 246 inline void popWith(JSContext *cx) const;
michael@0 247 };
michael@0 248
michael@0 249 class NullFramePtr : public AbstractFramePtr
michael@0 250 {
michael@0 251 public:
michael@0 252 NullFramePtr()
michael@0 253 : AbstractFramePtr()
michael@0 254 { }
michael@0 255 };
michael@0 256
michael@0 257 /*****************************************************************************/
michael@0 258
michael@0 259 /* Flags specified for a frame as it is constructed. */
michael@0 260 enum InitialFrameFlags {
michael@0 261 INITIAL_NONE = 0,
michael@0 262 INITIAL_CONSTRUCT = 0x20, /* == InterpreterFrame::CONSTRUCTING, asserted below */
michael@0 263 };
michael@0 264
michael@0 265 enum ExecuteType {
michael@0 266 EXECUTE_GLOBAL = 0x1, /* == InterpreterFrame::GLOBAL */
michael@0 267 EXECUTE_DIRECT_EVAL = 0x4, /* == InterpreterFrame::EVAL */
michael@0 268 EXECUTE_INDIRECT_EVAL = 0x5, /* == InterpreterFrame::GLOBAL | EVAL */
michael@0 269 EXECUTE_DEBUG = 0xc, /* == InterpreterFrame::EVAL | DEBUGGER */
michael@0 270 EXECUTE_DEBUG_GLOBAL = 0xd /* == InterpreterFrame::EVAL | DEBUGGER | GLOBAL */
michael@0 271 };
michael@0 272
michael@0 273 /*****************************************************************************/
michael@0 274
michael@0 275 class InterpreterFrame
michael@0 276 {
michael@0 277 public:
michael@0 278 enum Flags {
michael@0 279 /* Primary frame type */
michael@0 280 GLOBAL = 0x1, /* frame pushed for a global script */
michael@0 281 FUNCTION = 0x2, /* frame pushed for a scripted call */
michael@0 282
michael@0 283 /* Frame subtypes */
michael@0 284 EVAL = 0x4, /* frame pushed for eval() or debugger eval */
michael@0 285
michael@0 286
michael@0 287 /*
michael@0 288 * Frame pushed for debugger eval.
michael@0 289 * - Don't bother to JIT it, because it's probably short-lived.
michael@0 290 * - It is required to have a scope chain object outside the
michael@0 291 * js::ScopeObject hierarchy: either a global object, or a
michael@0 292 * DebugScopeObject (not a ScopeObject, despite the name)
michael@0 293 * - If evalInFramePrev_ is set, then this frame was created for an
michael@0 294 * "eval in frame" call, which can push a successor to any live
michael@0 295 * frame; so its logical "prev" frame is not necessarily the
michael@0 296 * previous frame in memory. Iteration should treat
michael@0 297 * evalInFramePrev_ as this frame's previous frame.
michael@0 298 */
michael@0 299 DEBUGGER = 0x8,
michael@0 300
michael@0 301 GENERATOR = 0x10, /* frame is associated with a generator */
michael@0 302 CONSTRUCTING = 0x20, /* frame is for a constructor invocation */
michael@0 303
michael@0 304 /*
michael@0 305 * Generator frame state
michael@0 306 *
michael@0 307 * YIELDING and SUSPENDED are similar, but there are differences. After
michael@0 308 * a generator yields, SendToGenerator immediately clears the YIELDING
michael@0 309 * flag, but the frame will still have the SUSPENDED flag. Also, when the
michael@0 310 * generator returns but before it's GC'ed, YIELDING is not set but
michael@0 311 * SUSPENDED is.
michael@0 312 */
michael@0 313 YIELDING = 0x40, /* Interpret dispatched JSOP_YIELD */
michael@0 314 SUSPENDED = 0x80, /* Generator is not running. */
michael@0 315
michael@0 316 /* Function prologue state */
michael@0 317 HAS_CALL_OBJ = 0x100, /* CallObject created for heavyweight fun */
michael@0 318 HAS_ARGS_OBJ = 0x200, /* ArgumentsObject created for needsArgsObj script */
michael@0 319
michael@0 320 /* Lazy frame initialization */
michael@0 321 HAS_HOOK_DATA = 0x400, /* frame has hookData_ set */
michael@0 322 HAS_RVAL = 0x800, /* frame has rval_ set */
michael@0 323 HAS_SCOPECHAIN = 0x1000, /* frame has scopeChain_ set */
michael@0 324
michael@0 325 /* Debugger state */
michael@0 326 PREV_UP_TO_DATE = 0x4000, /* see DebugScopes::updateLiveScopes */
michael@0 327
michael@0 328 /* Used in tracking calls and profiling (see vm/SPSProfiler.cpp) */
michael@0 329 HAS_PUSHED_SPS_FRAME = 0x8000, /* SPS was notified of enty */
michael@0 330
michael@0 331 /*
michael@0 332 * If set, we entered one of the JITs and ScriptFrameIter should skip
michael@0 333 * this frame.
michael@0 334 */
michael@0 335 RUNNING_IN_JIT = 0x10000,
michael@0 336
michael@0 337 /* Miscellaneous state. */
michael@0 338 USE_NEW_TYPE = 0x20000 /* Use new type for constructed |this| object. */
michael@0 339 };
michael@0 340
michael@0 341 private:
michael@0 342 mutable uint32_t flags_; /* bits described by Flags */
michael@0 343 union { /* describes what code is executing in a */
michael@0 344 JSScript *script; /* global frame */
michael@0 345 JSFunction *fun; /* function frame, pre GetScopeChain */
michael@0 346 } exec;
michael@0 347 union { /* describes the arguments of a function */
michael@0 348 unsigned nactual; /* for non-eval frames */
michael@0 349 JSScript *evalScript; /* the script of an eval-in-function */
michael@0 350 } u;
michael@0 351 mutable JSObject *scopeChain_; /* if HAS_SCOPECHAIN, current scope chain */
michael@0 352 Value rval_; /* if HAS_RVAL, return value of the frame */
michael@0 353 ArgumentsObject *argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */
michael@0 354
michael@0 355 /*
michael@0 356 * Previous frame and its pc and sp. Always nullptr for
michael@0 357 * InterpreterActivation's entry frame, always non-nullptr for inline
michael@0 358 * frames.
michael@0 359 */
michael@0 360 InterpreterFrame *prev_;
michael@0 361 jsbytecode *prevpc_;
michael@0 362 Value *prevsp_;
michael@0 363
michael@0 364 void *hookData_; /* if HAS_HOOK_DATA, closure returned by call hook */
michael@0 365
michael@0 366 /*
michael@0 367 * For an eval-in-frame DEBUGGER frame, the frame in whose scope we're
michael@0 368 * evaluating code. Iteration treats this as our previous frame.
michael@0 369 */
michael@0 370 AbstractFramePtr evalInFramePrev_;
michael@0 371
michael@0 372 Value *argv_; /* If hasArgs(), points to frame's arguments. */
michael@0 373 LifoAlloc::Mark mark_; /* Used to release memory for this frame. */
michael@0 374
michael@0 375 static void staticAsserts() {
michael@0 376 JS_STATIC_ASSERT(offsetof(InterpreterFrame, rval_) % sizeof(Value) == 0);
michael@0 377 JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(Value) == 0);
michael@0 378 }
michael@0 379
michael@0 380 void writeBarrierPost();
michael@0 381
michael@0 382 /*
michael@0 383 * The utilities are private since they are not able to assert that only
michael@0 384 * unaliased vars/formals are accessed. Normal code should prefer the
michael@0 385 * InterpreterFrame::unaliased* members (or InterpreterRegs::stackDepth for
michael@0 386 * the usual "depth is at least" assertions).
michael@0 387 */
michael@0 388 Value *slots() const { return (Value *)(this + 1); }
michael@0 389 Value *base() const { return slots() + script()->nfixed(); }
michael@0 390
michael@0 391 friend class FrameIter;
michael@0 392 friend class InterpreterRegs;
michael@0 393 friend class InterpreterStack;
michael@0 394 friend class jit::BaselineFrame;
michael@0 395
michael@0 396 /*
michael@0 397 * Frame initialization, called by InterpreterStack operations after acquiring
michael@0 398 * the raw memory for the frame:
michael@0 399 */
michael@0 400
michael@0 401 /* Used for Invoke and Interpret. */
michael@0 402 void initCallFrame(JSContext *cx, InterpreterFrame *prev, jsbytecode *prevpc, Value *prevsp,
michael@0 403 JSFunction &callee, JSScript *script, Value *argv, uint32_t nactual,
michael@0 404 InterpreterFrame::Flags flags);
michael@0 405
michael@0 406 /* Used for global and eval frames. */
michael@0 407 void initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr prev,
michael@0 408 const Value &thisv, JSObject &scopeChain, ExecuteType type);
michael@0 409
michael@0 410 public:
michael@0 411 /*
michael@0 412 * Frame prologue/epilogue
michael@0 413 *
michael@0 414 * Every stack frame must have 'prologue' called before executing the
michael@0 415 * first op and 'epilogue' called after executing the last op and before
michael@0 416 * popping the frame (whether the exit is exceptional or not).
michael@0 417 *
michael@0 418 * For inline JS calls/returns, it is easy to call the prologue/epilogue
michael@0 419 * exactly once. When calling JS from C++, Invoke/Execute push the stack
michael@0 420 * frame but do *not* call the prologue/epilogue. That means Interpret
michael@0 421 * must call the prologue/epilogue for the entry frame. This scheme
michael@0 422 * simplifies jit compilation.
michael@0 423 *
michael@0 424 * An important corner case is what happens when an error occurs (OOM,
michael@0 425 * over-recursed) after pushing the stack frame but before 'prologue' is
michael@0 426 * called or completes fully. To simplify usage, 'epilogue' does not assume
michael@0 427 * 'prologue' has completed and handles all the intermediate state details.
michael@0 428 */
michael@0 429
michael@0 430 bool prologue(JSContext *cx);
michael@0 431 void epilogue(JSContext *cx);
michael@0 432
michael@0 433 bool initFunctionScopeObjects(JSContext *cx);
michael@0 434
michael@0 435 /* Initialize local variables of newly-pushed frame. */
michael@0 436 void initVarsToUndefined();
michael@0 437
michael@0 438 /*
michael@0 439 * Stack frame type
michael@0 440 *
michael@0 441 * A stack frame may have one of three types, which determines which
michael@0 442 * members of the frame may be accessed and other invariants:
michael@0 443 *
michael@0 444 * global frame: execution of global code or an eval in global code
michael@0 445 * function frame: execution of function code or an eval in a function
michael@0 446 */
michael@0 447
michael@0 448 bool isFunctionFrame() const {
michael@0 449 return !!(flags_ & FUNCTION);
michael@0 450 }
michael@0 451
michael@0 452 bool isGlobalFrame() const {
michael@0 453 return !!(flags_ & GLOBAL);
michael@0 454 }
michael@0 455
michael@0 456 /*
michael@0 457 * Eval frames
michael@0 458 *
michael@0 459 * As noted above, global and function frames may optionally be 'eval
michael@0 460 * frames'. Eval code shares its parent's arguments which means that the
michael@0 461 * arg-access members of InterpreterFrame may not be used for eval frames.
michael@0 462 * Search for 'hasArgs' below for more details.
michael@0 463 *
michael@0 464 * A further sub-classification of eval frames is whether the frame was
michael@0 465 * pushed for an ES5 strict-mode eval().
michael@0 466 */
michael@0 467
michael@0 468 bool isEvalFrame() const {
michael@0 469 return flags_ & EVAL;
michael@0 470 }
michael@0 471
michael@0 472 bool isEvalInFunction() const {
michael@0 473 return (flags_ & (EVAL | FUNCTION)) == (EVAL | FUNCTION);
michael@0 474 }
michael@0 475
michael@0 476 bool isNonEvalFunctionFrame() const {
michael@0 477 return (flags_ & (FUNCTION | EVAL)) == FUNCTION;
michael@0 478 }
michael@0 479
michael@0 480 inline bool isStrictEvalFrame() const {
michael@0 481 return isEvalFrame() && script()->strict();
michael@0 482 }
michael@0 483
michael@0 484 bool isNonStrictEvalFrame() const {
michael@0 485 return isEvalFrame() && !script()->strict();
michael@0 486 }
michael@0 487
michael@0 488 bool isDirectEvalFrame() const {
michael@0 489 return isEvalFrame() && script()->staticLevel() > 0;
michael@0 490 }
michael@0 491
michael@0 492 bool isNonStrictDirectEvalFrame() const {
michael@0 493 return isNonStrictEvalFrame() && isDirectEvalFrame();
michael@0 494 }
michael@0 495
michael@0 496 /*
michael@0 497 * Previous frame
michael@0 498 *
michael@0 499 * A frame's 'prev' frame is either null or the previous frame pointed to
michael@0 500 * by cx->regs->fp when this frame was pushed. Often, given two prev-linked
michael@0 501 * frames, the next-frame is a function or eval that was called by the
michael@0 502 * prev-frame, but not always: the prev-frame may have called a native that
michael@0 503 * reentered the VM through JS_CallFunctionValue on the same context
michael@0 504 * (without calling JS_SaveFrameChain) which pushed the next-frame. Thus,
michael@0 505 * 'prev' has little semantic meaning and basically just tells the VM what
michael@0 506 * to set cx->regs->fp to when this frame is popped.
michael@0 507 */
michael@0 508
michael@0 509 InterpreterFrame *prev() const {
michael@0 510 return prev_;
michael@0 511 }
michael@0 512
michael@0 513 AbstractFramePtr evalInFramePrev() const {
michael@0 514 JS_ASSERT(isEvalFrame());
michael@0 515 return evalInFramePrev_;
michael@0 516 }
michael@0 517
michael@0 518 /*
michael@0 519 * (Unaliased) locals and arguments
michael@0 520 *
michael@0 521 * Only non-eval function frames have arguments. The arguments pushed by
michael@0 522 * the caller are the 'actual' arguments. The declared arguments of the
michael@0 523 * callee are the 'formal' arguments. When the caller passes less actual
michael@0 524 * arguments, missing formal arguments are padded with |undefined|.
michael@0 525 *
michael@0 526 * When a local/formal variable is "aliased" (accessed by nested closures,
michael@0 527 * dynamic scope operations, or 'arguments), the canonical location for
michael@0 528 * that value is the slot of an activation object (scope or arguments).
michael@0 529 * Currently, all variables are given slots in *both* the stack frame and
michael@0 530 * heap objects, even though, as just described, only one should ever be
michael@0 531 * accessed. Thus, it is up to the code performing an access to access the
michael@0 532 * correct value. These functions assert that accesses to stack values are
michael@0 533 * unaliased. For more about canonical values locations.
michael@0 534 */
michael@0 535
michael@0 536 inline Value &unaliasedVar(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING);
michael@0 537 inline Value &unaliasedLocal(uint32_t i, MaybeCheckAliasing = CHECK_ALIASING);
michael@0 538
michael@0 539 bool hasArgs() const { return isNonEvalFunctionFrame(); }
michael@0 540 inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING);
michael@0 541 inline Value &unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING);
michael@0 542 template <class Op> inline void unaliasedForEachActual(Op op);
michael@0 543
michael@0 544 bool copyRawFrameSlots(AutoValueVector *v);
michael@0 545
michael@0 546 unsigned numFormalArgs() const { JS_ASSERT(hasArgs()); return fun()->nargs(); }
michael@0 547 unsigned numActualArgs() const { JS_ASSERT(hasArgs()); return u.nactual; }
michael@0 548
michael@0 549 /* Watch out, this exposes a pointer to the unaliased formal arg array. */
michael@0 550 Value *argv() const { return argv_; }
michael@0 551
michael@0 552 /*
michael@0 553 * Arguments object
michael@0 554 *
michael@0 555 * If a non-eval function has script->needsArgsObj, an arguments object is
michael@0 556 * created in the prologue and stored in the local variable for the
michael@0 557 * 'arguments' binding (script->argumentsLocal). Since this local is
michael@0 558 * mutable, the arguments object can be overwritten and we can "lose" the
michael@0 559 * arguments object. Thus, InterpreterFrame keeps an explicit argsObj_ field so
michael@0 560 * that the original arguments object is always available.
michael@0 561 */
michael@0 562
michael@0 563 ArgumentsObject &argsObj() const;
michael@0 564 void initArgsObj(ArgumentsObject &argsobj);
michael@0 565
michael@0 566 JSObject *createRestParameter(JSContext *cx);
michael@0 567
michael@0 568 /*
michael@0 569 * Scope chain
michael@0 570 *
michael@0 571 * In theory, the scope chain would contain an object for every lexical
michael@0 572 * scope. However, only objects that are required for dynamic lookup are
michael@0 573 * actually created.
michael@0 574 *
michael@0 575 * Given that an InterpreterFrame corresponds roughly to a ES5 Execution Context
michael@0 576 * (ES5 10.3), InterpreterFrame::varObj corresponds to the VariableEnvironment
michael@0 577 * component of a Exection Context. Intuitively, the variables object is
michael@0 578 * where new bindings (variables and functions) are stored. One might
michael@0 579 * expect that this is either the Call object or scopeChain.globalObj for
michael@0 580 * function or global code, respectively, however the JSAPI allows calls of
michael@0 581 * Execute to specify a variables object on the scope chain other than the
michael@0 582 * call/global object. This allows embeddings to run multiple scripts under
michael@0 583 * the same global, each time using a new variables object to collect and
michael@0 584 * discard the script's global variables.
michael@0 585 */
michael@0 586
michael@0 587 inline HandleObject scopeChain() const;
michael@0 588
michael@0 589 inline ScopeObject &aliasedVarScope(ScopeCoordinate sc) const;
michael@0 590 inline GlobalObject &global() const;
michael@0 591 inline CallObject &callObj() const;
michael@0 592 inline JSObject &varObj();
michael@0 593
michael@0 594 inline void pushOnScopeChain(ScopeObject &scope);
michael@0 595 inline void popOffScopeChain();
michael@0 596
michael@0 597 /*
michael@0 598 * For blocks with aliased locals, these interfaces push and pop entries on
michael@0 599 * the scope chain.
michael@0 600 */
michael@0 601
michael@0 602 bool pushBlock(JSContext *cx, StaticBlockObject &block);
michael@0 603 void popBlock(JSContext *cx);
michael@0 604
michael@0 605 /*
michael@0 606 * With
michael@0 607 *
michael@0 608 * Entering/leaving a |with| block pushes/pops an object on the scope chain.
michael@0 609 * Pushing uses pushOnScopeChain, popping should use popWith.
michael@0 610 */
michael@0 611
michael@0 612 void popWith(JSContext *cx);
michael@0 613
michael@0 614 /*
michael@0 615 * Script
michael@0 616 *
michael@0 617 * All function and global frames have an associated JSScript which holds
michael@0 618 * the bytecode being executed for the frame. This script/bytecode does
michael@0 619 * not reflect any inlining that has been performed by the method JIT.
michael@0 620 * If other frames were inlined into this one, the script/pc reflect the
michael@0 621 * point of the outermost call. Inlined frame invariants:
michael@0 622 *
michael@0 623 * - Inlined frames have the same scope chain as the outer frame.
michael@0 624 * - Inlined frames have the same strictness as the outer frame.
michael@0 625 * - Inlined frames can only make calls to other JIT frames associated with
michael@0 626 * the same VMFrame. Other calls force expansion of the inlined frames.
michael@0 627 */
michael@0 628
michael@0 629 JSScript *script() const {
michael@0 630 return isFunctionFrame()
michael@0 631 ? isEvalFrame()
michael@0 632 ? u.evalScript
michael@0 633 : fun()->nonLazyScript()
michael@0 634 : exec.script;
michael@0 635 }
michael@0 636
michael@0 637 /* Return the previous frame's pc. */
michael@0 638 jsbytecode *prevpc() {
michael@0 639 JS_ASSERT(prev_);
michael@0 640 return prevpc_;
michael@0 641 }
michael@0 642
michael@0 643 /* Return the previous frame's sp. */
michael@0 644 Value *prevsp() {
michael@0 645 JS_ASSERT(prev_);
michael@0 646 return prevsp_;
michael@0 647 }
michael@0 648
michael@0 649 /*
michael@0 650 * Function
michael@0 651 *
michael@0 652 * All function frames have an associated interpreted JSFunction. The
michael@0 653 * function returned by fun() and maybeFun() is not necessarily the
michael@0 654 * original canonical function which the frame's script was compiled
michael@0 655 * against.
michael@0 656 */
michael@0 657
michael@0 658 JSFunction* fun() const {
michael@0 659 JS_ASSERT(isFunctionFrame());
michael@0 660 return exec.fun;
michael@0 661 }
michael@0 662
michael@0 663 JSFunction* maybeFun() const {
michael@0 664 return isFunctionFrame() ? fun() : nullptr;
michael@0 665 }
michael@0 666
michael@0 667 /*
michael@0 668 * This value
michael@0 669 *
michael@0 670 * Every frame has a this value although, until 'this' is computed, the
michael@0 671 * value may not be the semantically-correct 'this' value.
michael@0 672 *
michael@0 673 * The 'this' value is stored before the formal arguments for function
michael@0 674 * frames and directly before the frame for global frames. The *Args
michael@0 675 * members assert !isEvalFrame(), so we implement specialized inline
michael@0 676 * methods for accessing 'this'. When the caller has static knowledge that
michael@0 677 * a frame is a function, 'functionThis' allows more efficient access.
michael@0 678 */
michael@0 679
michael@0 680 Value &functionThis() const {
michael@0 681 JS_ASSERT(isFunctionFrame());
michael@0 682 if (isEvalFrame())
michael@0 683 return ((Value *)this)[-1];
michael@0 684 return argv()[-1];
michael@0 685 }
michael@0 686
michael@0 687 JSObject &constructorThis() const {
michael@0 688 JS_ASSERT(hasArgs());
michael@0 689 return argv()[-1].toObject();
michael@0 690 }
michael@0 691
michael@0 692 Value &thisValue() const {
michael@0 693 if (flags_ & (EVAL | GLOBAL))
michael@0 694 return ((Value *)this)[-1];
michael@0 695 return argv()[-1];
michael@0 696 }
michael@0 697
michael@0 698 /*
michael@0 699 * Callee
michael@0 700 *
michael@0 701 * Only function frames have a callee. An eval frame in a function has the
michael@0 702 * same callee as its containing function frame. maybeCalleev can be used
michael@0 703 * to return a value that is either the callee object (for function frames) or
michael@0 704 * null (for global frames).
michael@0 705 */
michael@0 706
michael@0 707 JSFunction &callee() const {
michael@0 708 JS_ASSERT(isFunctionFrame());
michael@0 709 return calleev().toObject().as<JSFunction>();
michael@0 710 }
michael@0 711
michael@0 712 const Value &calleev() const {
michael@0 713 JS_ASSERT(isFunctionFrame());
michael@0 714 return mutableCalleev();
michael@0 715 }
michael@0 716
michael@0 717 const Value &maybeCalleev() const {
michael@0 718 Value &calleev = flags_ & (EVAL | GLOBAL)
michael@0 719 ? ((Value *)this)[-2]
michael@0 720 : argv()[-2];
michael@0 721 JS_ASSERT(calleev.isObjectOrNull());
michael@0 722 return calleev;
michael@0 723 }
michael@0 724
michael@0 725 Value &mutableCalleev() const {
michael@0 726 JS_ASSERT(isFunctionFrame());
michael@0 727 if (isEvalFrame())
michael@0 728 return ((Value *)this)[-2];
michael@0 729 return argv()[-2];
michael@0 730 }
michael@0 731
michael@0 732 CallReceiver callReceiver() const {
michael@0 733 return CallReceiverFromArgv(argv());
michael@0 734 }
michael@0 735
michael@0 736 /*
michael@0 737 * Frame compartment
michael@0 738 *
michael@0 739 * A stack frame's compartment is the frame's containing context's
michael@0 740 * compartment when the frame was pushed.
michael@0 741 */
michael@0 742
michael@0 743 inline JSCompartment *compartment() const;
michael@0 744
michael@0 745 /* Debugger hook data */
michael@0 746
michael@0 747 bool hasHookData() const {
michael@0 748 return !!(flags_ & HAS_HOOK_DATA);
michael@0 749 }
michael@0 750
michael@0 751 void* hookData() const {
michael@0 752 JS_ASSERT(hasHookData());
michael@0 753 return hookData_;
michael@0 754 }
michael@0 755
michael@0 756 void* maybeHookData() const {
michael@0 757 return hasHookData() ? hookData_ : nullptr;
michael@0 758 }
michael@0 759
michael@0 760 void setHookData(void *v) {
michael@0 761 hookData_ = v;
michael@0 762 flags_ |= HAS_HOOK_DATA;
michael@0 763 }
michael@0 764
michael@0 765 bool hasPushedSPSFrame() {
michael@0 766 return !!(flags_ & HAS_PUSHED_SPS_FRAME);
michael@0 767 }
michael@0 768
michael@0 769 void setPushedSPSFrame() {
michael@0 770 flags_ |= HAS_PUSHED_SPS_FRAME;
michael@0 771 }
michael@0 772
michael@0 773 void unsetPushedSPSFrame() {
michael@0 774 flags_ &= ~HAS_PUSHED_SPS_FRAME;
michael@0 775 }
michael@0 776
michael@0 777 /* Return value */
michael@0 778
michael@0 779 bool hasReturnValue() const {
michael@0 780 return !!(flags_ & HAS_RVAL);
michael@0 781 }
michael@0 782
michael@0 783 MutableHandleValue returnValue() {
michael@0 784 if (!(flags_ & HAS_RVAL))
michael@0 785 rval_.setUndefined();
michael@0 786 return MutableHandleValue::fromMarkedLocation(&rval_);
michael@0 787 }
michael@0 788
michael@0 789 void markReturnValue() {
michael@0 790 flags_ |= HAS_RVAL;
michael@0 791 }
michael@0 792
michael@0 793 void setReturnValue(const Value &v) {
michael@0 794 rval_ = v;
michael@0 795 markReturnValue();
michael@0 796 }
michael@0 797
michael@0 798 void clearReturnValue() {
michael@0 799 rval_.setUndefined();
michael@0 800 markReturnValue();
michael@0 801 }
michael@0 802
michael@0 803 /*
michael@0 804 * A "generator" frame is a function frame associated with a generator.
michael@0 805 * Since generators are not executed LIFO, the VM copies a single abstract
michael@0 806 * generator frame back and forth between the LIFO VM stack (when the
michael@0 807 * generator is active) and a snapshot stored in JSGenerator (when the
michael@0 808 * generator is inactive). A generator frame is comprised of an
michael@0 809 * InterpreterFrame structure and the values that make up the arguments,
michael@0 810 * locals, and expression stack. The layout in the JSGenerator snapshot
michael@0 811 * matches the layout on the stack (see the "VM stack layout" comment
michael@0 812 * above).
michael@0 813 */
michael@0 814
michael@0 815 bool isGeneratorFrame() const {
michael@0 816 bool ret = flags_ & GENERATOR;
michael@0 817 JS_ASSERT_IF(ret, isNonEvalFunctionFrame());
michael@0 818 return ret;
michael@0 819 }
michael@0 820
michael@0 821 void initGeneratorFrame() const {
michael@0 822 JS_ASSERT(!isGeneratorFrame());
michael@0 823 JS_ASSERT(isNonEvalFunctionFrame());
michael@0 824 flags_ |= GENERATOR;
michael@0 825 }
michael@0 826
michael@0 827 Value *generatorArgsSnapshotBegin() const {
michael@0 828 JS_ASSERT(isGeneratorFrame());
michael@0 829 return argv() - 2;
michael@0 830 }
michael@0 831
michael@0 832 Value *generatorArgsSnapshotEnd() const {
michael@0 833 JS_ASSERT(isGeneratorFrame());
michael@0 834 return argv() + js::Max(numActualArgs(), numFormalArgs());
michael@0 835 }
michael@0 836
michael@0 837 Value *generatorSlotsSnapshotBegin() const {
michael@0 838 JS_ASSERT(isGeneratorFrame());
michael@0 839 return (Value *)(this + 1);
michael@0 840 }
michael@0 841
michael@0 842 enum TriggerPostBarriers {
michael@0 843 DoPostBarrier = true,
michael@0 844 NoPostBarrier = false
michael@0 845 };
michael@0 846 template <TriggerPostBarriers doPostBarrier>
michael@0 847 void copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp,
michael@0 848 const Value *othervp, Value *othersp);
michael@0 849
michael@0 850 /*
michael@0 851 * js::Execute pushes both global and function frames (since eval() in a
michael@0 852 * function pushes a frame with isFunctionFrame() && isEvalFrame()). Most
michael@0 853 * code should not care where a frame was pushed, but if it is necessary to
michael@0 854 * pick out frames pushed by js::Execute, this is the right query:
michael@0 855 */
michael@0 856
michael@0 857 bool isFramePushedByExecute() const {
michael@0 858 return !!(flags_ & (GLOBAL | EVAL));
michael@0 859 }
michael@0 860
michael@0 861 /*
michael@0 862 * Other flags
michael@0 863 */
michael@0 864
michael@0 865 InitialFrameFlags initialFlags() const {
michael@0 866 JS_STATIC_ASSERT((int)INITIAL_NONE == 0);
michael@0 867 JS_STATIC_ASSERT((int)INITIAL_CONSTRUCT == (int)CONSTRUCTING);
michael@0 868 uint32_t mask = CONSTRUCTING;
michael@0 869 JS_ASSERT((flags_ & mask) != mask);
michael@0 870 return InitialFrameFlags(flags_ & mask);
michael@0 871 }
michael@0 872
michael@0 873 void setConstructing() {
michael@0 874 flags_ |= CONSTRUCTING;
michael@0 875 }
michael@0 876
michael@0 877 bool isConstructing() const {
michael@0 878 return !!(flags_ & CONSTRUCTING);
michael@0 879 }
michael@0 880
michael@0 881 /*
michael@0 882 * These two queries should not be used in general: the presence/absence of
michael@0 883 * the call/args object is determined by the static(ish) properties of the
michael@0 884 * JSFunction/JSScript. These queries should only be performed when probing
michael@0 885 * a stack frame that may be in the middle of the prologue (during which
michael@0 886 * time the call/args object are created).
michael@0 887 */
michael@0 888
michael@0 889 inline bool hasCallObj() const;
michael@0 890
michael@0 891 bool hasCallObjUnchecked() const {
michael@0 892 return flags_ & HAS_CALL_OBJ;
michael@0 893 }
michael@0 894
michael@0 895 bool hasArgsObj() const {
michael@0 896 JS_ASSERT(script()->needsArgsObj());
michael@0 897 return flags_ & HAS_ARGS_OBJ;
michael@0 898 }
michael@0 899
michael@0 900 void setUseNewType() {
michael@0 901 JS_ASSERT(isConstructing());
michael@0 902 flags_ |= USE_NEW_TYPE;
michael@0 903 }
michael@0 904 bool useNewType() const {
michael@0 905 JS_ASSERT(isConstructing());
michael@0 906 return flags_ & USE_NEW_TYPE;
michael@0 907 }
michael@0 908
michael@0 909 bool isDebuggerFrame() const {
michael@0 910 return !!(flags_ & DEBUGGER);
michael@0 911 }
michael@0 912
michael@0 913 bool prevUpToDate() const {
michael@0 914 return !!(flags_ & PREV_UP_TO_DATE);
michael@0 915 }
michael@0 916
michael@0 917 void setPrevUpToDate() {
michael@0 918 flags_ |= PREV_UP_TO_DATE;
michael@0 919 }
michael@0 920
michael@0 921 bool isYielding() {
michael@0 922 return !!(flags_ & YIELDING);
michael@0 923 }
michael@0 924
michael@0 925 void setYielding() {
michael@0 926 flags_ |= YIELDING;
michael@0 927 }
michael@0 928
michael@0 929 void clearYielding() {
michael@0 930 flags_ &= ~YIELDING;
michael@0 931 }
michael@0 932
michael@0 933 bool isSuspended() const {
michael@0 934 JS_ASSERT(isGeneratorFrame());
michael@0 935 return flags_ & SUSPENDED;
michael@0 936 }
michael@0 937
michael@0 938 void setSuspended() {
michael@0 939 JS_ASSERT(isGeneratorFrame());
michael@0 940 flags_ |= SUSPENDED;
michael@0 941 }
michael@0 942
michael@0 943 void clearSuspended() {
michael@0 944 JS_ASSERT(isGeneratorFrame());
michael@0 945 flags_ &= ~SUSPENDED;
michael@0 946 }
michael@0 947
michael@0 948 public:
michael@0 949 void mark(JSTracer *trc);
michael@0 950 void markValues(JSTracer *trc, unsigned start, unsigned end);
michael@0 951 void markValues(JSTracer *trc, Value *sp, jsbytecode *pc);
michael@0 952
michael@0 953 // Entered Baseline/Ion from the interpreter.
michael@0 954 bool runningInJit() const {
michael@0 955 return !!(flags_ & RUNNING_IN_JIT);
michael@0 956 }
michael@0 957 void setRunningInJit() {
michael@0 958 flags_ |= RUNNING_IN_JIT;
michael@0 959 }
michael@0 960 void clearRunningInJit() {
michael@0 961 flags_ &= ~RUNNING_IN_JIT;
michael@0 962 }
michael@0 963 };
michael@0 964
michael@0 965 static const size_t VALUES_PER_STACK_FRAME = sizeof(InterpreterFrame) / sizeof(Value);
michael@0 966
michael@0 967 static inline InterpreterFrame::Flags
michael@0 968 ToFrameFlags(InitialFrameFlags initial)
michael@0 969 {
michael@0 970 return InterpreterFrame::Flags(initial);
michael@0 971 }
michael@0 972
michael@0 973 static inline InitialFrameFlags
michael@0 974 InitialFrameFlagsFromConstructing(bool b)
michael@0 975 {
michael@0 976 return b ? INITIAL_CONSTRUCT : INITIAL_NONE;
michael@0 977 }
michael@0 978
michael@0 979 static inline bool
michael@0 980 InitialFrameFlagsAreConstructing(InitialFrameFlags initial)
michael@0 981 {
michael@0 982 return !!(initial & INITIAL_CONSTRUCT);
michael@0 983 }
michael@0 984
michael@0 985 /*****************************************************************************/
michael@0 986
michael@0 987 class InterpreterRegs
michael@0 988 {
michael@0 989 public:
michael@0 990 Value *sp;
michael@0 991 jsbytecode *pc;
michael@0 992 private:
michael@0 993 InterpreterFrame *fp_;
michael@0 994 public:
michael@0 995 InterpreterFrame *fp() const { return fp_; }
michael@0 996
michael@0 997 unsigned stackDepth() const {
michael@0 998 JS_ASSERT(sp >= fp_->base());
michael@0 999 return sp - fp_->base();
michael@0 1000 }
michael@0 1001
michael@0 1002 Value *spForStackDepth(unsigned depth) const {
michael@0 1003 JS_ASSERT(fp_->script()->nfixed() + depth <= fp_->script()->nslots());
michael@0 1004 return fp_->base() + depth;
michael@0 1005 }
michael@0 1006
michael@0 1007 /* For generators. */
michael@0 1008 void rebaseFromTo(const InterpreterRegs &from, InterpreterFrame &to) {
michael@0 1009 fp_ = &to;
michael@0 1010 sp = to.slots() + (from.sp - from.fp_->slots());
michael@0 1011 pc = from.pc;
michael@0 1012 JS_ASSERT(fp_);
michael@0 1013 }
michael@0 1014
michael@0 1015 void popInlineFrame() {
michael@0 1016 pc = fp_->prevpc();
michael@0 1017 sp = fp_->prevsp() - fp_->numActualArgs() - 1;
michael@0 1018 fp_ = fp_->prev();
michael@0 1019 JS_ASSERT(fp_);
michael@0 1020 }
michael@0 1021 void prepareToRun(InterpreterFrame &fp, JSScript *script) {
michael@0 1022 pc = script->code();
michael@0 1023 sp = fp.slots() + script->nfixed();
michael@0 1024 fp_ = &fp;
michael@0 1025 }
michael@0 1026
michael@0 1027 void setToEndOfScript();
michael@0 1028
michael@0 1029 MutableHandleValue stackHandleAt(int i) {
michael@0 1030 return MutableHandleValue::fromMarkedLocation(&sp[i]);
michael@0 1031 }
michael@0 1032
michael@0 1033 HandleValue stackHandleAt(int i) const {
michael@0 1034 return HandleValue::fromMarkedLocation(&sp[i]);
michael@0 1035 }
michael@0 1036 };
michael@0 1037
michael@0 1038 /*****************************************************************************/
michael@0 1039
michael@0 1040 class InterpreterStack
michael@0 1041 {
michael@0 1042 friend class InterpreterActivation;
michael@0 1043
michael@0 1044 static const size_t DEFAULT_CHUNK_SIZE = 4 * 1024;
michael@0 1045 LifoAlloc allocator_;
michael@0 1046
michael@0 1047 // Number of interpreter frames on the stack, for over-recursion checks.
michael@0 1048 static const size_t MAX_FRAMES = 50 * 1000;
michael@0 1049 static const size_t MAX_FRAMES_TRUSTED = MAX_FRAMES + 1000;
michael@0 1050 size_t frameCount_;
michael@0 1051
michael@0 1052 inline uint8_t *allocateFrame(JSContext *cx, size_t size);
michael@0 1053
michael@0 1054 inline InterpreterFrame *
michael@0 1055 getCallFrame(JSContext *cx, const CallArgs &args, HandleScript script,
michael@0 1056 InterpreterFrame::Flags *pflags, Value **pargv);
michael@0 1057
michael@0 1058 void releaseFrame(InterpreterFrame *fp) {
michael@0 1059 frameCount_--;
michael@0 1060 allocator_.release(fp->mark_);
michael@0 1061 }
michael@0 1062
michael@0 1063 public:
michael@0 1064 InterpreterStack()
michael@0 1065 : allocator_(DEFAULT_CHUNK_SIZE),
michael@0 1066 frameCount_(0)
michael@0 1067 { }
michael@0 1068
michael@0 1069 ~InterpreterStack() {
michael@0 1070 JS_ASSERT(frameCount_ == 0);
michael@0 1071 }
michael@0 1072
michael@0 1073 // For execution of eval or global code.
michael@0 1074 InterpreterFrame *pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv,
michael@0 1075 HandleObject scopeChain, ExecuteType type,
michael@0 1076 AbstractFramePtr evalInFrame);
michael@0 1077
michael@0 1078 // Called to invoke a function.
michael@0 1079 InterpreterFrame *pushInvokeFrame(JSContext *cx, const CallArgs &args,
michael@0 1080 InitialFrameFlags initial);
michael@0 1081
michael@0 1082 // The interpreter can push light-weight, "inline" frames without entering a
michael@0 1083 // new InterpreterActivation or recursively calling Interpret.
michael@0 1084 bool pushInlineFrame(JSContext *cx, InterpreterRegs &regs, const CallArgs &args,
michael@0 1085 HandleScript script, InitialFrameFlags initial);
michael@0 1086
michael@0 1087 void popInlineFrame(InterpreterRegs &regs);
michael@0 1088
michael@0 1089 inline void purge(JSRuntime *rt);
michael@0 1090
michael@0 1091 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
michael@0 1092 return allocator_.sizeOfExcludingThis(mallocSizeOf);
michael@0 1093 }
michael@0 1094 };
michael@0 1095
michael@0 1096 void MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc);
michael@0 1097
michael@0 1098 /*****************************************************************************/
michael@0 1099
michael@0 1100 class InvokeArgs : public JS::CallArgs
michael@0 1101 {
michael@0 1102 AutoValueVector v_;
michael@0 1103
michael@0 1104 public:
michael@0 1105 InvokeArgs(JSContext *cx) : v_(cx) {}
michael@0 1106
michael@0 1107 bool init(unsigned argc) {
michael@0 1108 if (!v_.resize(2 + argc))
michael@0 1109 return false;
michael@0 1110 ImplicitCast<CallArgs>(*this) = CallArgsFromVp(argc, v_.begin());
michael@0 1111 return true;
michael@0 1112 }
michael@0 1113 };
michael@0 1114
michael@0 1115 template <>
michael@0 1116 struct DefaultHasher<AbstractFramePtr> {
michael@0 1117 typedef AbstractFramePtr Lookup;
michael@0 1118
michael@0 1119 static js::HashNumber hash(const Lookup &key) {
michael@0 1120 return size_t(key.raw());
michael@0 1121 }
michael@0 1122
michael@0 1123 static bool match(const AbstractFramePtr &k, const Lookup &l) {
michael@0 1124 return k == l;
michael@0 1125 }
michael@0 1126 };
michael@0 1127
michael@0 1128 /*****************************************************************************/
michael@0 1129
michael@0 1130 class InterpreterActivation;
michael@0 1131 class ForkJoinActivation;
michael@0 1132 class AsmJSActivation;
michael@0 1133
michael@0 1134 namespace jit {
michael@0 1135 class JitActivation;
michael@0 1136 };
michael@0 1137
michael@0 1138 class Activation
michael@0 1139 {
michael@0 1140 protected:
michael@0 1141 JSContext *cx_;
michael@0 1142 JSCompartment *compartment_;
michael@0 1143 Activation *prev_;
michael@0 1144
michael@0 1145 // Counter incremented by JS_SaveFrameChain on the top-most activation and
michael@0 1146 // decremented by JS_RestoreFrameChain. If > 0, ScriptFrameIter should stop
michael@0 1147 // iterating when it reaches this activation (if GO_THROUGH_SAVED is not
michael@0 1148 // set).
michael@0 1149 size_t savedFrameChain_;
michael@0 1150
michael@0 1151 // Counter incremented by JS::HideScriptedCaller and decremented by
michael@0 1152 // JS::UnhideScriptedCaller. If > 0 for the top activation,
michael@0 1153 // DescribeScriptedCaller will return null instead of querying that
michael@0 1154 // activation, which should prompt the caller to consult embedding-specific
michael@0 1155 // data structures instead.
michael@0 1156 size_t hideScriptedCallerCount_;
michael@0 1157
michael@0 1158 enum Kind { Interpreter, Jit, ForkJoin, AsmJS };
michael@0 1159 Kind kind_;
michael@0 1160
michael@0 1161 inline Activation(JSContext *cx, Kind kind_);
michael@0 1162 inline ~Activation();
michael@0 1163
michael@0 1164 public:
michael@0 1165 JSContext *cx() const {
michael@0 1166 return cx_;
michael@0 1167 }
michael@0 1168 JSCompartment *compartment() const {
michael@0 1169 return compartment_;
michael@0 1170 }
michael@0 1171 Activation *prev() const {
michael@0 1172 return prev_;
michael@0 1173 }
michael@0 1174
michael@0 1175 bool isInterpreter() const {
michael@0 1176 return kind_ == Interpreter;
michael@0 1177 }
michael@0 1178 bool isJit() const {
michael@0 1179 return kind_ == Jit;
michael@0 1180 }
michael@0 1181 bool isForkJoin() const {
michael@0 1182 return kind_ == ForkJoin;
michael@0 1183 }
michael@0 1184 bool isAsmJS() const {
michael@0 1185 return kind_ == AsmJS;
michael@0 1186 }
michael@0 1187
michael@0 1188 InterpreterActivation *asInterpreter() const {
michael@0 1189 JS_ASSERT(isInterpreter());
michael@0 1190 return (InterpreterActivation *)this;
michael@0 1191 }
michael@0 1192 jit::JitActivation *asJit() const {
michael@0 1193 JS_ASSERT(isJit());
michael@0 1194 return (jit::JitActivation *)this;
michael@0 1195 }
michael@0 1196 ForkJoinActivation *asForkJoin() const {
michael@0 1197 JS_ASSERT(isForkJoin());
michael@0 1198 return (ForkJoinActivation *)this;
michael@0 1199 }
michael@0 1200 AsmJSActivation *asAsmJS() const {
michael@0 1201 JS_ASSERT(isAsmJS());
michael@0 1202 return (AsmJSActivation *)this;
michael@0 1203 }
michael@0 1204
michael@0 1205 void saveFrameChain() {
michael@0 1206 savedFrameChain_++;
michael@0 1207 }
michael@0 1208 void restoreFrameChain() {
michael@0 1209 JS_ASSERT(savedFrameChain_ > 0);
michael@0 1210 savedFrameChain_--;
michael@0 1211 }
michael@0 1212 bool hasSavedFrameChain() const {
michael@0 1213 return savedFrameChain_ > 0;
michael@0 1214 }
michael@0 1215
michael@0 1216 void hideScriptedCaller() {
michael@0 1217 hideScriptedCallerCount_++;
michael@0 1218 }
michael@0 1219 void unhideScriptedCaller() {
michael@0 1220 JS_ASSERT(hideScriptedCallerCount_ > 0);
michael@0 1221 hideScriptedCallerCount_--;
michael@0 1222 }
michael@0 1223 bool scriptedCallerIsHidden() const {
michael@0 1224 return hideScriptedCallerCount_ > 0;
michael@0 1225 }
michael@0 1226
michael@0 1227 private:
michael@0 1228 Activation(const Activation &other) MOZ_DELETE;
michael@0 1229 void operator=(const Activation &other) MOZ_DELETE;
michael@0 1230 };
michael@0 1231
michael@0 1232 // This variable holds a special opcode value which is greater than all normal
michael@0 1233 // opcodes, and is chosen such that the bitwise or of this value with any
michael@0 1234 // opcode is this value.
michael@0 1235 static const jsbytecode EnableInterruptsPseudoOpcode = -1;
michael@0 1236
michael@0 1237 static_assert(EnableInterruptsPseudoOpcode >= JSOP_LIMIT,
michael@0 1238 "EnableInterruptsPseudoOpcode must be greater than any opcode");
michael@0 1239 static_assert(EnableInterruptsPseudoOpcode == jsbytecode(-1),
michael@0 1240 "EnableInterruptsPseudoOpcode must be the maximum jsbytecode value");
michael@0 1241
michael@0 1242 class InterpreterFrameIterator;
michael@0 1243 class RunState;
michael@0 1244
michael@0 1245 class InterpreterActivation : public Activation
michael@0 1246 {
michael@0 1247 friend class js::InterpreterFrameIterator;
michael@0 1248
michael@0 1249 RunState &state_;
michael@0 1250 InterpreterRegs regs_;
michael@0 1251 InterpreterFrame *entryFrame_;
michael@0 1252 size_t opMask_; // For debugger interrupts, see js::Interpret.
michael@0 1253
michael@0 1254 #ifdef DEBUG
michael@0 1255 size_t oldFrameCount_;
michael@0 1256 #endif
michael@0 1257
michael@0 1258 public:
michael@0 1259 inline InterpreterActivation(RunState &state, JSContext *cx, InterpreterFrame *entryFrame);
michael@0 1260 inline ~InterpreterActivation();
michael@0 1261
michael@0 1262 inline bool pushInlineFrame(const CallArgs &args, HandleScript script,
michael@0 1263 InitialFrameFlags initial);
michael@0 1264 inline void popInlineFrame(InterpreterFrame *frame);
michael@0 1265
michael@0 1266 InterpreterFrame *current() const {
michael@0 1267 return regs_.fp();
michael@0 1268 }
michael@0 1269 InterpreterRegs &regs() {
michael@0 1270 return regs_;
michael@0 1271 }
michael@0 1272 InterpreterFrame *entryFrame() const {
michael@0 1273 return entryFrame_;
michael@0 1274 }
michael@0 1275 size_t opMask() const {
michael@0 1276 return opMask_;
michael@0 1277 }
michael@0 1278
michael@0 1279 // If this js::Interpret frame is running |script|, enable interrupts.
michael@0 1280 void enableInterruptsIfRunning(JSScript *script) {
michael@0 1281 if (regs_.fp()->script() == script)
michael@0 1282 enableInterruptsUnconditionally();
michael@0 1283 }
michael@0 1284 void enableInterruptsUnconditionally() {
michael@0 1285 opMask_ = EnableInterruptsPseudoOpcode;
michael@0 1286 }
michael@0 1287 void clearInterruptsMask() {
michael@0 1288 opMask_ = 0;
michael@0 1289 }
michael@0 1290 };
michael@0 1291
michael@0 1292 // Iterates over a runtime's activation list.
michael@0 1293 class ActivationIterator
michael@0 1294 {
michael@0 1295 uint8_t *jitTop_;
michael@0 1296
michael@0 1297 protected:
michael@0 1298 Activation *activation_;
michael@0 1299
michael@0 1300 private:
michael@0 1301 void settle();
michael@0 1302
michael@0 1303 public:
michael@0 1304 explicit ActivationIterator(JSRuntime *rt);
michael@0 1305
michael@0 1306 ActivationIterator &operator++();
michael@0 1307
michael@0 1308 Activation *operator->() const {
michael@0 1309 return activation_;
michael@0 1310 }
michael@0 1311 Activation *activation() const {
michael@0 1312 return activation_;
michael@0 1313 }
michael@0 1314 uint8_t *jitTop() const {
michael@0 1315 JS_ASSERT(activation_->isJit());
michael@0 1316 return jitTop_;
michael@0 1317 }
michael@0 1318 bool done() const {
michael@0 1319 return activation_ == nullptr;
michael@0 1320 }
michael@0 1321 };
michael@0 1322
michael@0 1323 namespace jit {
michael@0 1324
michael@0 1325 // A JitActivation is used for frames running in Baseline or Ion.
michael@0 1326 class JitActivation : public Activation
michael@0 1327 {
michael@0 1328 uint8_t *prevIonTop_;
michael@0 1329 JSContext *prevJitJSContext_;
michael@0 1330 bool firstFrameIsConstructing_;
michael@0 1331 bool active_;
michael@0 1332
michael@0 1333 #ifdef JS_ION
michael@0 1334 // Rematerialized Ion frames which has info copied out of snapshots. Maps
michael@0 1335 // frame pointers (i.e. ionTop) to a vector of rematerializations of all
michael@0 1336 // inline frames associated with that frame.
michael@0 1337 //
michael@0 1338 // This table is lazily initialized by calling getRematerializedFrame.
michael@0 1339 typedef Vector<RematerializedFrame *, 1> RematerializedFrameVector;
michael@0 1340 typedef HashMap<uint8_t *, RematerializedFrameVector> RematerializedFrameTable;
michael@0 1341 RematerializedFrameTable rematerializedFrames_;
michael@0 1342
michael@0 1343 void freeRematerializedFramesInVector(RematerializedFrameVector &frames);
michael@0 1344 void clearRematerializedFrames();
michael@0 1345 #endif
michael@0 1346
michael@0 1347 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 1348 protected:
michael@0 1349 // Used to verify that live registers don't change between a VM call and
michael@0 1350 // the OsiPoint that follows it. Protected to silence Clang warning.
michael@0 1351 uint32_t checkRegs_;
michael@0 1352 RegisterDump regs_;
michael@0 1353 #endif
michael@0 1354
michael@0 1355 public:
michael@0 1356 JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active = true);
michael@0 1357 ~JitActivation();
michael@0 1358
michael@0 1359 bool isActive() const {
michael@0 1360 return active_;
michael@0 1361 }
michael@0 1362 void setActive(JSContext *cx, bool active = true);
michael@0 1363
michael@0 1364 uint8_t *prevIonTop() const {
michael@0 1365 return prevIonTop_;
michael@0 1366 }
michael@0 1367 JSCompartment *compartment() const {
michael@0 1368 return compartment_;
michael@0 1369 }
michael@0 1370 bool firstFrameIsConstructing() const {
michael@0 1371 return firstFrameIsConstructing_;
michael@0 1372 }
michael@0 1373 static size_t offsetOfPrevIonTop() {
michael@0 1374 return offsetof(JitActivation, prevIonTop_);
michael@0 1375 }
michael@0 1376 static size_t offsetOfPrevJitJSContext() {
michael@0 1377 return offsetof(JitActivation, prevJitJSContext_);
michael@0 1378 }
michael@0 1379 static size_t offsetOfActiveUint8() {
michael@0 1380 JS_ASSERT(sizeof(bool) == 1);
michael@0 1381 return offsetof(JitActivation, active_);
michael@0 1382 }
michael@0 1383
michael@0 1384 #ifdef CHECK_OSIPOINT_REGISTERS
michael@0 1385 void setCheckRegs(bool check) {
michael@0 1386 checkRegs_ = check;
michael@0 1387 }
michael@0 1388 static size_t offsetOfCheckRegs() {
michael@0 1389 return offsetof(JitActivation, checkRegs_);
michael@0 1390 }
michael@0 1391 static size_t offsetOfRegs() {
michael@0 1392 return offsetof(JitActivation, regs_);
michael@0 1393 }
michael@0 1394 #endif
michael@0 1395
michael@0 1396 #ifdef JS_ION
michael@0 1397 // Look up a rematerialized frame keyed by the fp, rematerializing the
michael@0 1398 // frame if one doesn't already exist. A frame can only be rematerialized
michael@0 1399 // if an IonFrameIterator pointing to the nearest uninlined frame can be
michael@0 1400 // provided, as values need to be read out of snapshots.
michael@0 1401 //
michael@0 1402 // The inlineDepth must be within bounds of the frame pointed to by iter.
michael@0 1403 RematerializedFrame *getRematerializedFrame(JSContext *cx, JitFrameIterator &iter,
michael@0 1404 size_t inlineDepth = 0);
michael@0 1405
michael@0 1406 // Look up a rematerialized frame by the fp. If inlineDepth is out of
michael@0 1407 // bounds of what has been rematerialized, nullptr is returned.
michael@0 1408 RematerializedFrame *lookupRematerializedFrame(uint8_t *top, size_t inlineDepth = 0);
michael@0 1409
michael@0 1410 bool hasRematerializedFrame(uint8_t *top, size_t inlineDepth = 0) {
michael@0 1411 return !!lookupRematerializedFrame(top, inlineDepth);
michael@0 1412 }
michael@0 1413
michael@0 1414 // Remove a previous rematerialization by fp.
michael@0 1415 void removeRematerializedFrame(uint8_t *top);
michael@0 1416
michael@0 1417 void markRematerializedFrames(JSTracer *trc);
michael@0 1418 #endif
michael@0 1419 };
michael@0 1420
michael@0 1421 // A filtering of the ActivationIterator to only stop at JitActivations.
michael@0 1422 class JitActivationIterator : public ActivationIterator
michael@0 1423 {
michael@0 1424 void settle() {
michael@0 1425 while (!done() && !activation_->isJit())
michael@0 1426 ActivationIterator::operator++();
michael@0 1427 }
michael@0 1428
michael@0 1429 public:
michael@0 1430 explicit JitActivationIterator(JSRuntime *rt)
michael@0 1431 : ActivationIterator(rt)
michael@0 1432 {
michael@0 1433 settle();
michael@0 1434 }
michael@0 1435
michael@0 1436 JitActivationIterator &operator++() {
michael@0 1437 ActivationIterator::operator++();
michael@0 1438 settle();
michael@0 1439 return *this;
michael@0 1440 }
michael@0 1441
michael@0 1442 // Returns the bottom and top addresses of the current JitActivation.
michael@0 1443 void jitStackRange(uintptr_t *&min, uintptr_t *&end);
michael@0 1444 };
michael@0 1445
michael@0 1446 } // namespace jit
michael@0 1447
michael@0 1448 // Iterates over the frames of a single InterpreterActivation.
michael@0 1449 class InterpreterFrameIterator
michael@0 1450 {
michael@0 1451 InterpreterActivation *activation_;
michael@0 1452 InterpreterFrame *fp_;
michael@0 1453 jsbytecode *pc_;
michael@0 1454 Value *sp_;
michael@0 1455
michael@0 1456 public:
michael@0 1457 explicit InterpreterFrameIterator(InterpreterActivation *activation)
michael@0 1458 : activation_(activation),
michael@0 1459 fp_(nullptr),
michael@0 1460 pc_(nullptr),
michael@0 1461 sp_(nullptr)
michael@0 1462 {
michael@0 1463 if (activation) {
michael@0 1464 fp_ = activation->current();
michael@0 1465 pc_ = activation->regs().pc;
michael@0 1466 sp_ = activation->regs().sp;
michael@0 1467 }
michael@0 1468 }
michael@0 1469
michael@0 1470 InterpreterFrame *frame() const {
michael@0 1471 JS_ASSERT(!done());
michael@0 1472 return fp_;
michael@0 1473 }
michael@0 1474 jsbytecode *pc() const {
michael@0 1475 JS_ASSERT(!done());
michael@0 1476 return pc_;
michael@0 1477 }
michael@0 1478 Value *sp() const {
michael@0 1479 JS_ASSERT(!done());
michael@0 1480 return sp_;
michael@0 1481 }
michael@0 1482
michael@0 1483 InterpreterFrameIterator &operator++();
michael@0 1484
michael@0 1485 bool done() const {
michael@0 1486 return fp_ == nullptr;
michael@0 1487 }
michael@0 1488 };
michael@0 1489
michael@0 1490 // An AsmJSActivation is part of two activation linked lists:
michael@0 1491 // - the normal Activation list used by FrameIter
michael@0 1492 // - a list of only AsmJSActivations that is signal-safe since it is accessed
michael@0 1493 // from the profiler at arbitrary points
michael@0 1494 //
michael@0 1495 // An eventual goal is to remove AsmJSActivation and to run asm.js code in a
michael@0 1496 // JitActivation interleaved with Ion/Baseline jit code. This would allow
michael@0 1497 // efficient calls back and forth but requires that we can walk the stack for
michael@0 1498 // all kinds of jit code.
michael@0 1499 class AsmJSActivation : public Activation
michael@0 1500 {
michael@0 1501 AsmJSModule &module_;
michael@0 1502 AsmJSActivation *prevAsmJS_;
michael@0 1503 void *errorRejoinSP_;
michael@0 1504 SPSProfiler *profiler_;
michael@0 1505 void *resumePC_;
michael@0 1506 uint8_t *exitSP_;
michael@0 1507
michael@0 1508 static const intptr_t InterruptedSP = -1;
michael@0 1509
michael@0 1510 public:
michael@0 1511 AsmJSActivation(JSContext *cx, AsmJSModule &module);
michael@0 1512 ~AsmJSActivation();
michael@0 1513
michael@0 1514 JSContext *cx() { return cx_; }
michael@0 1515 AsmJSModule &module() const { return module_; }
michael@0 1516 AsmJSActivation *prevAsmJS() const { return prevAsmJS_; }
michael@0 1517
michael@0 1518 // Read by JIT code:
michael@0 1519 static unsigned offsetOfContext() { return offsetof(AsmJSActivation, cx_); }
michael@0 1520 static unsigned offsetOfResumePC() { return offsetof(AsmJSActivation, resumePC_); }
michael@0 1521
michael@0 1522 // Initialized by JIT code:
michael@0 1523 static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); }
michael@0 1524 static unsigned offsetOfExitSP() { return offsetof(AsmJSActivation, exitSP_); }
michael@0 1525
michael@0 1526 // Set from SIGSEGV handler:
michael@0 1527 void setInterrupted(void *pc) { resumePC_ = pc; exitSP_ = (uint8_t*)InterruptedSP; }
michael@0 1528 bool isInterruptedSP() const { return exitSP_ == (uint8_t*)InterruptedSP; }
michael@0 1529
michael@0 1530 // Note: exitSP is the sp right before the call instruction. On x86, this
michael@0 1531 // means before the return address is pushed on the stack, on ARM, this
michael@0 1532 // means after.
michael@0 1533 uint8_t *exitSP() const { JS_ASSERT(!isInterruptedSP()); return exitSP_; }
michael@0 1534 };
michael@0 1535
michael@0 1536 // A FrameIter walks over the runtime's stack of JS script activations,
michael@0 1537 // abstracting over whether the JS scripts were running in the interpreter or
michael@0 1538 // different modes of compiled code.
michael@0 1539 //
michael@0 1540 // FrameIter is parameterized by what it includes in the stack iteration:
michael@0 1541 // - The SavedOption controls whether FrameIter stops when it finds an
michael@0 1542 // activation that was set aside via JS_SaveFrameChain (and not yet retored
michael@0 1543 // by JS_RestoreFrameChain). (Hopefully this will go away.)
michael@0 1544 // - The ContextOption determines whether the iteration will view frames from
michael@0 1545 // all JSContexts or just the given JSContext. (Hopefully this will go away.)
michael@0 1546 // - When provided, the optional JSPrincipal argument will cause FrameIter to
michael@0 1547 // only show frames in globals whose JSPrincipals are subsumed (via
michael@0 1548 // JSSecurityCallbacks::subsume) by the given JSPrincipal.
michael@0 1549 //
michael@0 1550 // Additionally, there are derived FrameIter types that automatically skip
michael@0 1551 // certain frames:
michael@0 1552 // - ScriptFrameIter only shows frames that have an associated JSScript
michael@0 1553 // (currently everything other than asm.js stack frames). When !hasScript(),
michael@0 1554 // clients must stick to the portion of the
michael@0 1555 // interface marked below.
michael@0 1556 // - NonBuiltinScriptFrameIter additionally filters out builtin (self-hosted)
michael@0 1557 // scripts.
michael@0 1558 class FrameIter
michael@0 1559 {
michael@0 1560 public:
michael@0 1561 enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
michael@0 1562 enum ContextOption { CURRENT_CONTEXT, ALL_CONTEXTS };
michael@0 1563 enum State { DONE, INTERP, JIT, ASMJS };
michael@0 1564
michael@0 1565 // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on
michael@0 1566 // the heap, so this structure should not contain any GC things.
michael@0 1567 struct Data
michael@0 1568 {
michael@0 1569 JSContext * cx_;
michael@0 1570 SavedOption savedOption_;
michael@0 1571 ContextOption contextOption_;
michael@0 1572 JSPrincipals * principals_;
michael@0 1573
michael@0 1574 State state_;
michael@0 1575
michael@0 1576 jsbytecode * pc_;
michael@0 1577
michael@0 1578 InterpreterFrameIterator interpFrames_;
michael@0 1579 ActivationIterator activations_;
michael@0 1580
michael@0 1581 #ifdef JS_ION
michael@0 1582 jit::JitFrameIterator jitFrames_;
michael@0 1583 unsigned ionInlineFrameNo_;
michael@0 1584 AsmJSFrameIterator asmJSFrames_;
michael@0 1585 #endif
michael@0 1586
michael@0 1587 Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption,
michael@0 1588 JSPrincipals *principals);
michael@0 1589 Data(const Data &other);
michael@0 1590 };
michael@0 1591
michael@0 1592 FrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
michael@0 1593 FrameIter(JSContext *cx, ContextOption, SavedOption, JSPrincipals* = nullptr);
michael@0 1594 FrameIter(const FrameIter &iter);
michael@0 1595 FrameIter(const Data &data);
michael@0 1596 FrameIter(AbstractFramePtr frame);
michael@0 1597
michael@0 1598 bool done() const { return data_.state_ == DONE; }
michael@0 1599
michael@0 1600 // -------------------------------------------------------
michael@0 1601 // The following functions can only be called when !done()
michael@0 1602 // -------------------------------------------------------
michael@0 1603
michael@0 1604 FrameIter &operator++();
michael@0 1605
michael@0 1606 JSCompartment *compartment() const;
michael@0 1607 Activation *activation() const { return data_.activations_.activation(); }
michael@0 1608
michael@0 1609 bool isInterp() const { JS_ASSERT(!done()); return data_.state_ == INTERP; }
michael@0 1610 bool isJit() const { JS_ASSERT(!done()); return data_.state_ == JIT; }
michael@0 1611 bool isAsmJS() const { JS_ASSERT(!done()); return data_.state_ == ASMJS; }
michael@0 1612 inline bool isIon() const;
michael@0 1613 inline bool isBaseline() const;
michael@0 1614
michael@0 1615 bool isFunctionFrame() const;
michael@0 1616 bool isGlobalFrame() const;
michael@0 1617 bool isEvalFrame() const;
michael@0 1618 bool isNonEvalFunctionFrame() const;
michael@0 1619 bool isGeneratorFrame() const;
michael@0 1620 bool hasArgs() const { return isNonEvalFunctionFrame(); }
michael@0 1621
michael@0 1622 /*
michael@0 1623 * Get an abstract frame pointer dispatching to either an interpreter,
michael@0 1624 * baseline, or rematerialized optimized frame.
michael@0 1625 */
michael@0 1626 ScriptSource *scriptSource() const;
michael@0 1627 const char *scriptFilename() const;
michael@0 1628 unsigned computeLine(uint32_t *column = nullptr) const;
michael@0 1629 JSAtom *functionDisplayAtom() const;
michael@0 1630 JSPrincipals *originPrincipals() const;
michael@0 1631
michael@0 1632 bool hasScript() const { return !isAsmJS(); }
michael@0 1633
michael@0 1634 // -----------------------------------------------------------
michael@0 1635 // The following functions can only be called when hasScript()
michael@0 1636 // -----------------------------------------------------------
michael@0 1637
michael@0 1638 inline JSScript *script() const;
michael@0 1639
michael@0 1640 bool isConstructing() const;
michael@0 1641 jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; }
michael@0 1642 void updatePcQuadratic();
michael@0 1643 JSFunction *callee() const;
michael@0 1644 Value calleev() const;
michael@0 1645 unsigned numActualArgs() const;
michael@0 1646 unsigned numFormalArgs() const;
michael@0 1647 Value unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
michael@0 1648 template <class Op> inline void unaliasedForEachActual(JSContext *cx, Op op);
michael@0 1649
michael@0 1650 JSObject *scopeChain() const;
michael@0 1651 CallObject &callObj() const;
michael@0 1652
michael@0 1653 bool hasArgsObj() const;
michael@0 1654 ArgumentsObject &argsObj() const;
michael@0 1655
michael@0 1656 // Ensure that computedThisValue is correct, see ComputeThis.
michael@0 1657 bool computeThis(JSContext *cx) const;
michael@0 1658 // thisv() may not always be correct, even after computeThis. In case when
michael@0 1659 // the frame is an Ion frame, the computed this value cannot be saved to
michael@0 1660 // the Ion frame but is instead saved in the RematerializedFrame for use
michael@0 1661 // by Debugger.
michael@0 1662 //
michael@0 1663 // Both methods exist because of speed. thisv() will never rematerialize
michael@0 1664 // an Ion frame, whereas computedThisValue() will.
michael@0 1665 Value computedThisValue() const;
michael@0 1666 Value thisv() const;
michael@0 1667
michael@0 1668 Value returnValue() const;
michael@0 1669 void setReturnValue(const Value &v);
michael@0 1670
michael@0 1671 JSFunction *maybeCallee() const {
michael@0 1672 return isFunctionFrame() ? callee() : nullptr;
michael@0 1673 }
michael@0 1674
michael@0 1675 // These are only valid for the top frame.
michael@0 1676 size_t numFrameSlots() const;
michael@0 1677 Value frameSlotValue(size_t index) const;
michael@0 1678
michael@0 1679 // Ensures that we have rematerialized the top frame and its associated
michael@0 1680 // inline frames. Can only be called when isIon().
michael@0 1681 bool ensureHasRematerializedFrame();
michael@0 1682
michael@0 1683 // True when isInterp() or isBaseline(). True when isIon() if it
michael@0 1684 // has a rematerialized frame. False otherwise false otherwise.
michael@0 1685 bool hasUsableAbstractFramePtr() const;
michael@0 1686
michael@0 1687 // -----------------------------------------------------------
michael@0 1688 // The following functions can only be called when isInterp(),
michael@0 1689 // isBaseline(), or isIon(). Further, abstractFramePtr() can
michael@0 1690 // only be called when hasUsableAbstractFramePtr().
michael@0 1691 // -----------------------------------------------------------
michael@0 1692
michael@0 1693 AbstractFramePtr abstractFramePtr() const;
michael@0 1694 AbstractFramePtr copyDataAsAbstractFramePtr() const;
michael@0 1695 Data *copyData() const;
michael@0 1696
michael@0 1697 // This can only be called when isInterp():
michael@0 1698 inline InterpreterFrame *interpFrame() const;
michael@0 1699
michael@0 1700 private:
michael@0 1701 Data data_;
michael@0 1702 #ifdef JS_ION
michael@0 1703 jit::InlineFrameIterator ionInlineFrames_;
michael@0 1704 #endif
michael@0 1705
michael@0 1706 void popActivation();
michael@0 1707 void popInterpreterFrame();
michael@0 1708 #ifdef JS_ION
michael@0 1709 void nextJitFrame();
michael@0 1710 void popJitFrame();
michael@0 1711 void popAsmJSFrame();
michael@0 1712 #endif
michael@0 1713 void settleOnActivation();
michael@0 1714
michael@0 1715 friend class ::JSBrokenFrameIterator;
michael@0 1716 };
michael@0 1717
michael@0 1718 class ScriptFrameIter : public FrameIter
michael@0 1719 {
michael@0 1720 void settle() {
michael@0 1721 while (!done() && !hasScript())
michael@0 1722 FrameIter::operator++();
michael@0 1723 }
michael@0 1724
michael@0 1725 public:
michael@0 1726 ScriptFrameIter(JSContext *cx, SavedOption savedOption = STOP_AT_SAVED)
michael@0 1727 : FrameIter(cx, savedOption)
michael@0 1728 {
michael@0 1729 settle();
michael@0 1730 }
michael@0 1731
michael@0 1732 ScriptFrameIter(JSContext *cx,
michael@0 1733 ContextOption cxOption,
michael@0 1734 SavedOption savedOption,
michael@0 1735 JSPrincipals *prin = nullptr)
michael@0 1736 : FrameIter(cx, cxOption, savedOption, prin)
michael@0 1737 {
michael@0 1738 settle();
michael@0 1739 }
michael@0 1740
michael@0 1741 ScriptFrameIter(const ScriptFrameIter &iter) : FrameIter(iter) { settle(); }
michael@0 1742 ScriptFrameIter(const FrameIter::Data &data) : FrameIter(data) { settle(); }
michael@0 1743 ScriptFrameIter(AbstractFramePtr frame) : FrameIter(frame) { settle(); }
michael@0 1744
michael@0 1745 ScriptFrameIter &operator++() {
michael@0 1746 FrameIter::operator++();
michael@0 1747 settle();
michael@0 1748 return *this;
michael@0 1749 }
michael@0 1750 };
michael@0 1751
michael@0 1752 #ifdef DEBUG
michael@0 1753 bool SelfHostedFramesVisible();
michael@0 1754 #else
michael@0 1755 static inline bool
michael@0 1756 SelfHostedFramesVisible()
michael@0 1757 {
michael@0 1758 return false;
michael@0 1759 }
michael@0 1760 #endif
michael@0 1761
michael@0 1762 /* A filtering of the FrameIter to only stop at non-self-hosted scripts. */
michael@0 1763 class NonBuiltinFrameIter : public FrameIter
michael@0 1764 {
michael@0 1765 void settle();
michael@0 1766
michael@0 1767 public:
michael@0 1768 NonBuiltinFrameIter(JSContext *cx,
michael@0 1769 FrameIter::SavedOption opt = FrameIter::STOP_AT_SAVED)
michael@0 1770 : FrameIter(cx, opt)
michael@0 1771 {
michael@0 1772 settle();
michael@0 1773 }
michael@0 1774
michael@0 1775 NonBuiltinFrameIter(JSContext *cx,
michael@0 1776 FrameIter::ContextOption contextOption,
michael@0 1777 FrameIter::SavedOption savedOption,
michael@0 1778 JSPrincipals *principals = nullptr)
michael@0 1779 : FrameIter(cx, contextOption, savedOption, principals)
michael@0 1780 {
michael@0 1781 settle();
michael@0 1782 }
michael@0 1783
michael@0 1784 NonBuiltinFrameIter(const FrameIter::Data &data)
michael@0 1785 : FrameIter(data)
michael@0 1786 {}
michael@0 1787
michael@0 1788 NonBuiltinFrameIter &operator++() {
michael@0 1789 FrameIter::operator++();
michael@0 1790 settle();
michael@0 1791 return *this;
michael@0 1792 }
michael@0 1793 };
michael@0 1794
michael@0 1795 /* A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts. */
michael@0 1796 class NonBuiltinScriptFrameIter : public ScriptFrameIter
michael@0 1797 {
michael@0 1798 void settle();
michael@0 1799
michael@0 1800 public:
michael@0 1801 NonBuiltinScriptFrameIter(JSContext *cx,
michael@0 1802 ScriptFrameIter::SavedOption opt = ScriptFrameIter::STOP_AT_SAVED)
michael@0 1803 : ScriptFrameIter(cx, opt)
michael@0 1804 {
michael@0 1805 settle();
michael@0 1806 }
michael@0 1807
michael@0 1808 NonBuiltinScriptFrameIter(JSContext *cx,
michael@0 1809 ScriptFrameIter::ContextOption contextOption,
michael@0 1810 ScriptFrameIter::SavedOption savedOption,
michael@0 1811 JSPrincipals *principals = nullptr)
michael@0 1812 : ScriptFrameIter(cx, contextOption, savedOption, principals)
michael@0 1813 {
michael@0 1814 settle();
michael@0 1815 }
michael@0 1816
michael@0 1817 NonBuiltinScriptFrameIter(const ScriptFrameIter::Data &data)
michael@0 1818 : ScriptFrameIter(data)
michael@0 1819 {}
michael@0 1820
michael@0 1821 NonBuiltinScriptFrameIter &operator++() {
michael@0 1822 ScriptFrameIter::operator++();
michael@0 1823 settle();
michael@0 1824 return *this;
michael@0 1825 }
michael@0 1826 };
michael@0 1827
michael@0 1828 /*
michael@0 1829 * Blindly iterate over all frames in the current thread's stack. These frames
michael@0 1830 * can be from different contexts and compartments, so beware.
michael@0 1831 */
michael@0 1832 class AllFramesIter : public ScriptFrameIter
michael@0 1833 {
michael@0 1834 public:
michael@0 1835 AllFramesIter(JSContext *cx)
michael@0 1836 : ScriptFrameIter(cx, ScriptFrameIter::ALL_CONTEXTS, ScriptFrameIter::GO_THROUGH_SAVED)
michael@0 1837 {}
michael@0 1838 };
michael@0 1839
michael@0 1840 /* Popular inline definitions. */
michael@0 1841
michael@0 1842 inline JSScript *
michael@0 1843 FrameIter::script() const
michael@0 1844 {
michael@0 1845 JS_ASSERT(!done());
michael@0 1846 if (data_.state_ == INTERP)
michael@0 1847 return interpFrame()->script();
michael@0 1848 #ifdef JS_ION
michael@0 1849 JS_ASSERT(data_.state_ == JIT);
michael@0 1850 if (data_.jitFrames_.isIonJS())
michael@0 1851 return ionInlineFrames_.script();
michael@0 1852 return data_.jitFrames_.script();
michael@0 1853 #else
michael@0 1854 return nullptr;
michael@0 1855 #endif
michael@0 1856 }
michael@0 1857
michael@0 1858 inline bool
michael@0 1859 FrameIter::isIon() const
michael@0 1860 {
michael@0 1861 #ifdef JS_ION
michael@0 1862 return isJit() && data_.jitFrames_.isIonJS();
michael@0 1863 #else
michael@0 1864 return false;
michael@0 1865 #endif
michael@0 1866 }
michael@0 1867
michael@0 1868 inline bool
michael@0 1869 FrameIter::isBaseline() const
michael@0 1870 {
michael@0 1871 #ifdef JS_ION
michael@0 1872 return isJit() && data_.jitFrames_.isBaselineJS();
michael@0 1873 #else
michael@0 1874 return false;
michael@0 1875 #endif
michael@0 1876 }
michael@0 1877
michael@0 1878 inline InterpreterFrame *
michael@0 1879 FrameIter::interpFrame() const
michael@0 1880 {
michael@0 1881 JS_ASSERT(data_.state_ == INTERP);
michael@0 1882 return data_.interpFrames_.frame();
michael@0 1883 }
michael@0 1884
michael@0 1885 } /* namespace js */
michael@0 1886 #endif /* vm_Stack_h */

mercurial