Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 ®s, const CallArgs &args, |
michael@0 | 1085 | HandleScript script, InitialFrameFlags initial); |
michael@0 | 1086 | |
michael@0 | 1087 | void popInlineFrame(InterpreterRegs ®s); |
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 ®s() { |
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 */ |