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