/src/mozilla-central/js/src/vm/Stack.h
Line | Count | Source (jump to first uncovered line) |
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/Atomics.h" |
11 | | #include "mozilla/HashFunctions.h" |
12 | | #include "mozilla/Maybe.h" |
13 | | #include "mozilla/MaybeOneOf.h" |
14 | | #include "mozilla/MemoryReporting.h" |
15 | | #include "mozilla/Variant.h" |
16 | | |
17 | | #include "jsutil.h" |
18 | | |
19 | | #include "gc/Rooting.h" |
20 | | #ifdef CHECK_OSIPOINT_REGISTERS |
21 | | #include "jit/Registers.h" // for RegisterDump |
22 | | #endif |
23 | | #include "jit/JSJitFrameIter.h" |
24 | | #include "js/RootingAPI.h" |
25 | | #include "js/TypeDecls.h" |
26 | | #include "js/UniquePtr.h" |
27 | | #include "vm/ArgumentsObject.h" |
28 | | #include "vm/JSFunction.h" |
29 | | #include "vm/JSScript.h" |
30 | | #include "vm/SavedFrame.h" |
31 | | #include "wasm/WasmFrameIter.h" |
32 | | #include "wasm/WasmSignalHandlers.h" |
33 | | #include "wasm/WasmTypes.h" |
34 | | |
35 | | namespace JS { |
36 | | namespace dbg { |
37 | | #ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING |
38 | | #pragma GCC diagnostic push |
39 | | #pragma GCC diagnostic ignored "-Wattributes" |
40 | | #endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING |
41 | | |
42 | | class JS_PUBLIC_API(AutoEntryMonitor); |
43 | | |
44 | | #ifdef JS_BROKEN_GCC_ATTRIBUTE_WARNING |
45 | | #pragma GCC diagnostic pop |
46 | | #endif // JS_BROKEN_GCC_ATTRIBUTE_WARNING |
47 | | } // namespace dbg |
48 | | } // namespace JS |
49 | | |
50 | | namespace js { |
51 | | |
52 | | class InterpreterRegs; |
53 | | class CallObject; |
54 | | class FrameIter; |
55 | | class EnvironmentObject; |
56 | | class ScriptFrameIter; |
57 | | class GeckoProfilerRuntime; |
58 | | class InterpreterFrame; |
59 | | class LexicalEnvironmentObject; |
60 | | class EnvironmentIter; |
61 | | class EnvironmentCoordinate; |
62 | | |
63 | | class SavedFrame; |
64 | | |
65 | | namespace jit { |
66 | | class CommonFrameLayout; |
67 | | } |
68 | | namespace wasm { |
69 | | class DebugFrame; |
70 | | class Instance; |
71 | | } |
72 | | |
73 | | // [SMDOC] VM stack layout |
74 | | // |
75 | | // A JSRuntime's stack consists of a linked list of activations. Every activation |
76 | | // contains a number of scripted frames that are either running in the interpreter |
77 | | // (InterpreterActivation) or JIT code (JitActivation). The frames inside a single |
78 | | // activation are contiguous: whenever C++ calls back into JS, a new activation is |
79 | | // pushed. |
80 | | // |
81 | | // Every activation is tied to a single JSContext and JS::Compartment. This means we |
82 | | // can reconstruct a given context's stack by skipping activations belonging to other |
83 | | // contexts. This happens whenever an embedding enters the JS engine on cx1 and |
84 | | // then, from a native called by the JS engine, reenters the VM on cx2. |
85 | | |
86 | | // Interpreter frames (InterpreterFrame) |
87 | | // |
88 | | // Each interpreter script activation (global or function code) is given a |
89 | | // fixed-size header (js::InterpreterFrame). The frame contains bookkeeping |
90 | | // information about the activation and links to the previous frame. |
91 | | // |
92 | | // The values after an InterpreterFrame in memory are its locals followed by its |
93 | | // expression stack. InterpreterFrame::argv_ points to the frame's arguments. |
94 | | // Missing formal arguments are padded with |undefined|, so the number of |
95 | | // arguments is always >= the number of formals. |
96 | | // |
97 | | // The top of an activation's current frame's expression stack is pointed to by |
98 | | // the activation's "current regs", which contains the stack pointer 'sp'. In |
99 | | // the interpreter, sp is adjusted as individual values are pushed and popped |
100 | | // from the stack and the InterpreterRegs struct (pointed to by the |
101 | | // InterpreterActivation) is a local var of js::Interpret. |
102 | | |
103 | | enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false }; |
104 | | enum MaybeCheckTDZ { CheckTDZ = true, DontCheckTDZ = false }; |
105 | | |
106 | | } // namespace js |
107 | | |
108 | | namespace mozilla { |
109 | | template <> |
110 | | struct IsPod<js::MaybeCheckTDZ> : TrueType {}; |
111 | | } // namespace mozilla |
112 | | |
113 | | /*****************************************************************************/ |
114 | | |
115 | | namespace js { |
116 | | |
117 | | namespace jit { |
118 | | class BaselineFrame; |
119 | | class RematerializedFrame; |
120 | | } // namespace jit |
121 | | |
122 | | /** |
123 | | * Pointer to a live JS or WASM stack frame. |
124 | | */ |
125 | | class AbstractFramePtr |
126 | | { |
127 | | friend class FrameIter; |
128 | | |
129 | | uintptr_t ptr_; |
130 | | |
131 | | enum { |
132 | | Tag_InterpreterFrame = 0x1, |
133 | | Tag_BaselineFrame = 0x2, |
134 | | Tag_RematerializedFrame = 0x3, |
135 | | Tag_WasmDebugFrame = 0x4, |
136 | | TagMask = 0x7 |
137 | | }; |
138 | | |
139 | | public: |
140 | | AbstractFramePtr() |
141 | | : ptr_(0) |
142 | 162 | {} |
143 | | |
144 | | MOZ_IMPLICIT AbstractFramePtr(InterpreterFrame* fp) |
145 | | : ptr_(fp ? uintptr_t(fp) | Tag_InterpreterFrame : 0) |
146 | 618 | { |
147 | 618 | MOZ_ASSERT_IF(fp, asInterpreterFrame() == fp); |
148 | 618 | } |
149 | | |
150 | | MOZ_IMPLICIT AbstractFramePtr(jit::BaselineFrame* fp) |
151 | | : ptr_(fp ? uintptr_t(fp) | Tag_BaselineFrame : 0) |
152 | 222 | { |
153 | 222 | MOZ_ASSERT_IF(fp, asBaselineFrame() == fp); |
154 | 222 | } |
155 | | |
156 | | MOZ_IMPLICIT AbstractFramePtr(jit::RematerializedFrame* fp) |
157 | | : ptr_(fp ? uintptr_t(fp) | Tag_RematerializedFrame : 0) |
158 | 0 | { |
159 | 0 | MOZ_ASSERT_IF(fp, asRematerializedFrame() == fp); |
160 | 0 | } |
161 | | |
162 | | MOZ_IMPLICIT AbstractFramePtr(wasm::DebugFrame* fp) |
163 | | : ptr_(fp ? uintptr_t(fp) | Tag_WasmDebugFrame : 0) |
164 | 0 | { |
165 | 0 | static_assert(wasm::DebugFrame::Alignment >= TagMask, "aligned"); |
166 | 0 | MOZ_ASSERT_IF(fp, asWasmDebugFrame() == fp); |
167 | 0 | } |
168 | | |
169 | 0 | static AbstractFramePtr FromRaw(void* raw) { |
170 | 0 | AbstractFramePtr frame; |
171 | 0 | frame.ptr_ = uintptr_t(raw); |
172 | 0 | return frame; |
173 | 0 | } |
174 | | |
175 | 2.70k | bool isInterpreterFrame() const { |
176 | 2.70k | return (ptr_ & TagMask) == Tag_InterpreterFrame; |
177 | 2.70k | } |
178 | 1.97k | InterpreterFrame* asInterpreterFrame() const { |
179 | 1.97k | MOZ_ASSERT(isInterpreterFrame()); |
180 | 1.97k | InterpreterFrame* res = (InterpreterFrame*)(ptr_ & ~TagMask); |
181 | 1.97k | MOZ_ASSERT(res); |
182 | 1.97k | return res; |
183 | 1.97k | } |
184 | 728 | bool isBaselineFrame() const { |
185 | 728 | return (ptr_ & TagMask) == Tag_BaselineFrame; |
186 | 728 | } |
187 | 728 | jit::BaselineFrame* asBaselineFrame() const { |
188 | 728 | MOZ_ASSERT(isBaselineFrame()); |
189 | 728 | jit::BaselineFrame* res = (jit::BaselineFrame*)(ptr_ & ~TagMask); |
190 | 728 | MOZ_ASSERT(res); |
191 | 728 | return res; |
192 | 728 | } |
193 | 0 | bool isRematerializedFrame() const { |
194 | 0 | return (ptr_ & TagMask) == Tag_RematerializedFrame; |
195 | 0 | } |
196 | 0 | jit::RematerializedFrame* asRematerializedFrame() const { |
197 | 0 | MOZ_ASSERT(isRematerializedFrame()); |
198 | 0 | jit::RematerializedFrame* res = (jit::RematerializedFrame*)(ptr_ & ~TagMask); |
199 | 0 | MOZ_ASSERT(res); |
200 | 0 | return res; |
201 | 0 | } |
202 | 867 | bool isWasmDebugFrame() const { |
203 | 867 | return (ptr_ & TagMask) == Tag_WasmDebugFrame; |
204 | 867 | } |
205 | 0 | wasm::DebugFrame* asWasmDebugFrame() const { |
206 | 0 | MOZ_ASSERT(isWasmDebugFrame()); |
207 | 0 | wasm::DebugFrame* res = (wasm::DebugFrame*)(ptr_ & ~TagMask); |
208 | 0 | MOZ_ASSERT(res); |
209 | 0 | return res; |
210 | 0 | } |
211 | | |
212 | 0 | void* raw() const { return reinterpret_cast<void*>(ptr_); } |
213 | | |
214 | 0 | bool operator ==(const AbstractFramePtr& other) const { return ptr_ == other.ptr_; } |
215 | 0 | bool operator !=(const AbstractFramePtr& other) const { return ptr_ != other.ptr_; } |
216 | | |
217 | 1.29k | explicit operator bool() const { return !!ptr_; } |
218 | | |
219 | | inline JSObject* environmentChain() const; |
220 | | inline CallObject& callObj() const; |
221 | | inline bool initFunctionEnvironmentObjects(JSContext* cx); |
222 | | inline bool pushVarEnvironment(JSContext* cx, HandleScope scope); |
223 | | template <typename SpecificEnvironment> |
224 | | inline void pushOnEnvironmentChain(SpecificEnvironment& env); |
225 | | template <typename SpecificEnvironment> |
226 | | inline void popOffEnvironmentChain(); |
227 | | |
228 | | inline JS::Realm* realm() const; |
229 | | |
230 | | inline bool hasInitialEnvironment() const; |
231 | | inline bool isGlobalFrame() const; |
232 | | inline bool isModuleFrame() const; |
233 | | inline bool isEvalFrame() const; |
234 | | inline bool isDebuggerEvalFrame() const; |
235 | | |
236 | | inline bool hasScript() const; |
237 | | inline JSScript* script() const; |
238 | | inline wasm::Instance* wasmInstance() const; |
239 | | inline GlobalObject* global() const; |
240 | | inline JSFunction* callee() const; |
241 | | inline Value calleev() const; |
242 | | inline Value& thisArgument() const; |
243 | | |
244 | | inline bool isConstructing() const; |
245 | | inline Value newTarget() const; |
246 | | |
247 | | inline bool debuggerNeedsCheckPrimitiveReturn() const; |
248 | | |
249 | | inline bool isFunctionFrame() const; |
250 | | inline bool isNonStrictDirectEvalFrame() const; |
251 | | inline bool isStrictEvalFrame() const; |
252 | | |
253 | | inline unsigned numActualArgs() const; |
254 | | inline unsigned numFormalArgs() const; |
255 | | |
256 | | inline Value* argv() const; |
257 | | |
258 | | inline bool hasArgs() const; |
259 | | inline bool hasArgsObj() const; |
260 | | inline ArgumentsObject& argsObj() const; |
261 | | inline void initArgsObj(ArgumentsObject& argsobj) const; |
262 | | |
263 | | inline Value& unaliasedLocal(uint32_t i); |
264 | | inline Value& unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); |
265 | | inline Value& unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING); |
266 | | template <class Op> inline void unaliasedForEachActual(JSContext* cx, Op op); |
267 | | |
268 | | inline bool prevUpToDate() const; |
269 | | inline void setPrevUpToDate() const; |
270 | | inline void unsetPrevUpToDate() const; |
271 | | |
272 | | inline bool isDebuggee() const; |
273 | | inline void setIsDebuggee(); |
274 | | inline void unsetIsDebuggee(); |
275 | | |
276 | | inline HandleValue returnValue() const; |
277 | | inline void setReturnValue(const Value& rval) const; |
278 | | |
279 | | friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, InterpreterFrame*); |
280 | | friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, jit::BaselineFrame*); |
281 | | friend void GDBTestInitAbstractFramePtr(AbstractFramePtr&, jit::RematerializedFrame*); |
282 | | friend void GDBTestInitAbstractFramePtr(AbstractFramePtr& frame, wasm::DebugFrame* ptr); |
283 | | }; |
284 | | |
285 | | class NullFramePtr : public AbstractFramePtr |
286 | | { |
287 | | public: |
288 | | NullFramePtr() |
289 | | : AbstractFramePtr() |
290 | 162 | { } |
291 | | }; |
292 | | |
293 | | enum MaybeConstruct { NO_CONSTRUCT = false, CONSTRUCT = true }; |
294 | | |
295 | | /*****************************************************************************/ |
296 | | |
297 | | class InterpreterFrame |
298 | | { |
299 | | enum Flags : uint32_t { |
300 | | CONSTRUCTING = 0x1, /* frame is for a constructor invocation */ |
301 | | |
302 | | RESUMED_GENERATOR = 0x2, /* frame is for a resumed generator invocation */ |
303 | | |
304 | | /* Function prologue state */ |
305 | | HAS_INITIAL_ENV = 0x4, /* callobj created for function or var env for eval */ |
306 | | HAS_ARGS_OBJ = 0x8, /* ArgumentsObject created for needsArgsObj script */ |
307 | | |
308 | | /* Lazy frame initialization */ |
309 | | HAS_RVAL = 0x10, /* frame has rval_ set */ |
310 | | |
311 | | /* Debugger state */ |
312 | | PREV_UP_TO_DATE = 0x20, /* see DebugScopes::updateLiveScopes */ |
313 | | |
314 | | /* |
315 | | * See comment above 'isDebuggee' in Realm.h for explanation of |
316 | | * invariants of debuggee compartments, scripts, and frames. |
317 | | */ |
318 | | DEBUGGEE = 0x40, /* Execution is being observed by Debugger */ |
319 | | |
320 | | /* Used in tracking calls and profiling (see vm/GeckoProfiler.cpp) */ |
321 | | HAS_PUSHED_PROF_FRAME = 0x80, /* Gecko Profiler was notified of entry */ |
322 | | |
323 | | /* |
324 | | * If set, we entered one of the JITs and ScriptFrameIter should skip |
325 | | * this frame. |
326 | | */ |
327 | | RUNNING_IN_JIT = 0x100, |
328 | | |
329 | | /* |
330 | | * If set, this frame has been on the stack when |
331 | | * |js::SavedStacks::saveCurrentStack| was called, and so there is a |
332 | | * |js::SavedFrame| object cached for this frame. |
333 | | */ |
334 | | HAS_CACHED_SAVED_FRAME = 0x200, |
335 | | }; |
336 | | |
337 | | mutable uint32_t flags_; /* bits described by Flags */ |
338 | | uint32_t nactual_; /* number of actual arguments, for function frames */ |
339 | | JSScript* script_; /* the script we're executing */ |
340 | | JSObject* envChain_; /* current environment chain */ |
341 | | Value rval_; /* if HAS_RVAL, return value of the frame */ |
342 | | ArgumentsObject* argsObj_; /* if HAS_ARGS_OBJ, the call's arguments object */ |
343 | | |
344 | | /* |
345 | | * Previous frame and its pc and sp. Always nullptr for |
346 | | * InterpreterActivation's entry frame, always non-nullptr for inline |
347 | | * frames. |
348 | | */ |
349 | | InterpreterFrame* prev_; |
350 | | jsbytecode* prevpc_; |
351 | | Value* prevsp_; |
352 | | |
353 | | void* unused; |
354 | | |
355 | | /* |
356 | | * For an eval-in-frame DEBUGGER_EVAL frame, the frame in whose scope |
357 | | * we're evaluating code. Iteration treats this as our previous frame. |
358 | | */ |
359 | | AbstractFramePtr evalInFramePrev_; |
360 | | |
361 | | Value* argv_; /* If hasArgs(), points to frame's arguments. */ |
362 | | LifoAlloc::Mark mark_; /* Used to release memory for this frame. */ |
363 | | |
364 | 0 | static void staticAsserts() { |
365 | 0 | JS_STATIC_ASSERT(offsetof(InterpreterFrame, rval_) % sizeof(Value) == 0); |
366 | 0 | JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(Value) == 0); |
367 | 0 | } |
368 | | |
369 | | /* |
370 | | * The utilities are private since they are not able to assert that only |
371 | | * unaliased vars/formals are accessed. Normal code should prefer the |
372 | | * InterpreterFrame::unaliased* members (or InterpreterRegs::stackDepth for |
373 | | * the usual "depth is at least" assertions). |
374 | | */ |
375 | 1.13k | Value* slots() const { return (Value*)(this + 1); } |
376 | 1 | Value* base() const { return slots() + script()->nfixed(); } |
377 | | |
378 | | friend class FrameIter; |
379 | | friend class InterpreterRegs; |
380 | | friend class InterpreterStack; |
381 | | friend class jit::BaselineFrame; |
382 | | |
383 | | /* |
384 | | * Frame initialization, called by InterpreterStack operations after acquiring |
385 | | * the raw memory for the frame: |
386 | | */ |
387 | | |
388 | | /* Used for Invoke and Interpret. */ |
389 | | void initCallFrame(InterpreterFrame* prev, jsbytecode* prevpc, Value* prevsp, |
390 | | JSFunction& callee, JSScript* script, Value* argv, uint32_t nactual, |
391 | | MaybeConstruct constructing); |
392 | | |
393 | | /* Used for global and eval frames. */ |
394 | | void initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr prev, |
395 | | const Value& newTargetValue, HandleObject envChain); |
396 | | |
397 | | public: |
398 | | /* |
399 | | * Frame prologue/epilogue |
400 | | * |
401 | | * Every stack frame must have 'prologue' called before executing the |
402 | | * first op and 'epilogue' called after executing the last op and before |
403 | | * popping the frame (whether the exit is exceptional or not). |
404 | | * |
405 | | * For inline JS calls/returns, it is easy to call the prologue/epilogue |
406 | | * exactly once. When calling JS from C++, Invoke/Execute push the stack |
407 | | * frame but do *not* call the prologue/epilogue. That means Interpret |
408 | | * must call the prologue/epilogue for the entry frame. This scheme |
409 | | * simplifies jit compilation. |
410 | | * |
411 | | * An important corner case is what happens when an error occurs (OOM, |
412 | | * over-recursed) after pushing the stack frame but before 'prologue' is |
413 | | * called or completes fully. To simplify usage, 'epilogue' does not assume |
414 | | * 'prologue' has completed and handles all the intermediate state details. |
415 | | */ |
416 | | |
417 | | bool prologue(JSContext* cx); |
418 | | void epilogue(JSContext* cx, jsbytecode* pc); |
419 | | |
420 | | bool checkReturn(JSContext* cx, HandleValue thisv); |
421 | | |
422 | | bool initFunctionEnvironmentObjects(JSContext* cx); |
423 | | |
424 | | /* |
425 | | * Initialize locals of newly-pushed frame to undefined. |
426 | | */ |
427 | | void initLocals(); |
428 | | |
429 | | /* |
430 | | * Stack frame type |
431 | | * |
432 | | * A stack frame may have one of four types, which determines which |
433 | | * members of the frame may be accessed and other invariants: |
434 | | * |
435 | | * global frame: execution of global code |
436 | | * function frame: execution of function code |
437 | | * module frame: execution of a module |
438 | | * eval frame: execution of eval code |
439 | | */ |
440 | | |
441 | 155 | bool isGlobalFrame() const { |
442 | 155 | return script_->isGlobalCode(); |
443 | 155 | } |
444 | | |
445 | 147 | bool isModuleFrame() const { |
446 | 147 | return script_->module(); |
447 | 147 | } |
448 | | |
449 | 333 | bool isEvalFrame() const { |
450 | 333 | return script_->isForEval(); |
451 | 333 | } |
452 | | |
453 | 167 | bool isFunctionFrame() const { |
454 | 167 | return script_->functionNonDelazifying(); |
455 | 167 | } |
456 | | |
457 | 0 | inline bool isStrictEvalFrame() const { |
458 | 0 | return isEvalFrame() && script()->strict(); |
459 | 0 | } |
460 | | |
461 | 0 | bool isNonStrictEvalFrame() const { |
462 | 0 | return isEvalFrame() && !script()->strict(); |
463 | 0 | } |
464 | | |
465 | | bool isNonGlobalEvalFrame() const; |
466 | | |
467 | 0 | bool isNonStrictDirectEvalFrame() const { |
468 | 0 | return isNonStrictEvalFrame() && isNonGlobalEvalFrame(); |
469 | 0 | } |
470 | | |
471 | | /* |
472 | | * Previous frame |
473 | | * |
474 | | * A frame's 'prev' frame is either null or the previous frame pointed to |
475 | | * by cx->regs->fp when this frame was pushed. Often, given two prev-linked |
476 | | * frames, the next-frame is a function or eval that was called by the |
477 | | * prev-frame, but not always: the prev-frame may have called a native that |
478 | | * reentered the VM through JS_CallFunctionValue on the same context |
479 | | * (without calling JS_SaveFrameChain) which pushed the next-frame. Thus, |
480 | | * 'prev' has little semantic meaning and basically just tells the VM what |
481 | | * to set cx->regs->fp to when this frame is popped. |
482 | | */ |
483 | | |
484 | 94 | InterpreterFrame* prev() const { |
485 | 94 | return prev_; |
486 | 94 | } |
487 | | |
488 | 0 | AbstractFramePtr evalInFramePrev() const { |
489 | 0 | MOZ_ASSERT(isEvalFrame()); |
490 | 0 | return evalInFramePrev_; |
491 | 0 | } |
492 | | |
493 | | /* |
494 | | * (Unaliased) locals and arguments |
495 | | * |
496 | | * Only non-eval function frames have arguments. The arguments pushed by |
497 | | * the caller are the 'actual' arguments. The declared arguments of the |
498 | | * callee are the 'formal' arguments. When the caller passes less actual |
499 | | * arguments, missing formal arguments are padded with |undefined|. |
500 | | * |
501 | | * When a local/formal variable is aliased (accessed by nested closures, |
502 | | * environment operations, or 'arguments'), the canonical location for |
503 | | * that value is the slot of an environment object. Aliased locals don't |
504 | | * have stack slots assigned to them. These functions assert that |
505 | | * accesses to stack values are unaliased. |
506 | | */ |
507 | | |
508 | | inline Value& unaliasedLocal(uint32_t i); |
509 | | |
510 | 0 | bool hasArgs() const { return isFunctionFrame(); } |
511 | | inline Value& unaliasedFormal(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); |
512 | | inline Value& unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING); |
513 | | template <class Op> inline void unaliasedForEachActual(Op op); |
514 | | |
515 | 1 | unsigned numFormalArgs() const { MOZ_ASSERT(hasArgs()); return callee().nargs(); } |
516 | 108 | unsigned numActualArgs() const { MOZ_ASSERT(hasArgs()); return nactual_; } |
517 | | |
518 | | /* Watch out, this exposes a pointer to the unaliased formal arg array. */ |
519 | 1.09k | Value* argv() const { MOZ_ASSERT(hasArgs()); return argv_; } |
520 | | |
521 | | /* |
522 | | * Arguments object |
523 | | * |
524 | | * If a non-eval function has script->needsArgsObj, an arguments object is |
525 | | * created in the prologue and stored in the local variable for the |
526 | | * 'arguments' binding (script->argumentsLocal). Since this local is |
527 | | * mutable, the arguments object can be overwritten and we can "lose" the |
528 | | * arguments object. Thus, InterpreterFrame keeps an explicit argsObj_ field so |
529 | | * that the original arguments object is always available. |
530 | | */ |
531 | | |
532 | | ArgumentsObject& argsObj() const; |
533 | | void initArgsObj(ArgumentsObject& argsobj); |
534 | | |
535 | | ArrayObject* createRestParameter(JSContext* cx); |
536 | | |
537 | | /* |
538 | | * Environment chain |
539 | | * |
540 | | * In theory, the environment chain would contain an object for every |
541 | | * lexical scope. However, only objects that are required for dynamic |
542 | | * lookup are actually created. |
543 | | * |
544 | | * Given that an InterpreterFrame corresponds roughly to a ES Execution |
545 | | * Context (ES 10.3), InterpreterFrame::varObj corresponds to the |
546 | | * VariableEnvironment component of a Exection Context. Intuitively, the |
547 | | * variables object is where new bindings (variables and functions) are |
548 | | * stored. One might expect that this is either the Call object or |
549 | | * envChain.globalObj for function or global code, respectively, however |
550 | | * the JSAPI allows calls of Execute to specify a variables object on the |
551 | | * environment chain other than the call/global object. This allows |
552 | | * embeddings to run multiple scripts under the same global, each time |
553 | | * using a new variables object to collect and discard the script's global |
554 | | * variables. |
555 | | */ |
556 | | |
557 | | inline HandleObject environmentChain() const; |
558 | | |
559 | | inline EnvironmentObject& aliasedEnvironment(EnvironmentCoordinate ec) const; |
560 | | inline GlobalObject& global() const; |
561 | | inline CallObject& callObj() const; |
562 | | inline JSObject& varObj() const; |
563 | | inline LexicalEnvironmentObject& extensibleLexicalEnvironment() const; |
564 | | |
565 | | template <typename SpecificEnvironment> |
566 | | inline void pushOnEnvironmentChain(SpecificEnvironment& env); |
567 | | template <typename SpecificEnvironment> |
568 | | inline void popOffEnvironmentChain(); |
569 | | inline void replaceInnermostEnvironment(EnvironmentObject& env); |
570 | | |
571 | | // Push a VarEnvironmentObject for function frames of functions that have |
572 | | // parameter expressions with closed over var bindings. |
573 | | bool pushVarEnvironment(JSContext* cx, HandleScope scope); |
574 | | |
575 | | /* |
576 | | * For lexical envs with aliased locals, these interfaces push and pop |
577 | | * entries on the environment chain. The "freshen" operation replaces the |
578 | | * current lexical env with a fresh copy of it, to implement semantics |
579 | | * providing distinct bindings per iteration of a for(;;) loop whose head |
580 | | * has a lexical declaration. The "recreate" operation replaces the |
581 | | * current lexical env with a copy of it containing uninitialized |
582 | | * bindings, to implement semantics providing distinct bindings per |
583 | | * iteration of a for-in/of loop. |
584 | | */ |
585 | | |
586 | | bool pushLexicalEnvironment(JSContext* cx, Handle<LexicalScope*> scope); |
587 | | bool freshenLexicalEnvironment(JSContext* cx); |
588 | | bool recreateLexicalEnvironment(JSContext* cx); |
589 | | |
590 | | /* |
591 | | * Script |
592 | | * |
593 | | * All frames have an associated JSScript which holds the bytecode being |
594 | | * executed for the frame. |
595 | | */ |
596 | | |
597 | 1.81k | JSScript* script() const { |
598 | 1.81k | return script_; |
599 | 1.81k | } |
600 | | |
601 | | /* Return the previous frame's pc. */ |
602 | 94 | jsbytecode* prevpc() { |
603 | 94 | MOZ_ASSERT(prev_); |
604 | 94 | return prevpc_; |
605 | 94 | } |
606 | | |
607 | | /* Return the previous frame's sp. */ |
608 | 94 | Value* prevsp() { |
609 | 94 | MOZ_ASSERT(prev_); |
610 | 94 | return prevsp_; |
611 | 94 | } |
612 | | |
613 | | /* |
614 | | * Return the 'this' argument passed to a non-eval function frame. This is |
615 | | * not necessarily the frame's this-binding, for instance non-strict |
616 | | * functions will box primitive 'this' values and thisArgument() will |
617 | | * return the original, unboxed Value. |
618 | | */ |
619 | 77 | Value& thisArgument() const { |
620 | 77 | MOZ_ASSERT(isFunctionFrame()); |
621 | 77 | return argv()[-1]; |
622 | 77 | } |
623 | | |
624 | | /* |
625 | | * Callee |
626 | | * |
627 | | * Only function frames have a callee. An eval frame in a function has the |
628 | | * same callee as its containing function frame. |
629 | | */ |
630 | | |
631 | 488 | JSFunction& callee() const { |
632 | 488 | MOZ_ASSERT(isFunctionFrame()); |
633 | 488 | return calleev().toObject().as<JSFunction>(); |
634 | 488 | } |
635 | | |
636 | 488 | const Value& calleev() const { |
637 | 488 | MOZ_ASSERT(isFunctionFrame()); |
638 | 488 | return argv()[-2]; |
639 | 488 | } |
640 | | |
641 | | /* |
642 | | * New Target |
643 | | * |
644 | | * Only function frames have a meaningful newTarget. An eval frame in a |
645 | | * function will have a copy of the newTarget of the enclosing function |
646 | | * frame. |
647 | | */ |
648 | 3 | Value newTarget() const { |
649 | 3 | if (isEvalFrame()) { |
650 | 0 | return ((Value*)this)[-1]; |
651 | 0 | } |
652 | 3 | |
653 | 3 | MOZ_ASSERT(isFunctionFrame()); |
654 | 3 | |
655 | 3 | if (callee().isArrow()) { |
656 | 0 | return callee().getExtendedSlot(FunctionExtended::ARROW_NEWTARGET_SLOT); |
657 | 0 | } |
658 | 3 | |
659 | 3 | if (isConstructing()) { |
660 | 0 | unsigned pushedArgs = Max(numFormalArgs(), numActualArgs()); |
661 | 0 | return argv()[pushedArgs]; |
662 | 0 | } |
663 | 3 | return UndefinedValue(); |
664 | 3 | } |
665 | | |
666 | | /* Profiler flags */ |
667 | | |
668 | 760 | bool hasPushedGeckoProfilerFrame() { |
669 | 760 | return !!(flags_ & HAS_PUSHED_PROF_FRAME); |
670 | 760 | } |
671 | | |
672 | 0 | void setPushedGeckoProfilerFrame() { |
673 | 0 | flags_ |= HAS_PUSHED_PROF_FRAME; |
674 | 0 | } |
675 | | |
676 | 0 | void unsetPushedGeckoProfilerFrame() { |
677 | 0 | flags_ &= ~HAS_PUSHED_PROF_FRAME; |
678 | 0 | } |
679 | | |
680 | | /* Return value */ |
681 | | |
682 | 166 | bool hasReturnValue() const { |
683 | 166 | return flags_ & HAS_RVAL; |
684 | 166 | } |
685 | | |
686 | 165 | MutableHandleValue returnValue() { |
687 | 165 | if (!hasReturnValue()) { |
688 | 28 | rval_.setUndefined(); |
689 | 28 | } |
690 | 165 | return MutableHandleValue::fromMarkedLocation(&rval_); |
691 | 165 | } |
692 | | |
693 | 194 | void markReturnValue() { |
694 | 194 | flags_ |= HAS_RVAL; |
695 | 194 | } |
696 | | |
697 | 194 | void setReturnValue(const Value& v) { |
698 | 194 | rval_ = v; |
699 | 194 | markReturnValue(); |
700 | 194 | } |
701 | | |
702 | 0 | void clearReturnValue() { |
703 | 0 | rval_.setUndefined(); |
704 | 0 | markReturnValue(); |
705 | 0 | } |
706 | | |
707 | 0 | void resumeGeneratorFrame(JSObject* envChain) { |
708 | 0 | MOZ_ASSERT(script()->isGenerator() || script()->isAsync()); |
709 | 0 | MOZ_ASSERT(isFunctionFrame()); |
710 | 0 | flags_ |= HAS_INITIAL_ENV; |
711 | 0 | envChain_ = envChain; |
712 | 0 | } |
713 | | |
714 | | /* |
715 | | * Other flags |
716 | | */ |
717 | | |
718 | 244 | bool isConstructing() const { |
719 | 244 | return !!(flags_ & CONSTRUCTING); |
720 | 244 | } |
721 | | |
722 | 0 | void setResumedGenerator() { |
723 | 0 | flags_ |= RESUMED_GENERATOR; |
724 | 0 | } |
725 | 94 | bool isResumedGenerator() const { |
726 | 94 | return !!(flags_ & RESUMED_GENERATOR); |
727 | 94 | } |
728 | | |
729 | | /* |
730 | | * These two queries should not be used in general: the presence/absence of |
731 | | * the call/args object is determined by the static(ish) properties of the |
732 | | * JSFunction/JSScript. These queries should only be performed when probing |
733 | | * a stack frame that may be in the middle of the prologue (during which |
734 | | * time the call/args object are created). |
735 | | */ |
736 | | |
737 | | inline bool hasInitialEnvironment() const; |
738 | | |
739 | 1 | bool hasInitialEnvironmentUnchecked() const { |
740 | 1 | return flags_ & HAS_INITIAL_ENV; |
741 | 1 | } |
742 | | |
743 | 0 | bool hasArgsObj() const { |
744 | 0 | MOZ_ASSERT(script()->needsArgsObj()); |
745 | 0 | return flags_ & HAS_ARGS_OBJ; |
746 | 0 | } |
747 | | |
748 | | /* |
749 | | * Debugger eval frames. |
750 | | * |
751 | | * - If evalInFramePrev_ is non-null, frame was created for an "eval in |
752 | | * frame" call, which can push a successor to any live frame; so its |
753 | | * logical "prev" frame is not necessarily the previous frame in memory. |
754 | | * Iteration should treat evalInFramePrev_ as this frame's previous frame. |
755 | | * |
756 | | * - Don't bother to JIT it, because it's probably short-lived. |
757 | | * |
758 | | * - It is required to have a environment chain object outside the |
759 | | * js::EnvironmentObject hierarchy: either a global object, or a |
760 | | * DebugEnvironmentProxy. |
761 | | */ |
762 | 12 | bool isDebuggerEvalFrame() const { |
763 | 12 | return isEvalFrame() && !!evalInFramePrev_; |
764 | 12 | } |
765 | | |
766 | 0 | bool prevUpToDate() const { |
767 | 0 | return !!(flags_ & PREV_UP_TO_DATE); |
768 | 0 | } |
769 | | |
770 | 0 | void setPrevUpToDate() { |
771 | 0 | flags_ |= PREV_UP_TO_DATE; |
772 | 0 | } |
773 | | |
774 | 0 | void unsetPrevUpToDate() { |
775 | 0 | flags_ &= ~PREV_UP_TO_DATE; |
776 | 0 | } |
777 | | |
778 | 324 | bool isDebuggee() const { |
779 | 324 | return !!(flags_ & DEBUGGEE); |
780 | 324 | } |
781 | | |
782 | 0 | void setIsDebuggee() { |
783 | 0 | flags_ |= DEBUGGEE; |
784 | 0 | } |
785 | | |
786 | | inline void unsetIsDebuggee(); |
787 | | |
788 | 0 | bool hasCachedSavedFrame() const { |
789 | 0 | return flags_ & HAS_CACHED_SAVED_FRAME; |
790 | 0 | } |
791 | 0 | void setHasCachedSavedFrame() { |
792 | 0 | flags_ |= HAS_CACHED_SAVED_FRAME; |
793 | 0 | } |
794 | 0 | void clearHasCachedSavedFrame() { |
795 | 0 | flags_ &= ~HAS_CACHED_SAVED_FRAME; |
796 | 0 | } |
797 | | |
798 | | public: |
799 | | void trace(JSTracer* trc, Value* sp, jsbytecode* pc); |
800 | | void traceValues(JSTracer* trc, unsigned start, unsigned end); |
801 | | |
802 | | // Entered Baseline/Ion from the interpreter. |
803 | 8 | bool runningInJit() const { |
804 | 8 | return !!(flags_ & RUNNING_IN_JIT); |
805 | 8 | } |
806 | 1 | void setRunningInJit() { |
807 | 1 | flags_ |= RUNNING_IN_JIT; |
808 | 1 | } |
809 | 1 | void clearRunningInJit() { |
810 | 1 | flags_ &= ~RUNNING_IN_JIT; |
811 | 1 | } |
812 | | }; |
813 | | |
814 | | /*****************************************************************************/ |
815 | | |
816 | | class InterpreterRegs |
817 | | { |
818 | | public: |
819 | | Value* sp; |
820 | | jsbytecode* pc; |
821 | | private: |
822 | | InterpreterFrame* fp_; |
823 | | public: |
824 | 6.93k | InterpreterFrame* fp() const { return fp_; } |
825 | | |
826 | 1 | unsigned stackDepth() const { |
827 | 1 | MOZ_ASSERT(sp >= fp_->base()); |
828 | 1 | return sp - fp_->base(); |
829 | 1 | } |
830 | | |
831 | 0 | Value* spForStackDepth(unsigned depth) const { |
832 | 0 | MOZ_ASSERT(fp_->script()->nfixed() + depth <= fp_->script()->nslots()); |
833 | 0 | return fp_->base() + depth; |
834 | 0 | } |
835 | | |
836 | | /* For generators. */ |
837 | 0 | void rebaseFromTo(const InterpreterRegs& from, InterpreterFrame& to) { |
838 | 0 | fp_ = &to; |
839 | 0 | sp = to.slots() + (from.sp - from.fp_->slots()); |
840 | 0 | pc = from.pc; |
841 | 0 | MOZ_ASSERT(fp_); |
842 | 0 | } |
843 | | |
844 | 94 | void popInlineFrame() { |
845 | 94 | pc = fp_->prevpc(); |
846 | 94 | unsigned spForNewTarget = fp_->isResumedGenerator() ? 0 : fp_->isConstructing(); |
847 | 94 | sp = fp_->prevsp() - fp_->numActualArgs() - 1 - spForNewTarget; |
848 | 94 | fp_ = fp_->prev(); |
849 | 94 | MOZ_ASSERT(fp_); |
850 | 94 | } |
851 | 155 | void prepareToRun(InterpreterFrame& fp, JSScript* script) { |
852 | 155 | pc = script->code(); |
853 | 155 | sp = fp.slots() + script->nfixed(); |
854 | 155 | fp_ = &fp; |
855 | 155 | } |
856 | | |
857 | | void setToEndOfScript(); |
858 | | |
859 | 1.65k | MutableHandleValue stackHandleAt(int i) { |
860 | 1.65k | return MutableHandleValue::fromMarkedLocation(&sp[i]); |
861 | 1.65k | } |
862 | | |
863 | 0 | HandleValue stackHandleAt(int i) const { |
864 | 0 | return HandleValue::fromMarkedLocation(&sp[i]); |
865 | 0 | } |
866 | | |
867 | | friend void GDBTestInitInterpreterRegs(InterpreterRegs&, js::InterpreterFrame*, |
868 | | JS::Value*, uint8_t*); |
869 | | }; |
870 | | |
871 | | /*****************************************************************************/ |
872 | | |
873 | | class InterpreterStack |
874 | | { |
875 | | friend class InterpreterActivation; |
876 | | |
877 | | static const size_t DEFAULT_CHUNK_SIZE = 4 * 1024; |
878 | | LifoAlloc allocator_; |
879 | | |
880 | | // Number of interpreter frames on the stack, for over-recursion checks. |
881 | | static const size_t MAX_FRAMES = 50 * 1000; |
882 | | static const size_t MAX_FRAMES_TRUSTED = MAX_FRAMES + 1000; |
883 | | size_t frameCount_; |
884 | | |
885 | | inline uint8_t* allocateFrame(JSContext* cx, size_t size); |
886 | | |
887 | | inline InterpreterFrame* |
888 | | getCallFrame(JSContext* cx, const CallArgs& args, HandleScript script, |
889 | | MaybeConstruct constructing, Value** pargv); |
890 | | |
891 | 155 | void releaseFrame(InterpreterFrame* fp) { |
892 | 155 | frameCount_--; |
893 | 155 | allocator_.release(fp->mark_); |
894 | 155 | } |
895 | | |
896 | | public: |
897 | | InterpreterStack() |
898 | | : allocator_(DEFAULT_CHUNK_SIZE), |
899 | | frameCount_(0) |
900 | 27 | { } |
901 | | |
902 | 0 | ~InterpreterStack() { |
903 | 0 | MOZ_ASSERT(frameCount_ == 0); |
904 | 0 | } |
905 | | |
906 | | // For execution of eval or global code. |
907 | | InterpreterFrame* pushExecuteFrame(JSContext* cx, HandleScript script, |
908 | | const Value& newTargetValue, HandleObject envChain, |
909 | | AbstractFramePtr evalInFrame); |
910 | | |
911 | | // Called to invoke a function. |
912 | | InterpreterFrame* pushInvokeFrame(JSContext* cx, const CallArgs& args, |
913 | | MaybeConstruct constructing); |
914 | | |
915 | | // The interpreter can push light-weight, "inline" frames without entering a |
916 | | // new InterpreterActivation or recursively calling Interpret. |
917 | | bool pushInlineFrame(JSContext* cx, InterpreterRegs& regs, const CallArgs& args, |
918 | | HandleScript script, MaybeConstruct constructing); |
919 | | |
920 | | void popInlineFrame(InterpreterRegs& regs); |
921 | | |
922 | | bool resumeGeneratorCallFrame(JSContext* cx, InterpreterRegs& regs, |
923 | | HandleFunction callee, HandleObject envChain); |
924 | | |
925 | | inline void purge(JSRuntime* rt); |
926 | | |
927 | 0 | size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const { |
928 | 0 | return allocator_.sizeOfExcludingThis(mallocSizeOf); |
929 | 0 | } |
930 | | }; |
931 | | |
932 | | void TraceInterpreterActivations(JSContext* cx, JSTracer* trc); |
933 | | |
934 | | /*****************************************************************************/ |
935 | | |
936 | | /** Base class for all function call args. */ |
937 | | class AnyInvokeArgs : public JS::CallArgs |
938 | | { |
939 | | }; |
940 | | |
941 | | /** Base class for all function construction args. */ |
942 | | class AnyConstructArgs : public JS::CallArgs |
943 | | { |
944 | | // Only js::Construct (or internal methods that call the qualified CallArgs |
945 | | // versions) should do these things! |
946 | | void setCallee(const Value& v) = delete; |
947 | | void setThis(const Value& v) = delete; |
948 | | MutableHandleValue newTarget() const = delete; |
949 | | MutableHandleValue rval() const = delete; |
950 | | }; |
951 | | |
952 | | namespace detail { |
953 | | |
954 | | /** Function call/construct args of statically-unknown count. */ |
955 | | template <MaybeConstruct Construct> |
956 | | class GenericArgsBase |
957 | | : public mozilla::Conditional<Construct, AnyConstructArgs, AnyInvokeArgs>::Type |
958 | | { |
959 | | protected: |
960 | | AutoValueVector v_; |
961 | | |
962 | 6.45M | explicit GenericArgsBase(JSContext* cx) : v_(cx) {} js::detail::GenericArgsBase<(js::MaybeConstruct)0>::GenericArgsBase(JSContext*) Line | Count | Source | 962 | 6.45M | explicit GenericArgsBase(JSContext* cx) : v_(cx) {} |
Unexecuted instantiation: js::detail::GenericArgsBase<(js::MaybeConstruct)1>::GenericArgsBase(JSContext*) |
963 | | |
964 | | public: |
965 | 6.45M | bool init(JSContext* cx, unsigned argc) { |
966 | 6.45M | if (argc > ARGS_LENGTH_MAX) { |
967 | 0 | JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_MANY_ARGUMENTS); |
968 | 0 | return false; |
969 | 0 | } |
970 | 6.45M | |
971 | 6.45M | // callee, this, arguments[, new.target iff constructing] |
972 | 6.45M | size_t len = 2 + argc + uint32_t(Construct); |
973 | 6.45M | MOZ_ASSERT(len > argc); // no overflow |
974 | 6.45M | if (!v_.resize(len)) { |
975 | 0 | return false; |
976 | 0 | } |
977 | 6.45M | |
978 | 6.45M | *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(argc, v_.begin()); |
979 | 6.45M | this->constructing_ = Construct; |
980 | 6.45M | if (Construct) { |
981 | 0 | this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING)); |
982 | 0 | } |
983 | 6.45M | return true; |
984 | 6.45M | } js::detail::GenericArgsBase<(js::MaybeConstruct)0>::init(JSContext*, unsigned int) Line | Count | Source | 965 | 6.45M | bool init(JSContext* cx, unsigned argc) { | 966 | 6.45M | if (argc > ARGS_LENGTH_MAX) { | 967 | 0 | JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TOO_MANY_ARGUMENTS); | 968 | 0 | return false; | 969 | 0 | } | 970 | 6.45M | | 971 | 6.45M | // callee, this, arguments[, new.target iff constructing] | 972 | 6.45M | size_t len = 2 + argc + uint32_t(Construct); | 973 | 6.45M | MOZ_ASSERT(len > argc); // no overflow | 974 | 6.45M | if (!v_.resize(len)) { | 975 | 0 | return false; | 976 | 0 | } | 977 | 6.45M | | 978 | 6.45M | *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(argc, v_.begin()); | 979 | 6.45M | this->constructing_ = Construct; | 980 | 6.45M | if (Construct) { | 981 | 0 | this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING)); | 982 | 0 | } | 983 | 6.45M | return true; | 984 | 6.45M | } |
Unexecuted instantiation: js::detail::GenericArgsBase<(js::MaybeConstruct)1>::init(JSContext*, unsigned int) |
985 | | }; |
986 | | |
987 | | /** Function call/construct args of statically-known count. */ |
988 | | template <MaybeConstruct Construct, size_t N> |
989 | | class FixedArgsBase |
990 | | : public mozilla::Conditional<Construct, AnyConstructArgs, AnyInvokeArgs>::Type |
991 | | { |
992 | | static_assert(N <= ARGS_LENGTH_MAX, "o/~ too many args o/~"); |
993 | | |
994 | | protected: |
995 | | JS::AutoValueArray<2 + N + uint32_t(Construct)> v_; |
996 | | |
997 | 4 | explicit FixedArgsBase(JSContext* cx) : v_(cx) { |
998 | 4 | *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(N, v_.begin()); |
999 | 4 | this->constructing_ = Construct; |
1000 | 4 | if (Construct) { |
1001 | 0 | this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING)); |
1002 | 0 | } |
1003 | 4 | } Unexecuted instantiation: js::detail::FixedArgsBase<(js::MaybeConstruct)0, 2ul>::FixedArgsBase(JSContext*) Unexecuted instantiation: js::detail::FixedArgsBase<(js::MaybeConstruct)1, 1ul>::FixedArgsBase(JSContext*) js::detail::FixedArgsBase<(js::MaybeConstruct)0, 1ul>::FixedArgsBase(JSContext*) Line | Count | Source | 997 | 1 | explicit FixedArgsBase(JSContext* cx) : v_(cx) { | 998 | 1 | *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(N, v_.begin()); | 999 | 1 | this->constructing_ = Construct; | 1000 | 1 | if (Construct) { | 1001 | 0 | this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING)); | 1002 | 0 | } | 1003 | 1 | } |
Unexecuted instantiation: js::detail::FixedArgsBase<(js::MaybeConstruct)1, 3ul>::FixedArgsBase(JSContext*) js::detail::FixedArgsBase<(js::MaybeConstruct)0, 0ul>::FixedArgsBase(JSContext*) Line | Count | Source | 997 | 3 | explicit FixedArgsBase(JSContext* cx) : v_(cx) { | 998 | 3 | *static_cast<JS::CallArgs*>(this) = CallArgsFromVp(N, v_.begin()); | 999 | 3 | this->constructing_ = Construct; | 1000 | 3 | if (Construct) { | 1001 | 0 | this->CallArgs::setThis(MagicValue(JS_IS_CONSTRUCTING)); | 1002 | 0 | } | 1003 | 3 | } |
Unexecuted instantiation: js::detail::FixedArgsBase<(js::MaybeConstruct)0, 3ul>::FixedArgsBase(JSContext*) Unexecuted instantiation: js::detail::FixedArgsBase<(js::MaybeConstruct)0, 5ul>::FixedArgsBase(JSContext*) Unexecuted instantiation: js::detail::FixedArgsBase<(js::MaybeConstruct)0, 4ul>::FixedArgsBase(JSContext*) |
1004 | | }; |
1005 | | |
1006 | | } // namespace detail |
1007 | | |
1008 | | /** Function call args of statically-unknown count. */ |
1009 | | class InvokeArgs : public detail::GenericArgsBase<NO_CONSTRUCT> |
1010 | | { |
1011 | | using Base = detail::GenericArgsBase<NO_CONSTRUCT>; |
1012 | | |
1013 | | public: |
1014 | 1.62M | explicit InvokeArgs(JSContext* cx) : Base(cx) {} |
1015 | | }; |
1016 | | |
1017 | | /** Function call args of statically-unknown count. */ |
1018 | | class InvokeArgsMaybeIgnoresReturnValue : public detail::GenericArgsBase<NO_CONSTRUCT> |
1019 | | { |
1020 | | using Base = detail::GenericArgsBase<NO_CONSTRUCT>; |
1021 | | |
1022 | | public: |
1023 | 4.82M | explicit InvokeArgsMaybeIgnoresReturnValue(JSContext* cx, bool ignoresReturnValue) : Base(cx) { |
1024 | 4.82M | this->ignoresReturnValue_ = ignoresReturnValue; |
1025 | 4.82M | } |
1026 | | }; |
1027 | | |
1028 | | /** Function call args of statically-known count. */ |
1029 | | template <size_t N> |
1030 | | class FixedInvokeArgs : public detail::FixedArgsBase<NO_CONSTRUCT, N> |
1031 | | { |
1032 | | using Base = detail::FixedArgsBase<NO_CONSTRUCT, N>; |
1033 | | |
1034 | | public: |
1035 | 4 | explicit FixedInvokeArgs(JSContext* cx) : Base(cx) {} Unexecuted instantiation: js::FixedInvokeArgs<2ul>::FixedInvokeArgs(JSContext*) js::FixedInvokeArgs<1ul>::FixedInvokeArgs(JSContext*) Line | Count | Source | 1035 | 1 | explicit FixedInvokeArgs(JSContext* cx) : Base(cx) {} |
js::FixedInvokeArgs<0ul>::FixedInvokeArgs(JSContext*) Line | Count | Source | 1035 | 3 | explicit FixedInvokeArgs(JSContext* cx) : Base(cx) {} |
Unexecuted instantiation: js::FixedInvokeArgs<3ul>::FixedInvokeArgs(JSContext*) Unexecuted instantiation: js::FixedInvokeArgs<5ul>::FixedInvokeArgs(JSContext*) Unexecuted instantiation: js::FixedInvokeArgs<4ul>::FixedInvokeArgs(JSContext*) |
1036 | | }; |
1037 | | |
1038 | | /** Function construct args of statically-unknown count. */ |
1039 | | class ConstructArgs : public detail::GenericArgsBase<CONSTRUCT> |
1040 | | { |
1041 | | using Base = detail::GenericArgsBase<CONSTRUCT>; |
1042 | | |
1043 | | public: |
1044 | 0 | explicit ConstructArgs(JSContext* cx) : Base(cx) {} |
1045 | | }; |
1046 | | |
1047 | | /** Function call args of statically-known count. */ |
1048 | | template <size_t N> |
1049 | | class FixedConstructArgs : public detail::FixedArgsBase<CONSTRUCT, N> |
1050 | | { |
1051 | | using Base = detail::FixedArgsBase<CONSTRUCT, N>; |
1052 | | |
1053 | | public: |
1054 | 0 | explicit FixedConstructArgs(JSContext* cx) : Base(cx) {} Unexecuted instantiation: js::FixedConstructArgs<1ul>::FixedConstructArgs(JSContext*) Unexecuted instantiation: js::FixedConstructArgs<3ul>::FixedConstructArgs(JSContext*) |
1055 | | }; |
1056 | | |
1057 | | template <class Args, class Arraylike> |
1058 | | inline bool |
1059 | | FillArgumentsFromArraylike(JSContext* cx, Args& args, const Arraylike& arraylike) |
1060 | 1.62M | { |
1061 | 1.62M | uint32_t len = arraylike.length(); |
1062 | 1.62M | if (!args.init(cx, len)) { |
1063 | 0 | return false; |
1064 | 0 | } |
1065 | 1.62M | |
1066 | 6.49M | for (uint32_t i = 0; i < len; i++) { |
1067 | 4.87M | args[i].set(arraylike[i]); |
1068 | 4.87M | } |
1069 | 1.62M | |
1070 | 1.62M | return true; |
1071 | 1.62M | } bool js::FillArgumentsFromArraylike<js::InvokeArgs, JS::HandleValueArray>(JSContext*, js::InvokeArgs&, JS::HandleValueArray const&) Line | Count | Source | 1060 | 1.62M | { | 1061 | 1.62M | uint32_t len = arraylike.length(); | 1062 | 1.62M | if (!args.init(cx, len)) { | 1063 | 0 | return false; | 1064 | 0 | } | 1065 | 1.62M | | 1066 | 6.49M | for (uint32_t i = 0; i < len; i++) { | 1067 | 4.87M | args[i].set(arraylike[i]); | 1068 | 4.87M | } | 1069 | 1.62M | | 1070 | 1.62M | return true; | 1071 | 1.62M | } |
Unexecuted instantiation: bool js::FillArgumentsFromArraylike<js::ConstructArgs, JS::HandleValueArray>(JSContext*, js::ConstructArgs&, JS::HandleValueArray const&) Unexecuted instantiation: bool js::FillArgumentsFromArraylike<js::InvokeArgs, JS::CallArgs>(JSContext*, js::InvokeArgs&, JS::CallArgs const&) Unexecuted instantiation: bool js::FillArgumentsFromArraylike<js::ConstructArgs, JS::CallArgs>(JSContext*, js::ConstructArgs&, JS::CallArgs const&) |
1072 | | |
1073 | | } // namespace js |
1074 | | |
1075 | | namespace mozilla { |
1076 | | |
1077 | | template <> |
1078 | | struct DefaultHasher<js::AbstractFramePtr> { |
1079 | | typedef js::AbstractFramePtr Lookup; |
1080 | | |
1081 | 0 | static js::HashNumber hash(const Lookup& key) { |
1082 | 0 | return mozilla::HashGeneric(key.raw()); |
1083 | 0 | } |
1084 | | |
1085 | 0 | static bool match(const js::AbstractFramePtr& k, const Lookup& l) { |
1086 | 0 | return k == l; |
1087 | 0 | } |
1088 | | }; |
1089 | | |
1090 | | } // namespace mozilla |
1091 | | |
1092 | | namespace js { |
1093 | | |
1094 | | /*****************************************************************************/ |
1095 | | |
1096 | | // SavedFrame caching to minimize stack walking. |
1097 | | // |
1098 | | // Since each SavedFrame object includes a 'parent' pointer to the SavedFrame |
1099 | | // for its caller, if we could easily find the right SavedFrame for a given |
1100 | | // stack frame, we wouldn't need to walk the rest of the stack. Traversing deep |
1101 | | // stacks can be expensive, and when we're profiling or instrumenting code, we |
1102 | | // may want to capture JavaScript stacks frequently, so such cases would benefit |
1103 | | // if we could avoid walking the entire stack. |
1104 | | // |
1105 | | // We could have a cache mapping frame addresses to their SavedFrame objects, |
1106 | | // but invalidating its entries would be a challenge. Popping a stack frame is |
1107 | | // extremely performance-sensitive, and SpiderMonkey stack frames can be OSR'd, |
1108 | | // thrown, rematerialized, and perhaps meet other fates; we would rather our |
1109 | | // cache not depend on handling so many tricky cases. |
1110 | | // |
1111 | | // It turns out that we can keep the cache accurate by reserving a single bit in |
1112 | | // the stack frame, which must be clear on any newly pushed frame. When we |
1113 | | // insert an entry into the cache mapping a given frame address to its |
1114 | | // SavedFrame, we set the bit in the frame. Then, we take care to probe the |
1115 | | // cache only for frames whose bit is set; the bit tells us that the frame has |
1116 | | // never left the stack, so its cache entry must be accurate, at least about |
1117 | | // which function the frame is executing (the line may have changed; more about |
1118 | | // that below). The code refers to this bit as the 'hasCachedSavedFrame' flag. |
1119 | | // |
1120 | | // We could manage such a cache replacing least-recently used entries, but we |
1121 | | // can do better than that: the cache can be a stack, of which we need examine |
1122 | | // only entries from the top. |
1123 | | // |
1124 | | // First, observe that stacks are walked from the youngest frame to the oldest, |
1125 | | // but SavedFrame chains are built from oldest to youngest, to ensure common |
1126 | | // tails are shared. This means that capturing a stack is necessarily a |
1127 | | // two-phase process: walk the stack, and then build the SavedFrames. |
1128 | | // |
1129 | | // Naturally, the first time we capture the stack, the cache is empty, and we |
1130 | | // must traverse the entire stack. As we build each SavedFrame, we push an entry |
1131 | | // associating the frame's address to its SavedFrame on the cache, and set the |
1132 | | // frame's bit. At the end, every frame has its bit set and an entry in the |
1133 | | // cache. |
1134 | | // |
1135 | | // Then the program runs some more. Some, none, or all of the frames are popped. |
1136 | | // Any new frames are pushed with their bit clear. Any frame with its bit set |
1137 | | // has never left the stack. The cache is left untouched. |
1138 | | // |
1139 | | // For the next capture, we walk the stack up to the first frame with its bit |
1140 | | // set, if there is one. Call it F; it must have a cache entry. We pop entries |
1141 | | // from the cache - all invalid, because they are above F's entry, and hence |
1142 | | // younger - until we find the entry matching F's address. Since F's bit is set, |
1143 | | // we know it never left the stack, and hence that no younger frame could have |
1144 | | // had a colliding address. And since the frame's bit was set when we pushed the |
1145 | | // cache entry, we know the entry is still valid. |
1146 | | // |
1147 | | // F's cache entry's SavedFrame covers the rest of the stack, so we don't need |
1148 | | // to walk the stack any further. Now we begin building SavedFrame objects for |
1149 | | // the new frames, pushing cache entries, and setting bits on the frames. By the |
1150 | | // end, the cache again covers the full stack, and every frame's bit is set. |
1151 | | // |
1152 | | // If we walk the stack to the end, and find no frame with its bit set, then the |
1153 | | // entire cache is invalid. At this point, it must be emptied, so that the new |
1154 | | // entries we are about to push are the only frames in the cache. |
1155 | | // |
1156 | | // For example, suppose we have the following stack (let 'A > B' mean "A called |
1157 | | // B", so the frames are listed oldest first): |
1158 | | // |
1159 | | // P > Q > R > S Initial stack, bits not set. |
1160 | | // P* > Q* > R* > S* Capture a SavedFrame stack, set bits. |
1161 | | // The cache now holds: P > Q > R > S. |
1162 | | // P* > Q* > R* Return from S. |
1163 | | // P* > Q* Return from R. |
1164 | | // P* > Q* > T > U Call T and U. New frames have clear bits. |
1165 | | // |
1166 | | // If we capture the stack now, the cache still holds: |
1167 | | // |
1168 | | // P > Q > R > S |
1169 | | // |
1170 | | // As we traverse the stack, we'll cross U and T, and then find Q with its bit |
1171 | | // set. We pop entries from the cache until we find the entry for Q; this |
1172 | | // removes entries R and S, which were indeed invalid. In Q's cache entry, we |
1173 | | // find the SavedFrame representing the stack P > Q. Now we build SavedFrames |
1174 | | // for the new portion of the stack, pushing an entry for T and setting the bit |
1175 | | // on the frame, and then doing the same for U. In the end, the call stack again |
1176 | | // has bits set on all its frames: |
1177 | | // |
1178 | | // P* > Q* > T* > U* All frames are now in the cache. |
1179 | | // |
1180 | | // And the cache again holds entries for the entire stack: |
1181 | | // |
1182 | | // P > Q > T > U |
1183 | | // |
1184 | | // Some details: |
1185 | | // |
1186 | | // - When we find a cache entry whose frame address matches our frame F, we know |
1187 | | // that F has never left the stack, but it may certainly be the case that |
1188 | | // execution took place in that frame, and that the current source position |
1189 | | // within F's function has changed. This means that the entry's SavedFrame, |
1190 | | // which records the source line and column as well as the function, is not |
1191 | | // correct. To detect this case, when we push a cache entry, we record the |
1192 | | // frame's pc. When consulting the cache, if a frame's address matches but its |
1193 | | // pc does not, then we pop the cache entry and continue walking the stack. |
1194 | | // The next stack frame will definitely hit: since its callee frame never left |
1195 | | // the stack, the calling frame never got the chance to execute. |
1196 | | // |
1197 | | // - Generators, at least conceptually, have long-lived stack frames that |
1198 | | // disappear from the stack when the generator yields, and reappear on the |
1199 | | // stack when the generator's 'next' method is called. When a generator's |
1200 | | // frame is placed again atop the stack, its bit must be cleared - for the |
1201 | | // purposes of the cache, treating the frame as a new frame - to respect the |
1202 | | // invariants we used to justify the algorithm above. Async function |
1203 | | // activations usually appear atop empty stacks, since they are invoked as a |
1204 | | // promise callback, but the same rule applies. |
1205 | | // |
1206 | | // - SpiderMonkey has many types of stack frames, and not all have a place to |
1207 | | // store a bit indicating a cached SavedFrame. But as long as we don't create |
1208 | | // cache entries for frames we can't mark, simply omitting them from the cache |
1209 | | // is harmless. Uncacheable frame types include inlined Ion frames and |
1210 | | // non-Debug wasm frames. The LiveSavedFrameCache::FramePtr type represents |
1211 | | // only pointers to frames that can be cached, so if you have a FramePtr, you |
1212 | | // don't need to further check the frame for cachability. FramePtr provides |
1213 | | // access to the hasCachedSavedFrame bit. |
1214 | | // |
1215 | | // - We actually break up the cache into one cache per Activation. Popping an |
1216 | | // activation invalidates all its cache entries, simply by freeing the cache |
1217 | | // altogether. |
1218 | | // |
1219 | | // - The entire chain of SavedFrames for a given stack capture is created in the |
1220 | | // compartment of the code that requested the capture, *not* in that of the |
1221 | | // frames it represents, so in general, different compartments may have |
1222 | | // different SavedFrame objects representing the same actual stack frame. The |
1223 | | // LiveSavedFrameCache simply records whichever SavedFrames were created most |
1224 | | // recently. When we find a cache hit, we check the entry's SavedFrame's |
1225 | | // compartment against the current compartment; if they do not match, we flush |
1226 | | // the entire cache. This means that it is not always true that, if a frame's |
1227 | | // bit is set, it must have an entry in the cache. But we can still assert |
1228 | | // that, if a frame's bit is set and the cache is not completely empty, the |
1229 | | // frame will have an entry. When the cache is flushed, it will be repopulated |
1230 | | // immediately with the new capture's frames. |
1231 | | // |
1232 | | // - When the Debugger API evaluates an expression in some frame (the 'target |
1233 | | // frame'), it's SpiderMonkey's convention that the target frame be treated as |
1234 | | // the parent of the eval frame. In reality, of course, the eval frame is |
1235 | | // pushed on the top of the stack like any other frame, but stack captures |
1236 | | // simply jump straight over the intervening frames, so that the '.parent' |
1237 | | // property of a SavedFrame for the eval is the SavedFrame for the target. |
1238 | | // This is arranged by giving the eval frame an 'evalInFramePrev` link |
1239 | | // pointing to the target, which an ordinary FrameIter will notice and |
1240 | | // respect. |
1241 | | // |
1242 | | // If the LiveSavedFrameCache were presented with stack traversals that |
1243 | | // skipped frames in this way, it would cause havoc. First, with no debugger |
1244 | | // eval frames present, capture the stack, populating the cache. Then push a |
1245 | | // debugger eval frame and capture again; the skipped frames to appear to be |
1246 | | // absent from the stack. Now pop the debugger eval frame, and capture a third |
1247 | | // time: the no-longer-skipped frames seem to reappear on the stack, with |
1248 | | // their cached bits still set. |
1249 | | // |
1250 | | // The LiveSavedFrameCache assumes that the stack it sees is used in a |
1251 | | // stack-like fashion: if a frame has its bit set, it has never left the |
1252 | | // stack. To support this assumption, when the cache is in use, we do not skip |
1253 | | // the frames between a debugger eval frame an its target; we always traverse |
1254 | | // the entire stack, invalidating and populating the cache in the usual way. |
1255 | | // Instead, when we construct a SavedFrame for a debugger eval frame, we |
1256 | | // select the appropriate parent at that point: rather than the next-older |
1257 | | // frame, we find the SavedFrame for the eval's target frame. The skip appears |
1258 | | // in the SavedFrame chains, even as the traversal covers all the frames. |
1259 | | class LiveSavedFrameCache |
1260 | | { |
1261 | | public: |
1262 | | // The address of a live frame for which we can cache SavedFrames: it has a |
1263 | | // 'hasCachedSavedFrame' bit we can examine and set, and can be converted to |
1264 | | // a Key to index the cache. |
1265 | | class FramePtr { |
1266 | | // We use jit::CommonFrameLayout for both Baseline frames and Ion |
1267 | | // physical frames. |
1268 | | using Ptr = mozilla::Variant<InterpreterFrame*, |
1269 | | jit::CommonFrameLayout*, |
1270 | | jit::RematerializedFrame*, |
1271 | | wasm::DebugFrame*>; |
1272 | | |
1273 | | Ptr ptr; |
1274 | | |
1275 | | template<typename Frame> |
1276 | 0 | explicit FramePtr(Frame ptr) : ptr(ptr) { } Unexecuted instantiation: js::LiveSavedFrameCache::FramePtr::FramePtr<js::jit::CommonFrameLayout*>(js::jit::CommonFrameLayout*) Unexecuted instantiation: js::LiveSavedFrameCache::FramePtr::FramePtr<js::InterpreterFrame*>(js::InterpreterFrame*) Unexecuted instantiation: js::LiveSavedFrameCache::FramePtr::FramePtr<js::wasm::DebugFrame*>(js::wasm::DebugFrame*) Unexecuted instantiation: js::LiveSavedFrameCache::FramePtr::FramePtr<js::jit::RematerializedFrame*>(js::jit::RematerializedFrame*) |
1277 | | |
1278 | | struct HasCachedMatcher; |
1279 | | struct SetHasCachedMatcher; |
1280 | | struct ClearHasCachedMatcher; |
1281 | | |
1282 | | public: |
1283 | | // If iter's frame is of a type that can be cached, construct a FramePtr |
1284 | | // for its frame. Otherwise, return Nothing. |
1285 | | static inline mozilla::Maybe<FramePtr> create(const FrameIter& iter); |
1286 | | |
1287 | | // Construct a FramePtr from an AbstractFramePtr. This always succeeds. |
1288 | | static inline FramePtr create(AbstractFramePtr abstractFramePtr); |
1289 | | |
1290 | | inline bool hasCachedSavedFrame() const; |
1291 | | inline void setHasCachedSavedFrame(); |
1292 | | inline void clearHasCachedSavedFrame(); |
1293 | | |
1294 | | // Return true if this FramePtr refers to an interpreter frame. |
1295 | 0 | inline bool isInterpreterFrame() const { return ptr.is<InterpreterFrame*>(); } |
1296 | | |
1297 | | // If this FramePtr is an interpreter frame, return a pointer to it. |
1298 | 0 | inline InterpreterFrame& asInterpreterFrame() const { return *ptr.as<InterpreterFrame*>(); } |
1299 | | |
1300 | 0 | bool operator==(const FramePtr& rhs) const { return rhs.ptr == this->ptr; } |
1301 | 0 | bool operator!=(const FramePtr& rhs) const { return !(rhs == *this); } |
1302 | | }; |
1303 | | |
1304 | | private: |
1305 | | // A key in the cache: the address of a frame, live or dead, for which we |
1306 | | // can cache SavedFrames. Since the pointer may not be live, the only |
1307 | | // operation this type permits is comparison. |
1308 | | class Key { |
1309 | | FramePtr framePtr; |
1310 | | |
1311 | | public: |
1312 | 0 | MOZ_IMPLICIT Key(const FramePtr& framePtr) : framePtr(framePtr) { } |
1313 | | |
1314 | 0 | bool operator==(const Key& rhs) const { return rhs.framePtr == this->framePtr; } |
1315 | 0 | bool operator!=(const Key& rhs) const { return !(rhs == *this); } |
1316 | | }; |
1317 | | |
1318 | | struct Entry |
1319 | | { |
1320 | | const Key key; |
1321 | | const jsbytecode* pc; |
1322 | | HeapPtr<SavedFrame*> savedFrame; |
1323 | | |
1324 | | Entry(const Key& key, const jsbytecode* pc, SavedFrame* savedFrame) |
1325 | | : key(key) |
1326 | | , pc(pc) |
1327 | | , savedFrame(savedFrame) |
1328 | 0 | { } |
1329 | | }; |
1330 | | |
1331 | | using EntryVector = Vector<Entry, 0, SystemAllocPolicy>; |
1332 | | EntryVector* frames; |
1333 | | |
1334 | | LiveSavedFrameCache(const LiveSavedFrameCache&) = delete; |
1335 | | LiveSavedFrameCache& operator=(const LiveSavedFrameCache&) = delete; |
1336 | | |
1337 | | public: |
1338 | 1.62M | explicit LiveSavedFrameCache() : frames(nullptr) { } |
1339 | | |
1340 | | LiveSavedFrameCache(LiveSavedFrameCache&& rhs) |
1341 | | : frames(rhs.frames) |
1342 | 1.62M | { |
1343 | 1.62M | MOZ_ASSERT(this != &rhs, "self-move disallowed"); |
1344 | 1.62M | rhs.frames = nullptr; |
1345 | 1.62M | } |
1346 | | |
1347 | 3.24M | ~LiveSavedFrameCache() { |
1348 | 3.24M | if (frames) { |
1349 | 0 | js_delete(frames); |
1350 | 0 | frames = nullptr; |
1351 | 0 | } |
1352 | 3.24M | } |
1353 | | |
1354 | 14 | bool initialized() const { return !!frames; } |
1355 | 0 | bool init(JSContext* cx) { |
1356 | 0 | frames = js_new<EntryVector>(); |
1357 | 0 | if (!frames) { |
1358 | 0 | JS_ReportOutOfMemory(cx); |
1359 | 0 | return false; |
1360 | 0 | } |
1361 | 0 | return true; |
1362 | 0 | } |
1363 | | |
1364 | | void trace(JSTracer* trc); |
1365 | | |
1366 | | // Set |frame| to the cached SavedFrame corresponding to |framePtr| at |pc|. |
1367 | | // |framePtr|'s hasCachedSavedFrame bit must be set. Remove all cache |
1368 | | // entries for frames younger than that one. |
1369 | | // |
1370 | | // This may set |frame| to nullptr if |pc| is different from the pc supplied |
1371 | | // when the cache entry was inserted. In this case, the cached SavedFrame |
1372 | | // (probably) has the wrong source position. Entries for younger frames are |
1373 | | // still removed. The next frame, if any, will be a cache hit. |
1374 | | // |
1375 | | // This may also set |frame| to nullptr if the cache was populated with |
1376 | | // SavedFrame objects for a different compartment than cx's current |
1377 | | // compartment. In this case, the entire cache is flushed. |
1378 | | void find(JSContext* cx, FramePtr& framePtr, const jsbytecode* pc, |
1379 | | MutableHandleSavedFrame frame) const; |
1380 | | |
1381 | | // Search the cache for a frame matching |framePtr|, without removing any |
1382 | | // entries. Return the matching saved frame, or nullptr if none is found. |
1383 | | // This is used for resolving |evalInFramePrev| links. |
1384 | | void findWithoutInvalidation(const FramePtr& framePtr, MutableHandleSavedFrame frame) const; |
1385 | | |
1386 | | // Push a cache entry mapping |framePtr| and |pc| to |savedFrame| on the top |
1387 | | // of the cache's stack. You must insert entries for frames from oldest to |
1388 | | // youngest. They must all be younger than the frame that the |find| method |
1389 | | // found a hit for; or you must have cleared the entire cache with the |
1390 | | // |clear| method. |
1391 | | bool insert(JSContext* cx, FramePtr& framePtr, const jsbytecode* pc, |
1392 | | HandleSavedFrame savedFrame); |
1393 | | |
1394 | | // Remove all entries from the cache. |
1395 | 0 | void clear() { if (frames) frames->clear(); } |
1396 | | }; |
1397 | | |
1398 | | static_assert(sizeof(LiveSavedFrameCache) == sizeof(uintptr_t), |
1399 | | "Every js::Activation has a LiveSavedFrameCache, so we need to be pretty careful " |
1400 | | "about avoiding bloat. If you're adding members to LiveSavedFrameCache, maybe you " |
1401 | | "should consider figuring out a way to make js::Activation have a " |
1402 | | "LiveSavedFrameCache* instead of a Rooted<LiveSavedFrameCache>."); |
1403 | | |
1404 | | /*****************************************************************************/ |
1405 | | |
1406 | | class InterpreterActivation; |
1407 | | |
1408 | | namespace jit { |
1409 | | class JitActivation; |
1410 | | } // namespace jit |
1411 | | |
1412 | | // This class is separate from Activation, because it calls Compartment::wrap() |
1413 | | // which can GC and walk the stack. It's not safe to do that within the |
1414 | | // JitActivation constructor. |
1415 | | class MOZ_RAII ActivationEntryMonitor |
1416 | | { |
1417 | | JSContext* cx_; |
1418 | | |
1419 | | // The entry point monitor that was set on cx_->runtime() when this |
1420 | | // ActivationEntryMonitor was created. |
1421 | | JS::dbg::AutoEntryMonitor* entryMonitor_; |
1422 | | |
1423 | | explicit ActivationEntryMonitor(JSContext* cx); |
1424 | | |
1425 | | ActivationEntryMonitor(const ActivationEntryMonitor& other) = delete; |
1426 | | void operator=(const ActivationEntryMonitor& other) = delete; |
1427 | | |
1428 | | Value asyncStack(JSContext* cx); |
1429 | | |
1430 | | public: |
1431 | | ActivationEntryMonitor(JSContext* cx, InterpreterFrame* entryFrame); |
1432 | | ActivationEntryMonitor(JSContext* cx, jit::CalleeToken entryToken); |
1433 | | inline ~ActivationEntryMonitor(); |
1434 | | }; |
1435 | | |
1436 | | class Activation |
1437 | | { |
1438 | | protected: |
1439 | | JSContext* cx_; |
1440 | | JS::Compartment* compartment_; |
1441 | | Activation* prev_; |
1442 | | Activation* prevProfiling_; |
1443 | | |
1444 | | // Counter incremented by JS::HideScriptedCaller and decremented by |
1445 | | // JS::UnhideScriptedCaller. If > 0 for the top activation, |
1446 | | // DescribeScriptedCaller will return null instead of querying that |
1447 | | // activation, which should prompt the caller to consult embedding-specific |
1448 | | // data structures instead. |
1449 | | size_t hideScriptedCallerCount_; |
1450 | | |
1451 | | // The cache of SavedFrame objects we have already captured when walking |
1452 | | // this activation's stack. |
1453 | | Rooted<LiveSavedFrameCache> frameCache_; |
1454 | | |
1455 | | // Youngest saved frame of an async stack that will be iterated during stack |
1456 | | // capture in place of the actual stack of previous activations. Note that |
1457 | | // the stack of this activation is captured entirely before this is used. |
1458 | | // |
1459 | | // Usually this is nullptr, meaning that normal stack capture will occur. |
1460 | | // When this is set, the stack of any previous activation is ignored. |
1461 | | Rooted<SavedFrame*> asyncStack_; |
1462 | | |
1463 | | // Value of asyncCause to be attached to asyncStack_. |
1464 | | const char* asyncCause_; |
1465 | | |
1466 | | // True if the async call was explicitly requested, e.g. via |
1467 | | // callFunctionWithAsyncStack. |
1468 | | bool asyncCallIsExplicit_; |
1469 | | |
1470 | | enum Kind { Interpreter, Jit }; |
1471 | | Kind kind_; |
1472 | | |
1473 | | inline Activation(JSContext* cx, Kind kind); |
1474 | | inline ~Activation(); |
1475 | | |
1476 | | public: |
1477 | 0 | JSContext* cx() const { |
1478 | 0 | return cx_; |
1479 | 0 | } |
1480 | 34 | JS::Compartment* compartment() const { |
1481 | 34 | return compartment_; |
1482 | 34 | } |
1483 | 66 | Activation* prev() const { |
1484 | 66 | return prev_; |
1485 | 66 | } |
1486 | 0 | Activation* prevProfiling() const { return prevProfiling_; } |
1487 | | inline Activation* mostRecentProfiling(); |
1488 | | |
1489 | 78 | bool isInterpreter() const { |
1490 | 78 | return kind_ == Interpreter; |
1491 | 78 | } |
1492 | 68 | bool isJit() const { |
1493 | 68 | return kind_ == Jit; |
1494 | 68 | } |
1495 | | inline bool hasWasmExitFP() const; |
1496 | | |
1497 | | inline bool isProfiling() const; |
1498 | | void registerProfiling(); |
1499 | | void unregisterProfiling(); |
1500 | | |
1501 | 78 | InterpreterActivation* asInterpreter() const { |
1502 | 78 | MOZ_ASSERT(isInterpreter()); |
1503 | 78 | return (InterpreterActivation*)this; |
1504 | 78 | } |
1505 | 93 | jit::JitActivation* asJit() const { |
1506 | 93 | MOZ_ASSERT(isJit()); |
1507 | 93 | return (jit::JitActivation*)this; |
1508 | 93 | } |
1509 | | |
1510 | 4 | void hideScriptedCaller() { |
1511 | 4 | hideScriptedCallerCount_++; |
1512 | 4 | } |
1513 | 4 | void unhideScriptedCaller() { |
1514 | 4 | MOZ_ASSERT(hideScriptedCallerCount_ > 0); |
1515 | 4 | hideScriptedCallerCount_--; |
1516 | 4 | } |
1517 | 0 | bool scriptedCallerIsHidden() const { |
1518 | 0 | return hideScriptedCallerCount_ > 0; |
1519 | 0 | } |
1520 | | |
1521 | 0 | static size_t offsetOfPrev() { |
1522 | 0 | return offsetof(Activation, prev_); |
1523 | 0 | } |
1524 | 0 | static size_t offsetOfPrevProfiling() { |
1525 | 0 | return offsetof(Activation, prevProfiling_); |
1526 | 0 | } |
1527 | | |
1528 | 0 | SavedFrame* asyncStack() { |
1529 | 0 | return asyncStack_; |
1530 | 0 | } |
1531 | | |
1532 | 0 | const char* asyncCause() const { |
1533 | 0 | return asyncCause_; |
1534 | 0 | } |
1535 | | |
1536 | 0 | bool asyncCallIsExplicit() const { |
1537 | 0 | return asyncCallIsExplicit_; |
1538 | 0 | } |
1539 | | |
1540 | | inline LiveSavedFrameCache* getLiveSavedFrameCache(JSContext* cx); |
1541 | 0 | void clearLiveSavedFrameCache() { frameCache_.get().clear(); } |
1542 | | |
1543 | | private: |
1544 | | Activation(const Activation& other) = delete; |
1545 | | void operator=(const Activation& other) = delete; |
1546 | | }; |
1547 | | |
1548 | | // This variable holds a special opcode value which is greater than all normal |
1549 | | // opcodes, and is chosen such that the bitwise or of this value with any |
1550 | | // opcode is this value. |
1551 | | static const jsbytecode EnableInterruptsPseudoOpcode = -1; |
1552 | | |
1553 | | static_assert(EnableInterruptsPseudoOpcode >= JSOP_LIMIT, |
1554 | | "EnableInterruptsPseudoOpcode must be greater than any opcode"); |
1555 | | static_assert(EnableInterruptsPseudoOpcode == jsbytecode(-1), |
1556 | | "EnableInterruptsPseudoOpcode must be the maximum jsbytecode value"); |
1557 | | |
1558 | | class InterpreterFrameIterator; |
1559 | | class RunState; |
1560 | | |
1561 | | class InterpreterActivation : public Activation |
1562 | | { |
1563 | | friend class js::InterpreterFrameIterator; |
1564 | | |
1565 | | InterpreterRegs regs_; |
1566 | | InterpreterFrame* entryFrame_; |
1567 | | size_t opMask_; // For debugger interrupts, see js::Interpret. |
1568 | | |
1569 | | #ifdef DEBUG |
1570 | | size_t oldFrameCount_; |
1571 | | #endif |
1572 | | |
1573 | | public: |
1574 | | inline InterpreterActivation(RunState& state, JSContext* cx, InterpreterFrame* entryFrame); |
1575 | | inline ~InterpreterActivation(); |
1576 | | |
1577 | | inline bool pushInlineFrame(const CallArgs& args, HandleScript script, |
1578 | | MaybeConstruct constructing); |
1579 | | inline void popInlineFrame(InterpreterFrame* frame); |
1580 | | |
1581 | | inline bool resumeGeneratorFrame(HandleFunction callee, HandleObject envChain); |
1582 | | |
1583 | 20 | InterpreterFrame* current() const { |
1584 | 20 | return regs_.fp(); |
1585 | 20 | } |
1586 | 57.7k | InterpreterRegs& regs() { |
1587 | 57.7k | return regs_; |
1588 | 57.7k | } |
1589 | 338 | InterpreterFrame* entryFrame() const { |
1590 | 338 | return entryFrame_; |
1591 | 338 | } |
1592 | 10.6k | size_t opMask() const { |
1593 | 10.6k | return opMask_; |
1594 | 10.6k | } |
1595 | | |
1596 | 27 | bool isProfiling() const { |
1597 | 27 | return false; |
1598 | 27 | } |
1599 | | |
1600 | | // If this js::Interpret frame is running |script|, enable interrupts. |
1601 | 18 | void enableInterruptsIfRunning(JSScript* script) { |
1602 | 18 | if (regs_.fp()->script() == script) { |
1603 | 1 | enableInterruptsUnconditionally(); |
1604 | 1 | } |
1605 | 18 | } |
1606 | 41 | void enableInterruptsUnconditionally() { |
1607 | 41 | opMask_ = EnableInterruptsPseudoOpcode; |
1608 | 41 | } |
1609 | 41 | void clearInterruptsMask() { |
1610 | 41 | opMask_ = 0; |
1611 | 41 | } |
1612 | | }; |
1613 | | |
1614 | | // Iterates over a thread's activation list. |
1615 | | class ActivationIterator |
1616 | | { |
1617 | | protected: |
1618 | | Activation* activation_; |
1619 | | |
1620 | | public: |
1621 | | explicit ActivationIterator(JSContext* cx); |
1622 | | |
1623 | | ActivationIterator& operator++(); |
1624 | | |
1625 | 108 | Activation* operator->() const { |
1626 | 108 | return activation_; |
1627 | 108 | } |
1628 | 22 | Activation* activation() const { |
1629 | 22 | return activation_; |
1630 | 22 | } |
1631 | 253 | bool done() const { |
1632 | 253 | return activation_ == nullptr; |
1633 | 253 | } |
1634 | | }; |
1635 | | |
1636 | | namespace jit { |
1637 | | |
1638 | | class BailoutFrameInfo; |
1639 | | |
1640 | | // A JitActivation is used for frames running in Baseline or Ion. |
1641 | | class JitActivation : public Activation |
1642 | | { |
1643 | | // If Baseline, Ion or Wasm code is on the stack, and has called into C++, |
1644 | | // this will be aligned to an ExitFrame. The last bit indicates if it's a |
1645 | | // wasm frame (bit set to wasm::ExitOrJitEntryFPTag) or not |
1646 | | // (bit set to ~wasm::ExitOrJitEntryFPTag). |
1647 | | uint8_t* packedExitFP_; |
1648 | | |
1649 | | // When hasWasmExitFP(), encodedWasmExitReason_ holds ExitReason. |
1650 | | uint32_t encodedWasmExitReason_; |
1651 | | |
1652 | | JitActivation* prevJitActivation_; |
1653 | | |
1654 | | // Rematerialized Ion frames which has info copied out of snapshots. Maps |
1655 | | // frame pointers (i.e. packedExitFP_) to a vector of rematerializations of all |
1656 | | // inline frames associated with that frame. |
1657 | | // |
1658 | | // This table is lazily initialized by calling getRematerializedFrame. |
1659 | | typedef GCVector<RematerializedFrame*> RematerializedFrameVector; |
1660 | | typedef HashMap<uint8_t*, RematerializedFrameVector> RematerializedFrameTable; |
1661 | | js::UniquePtr<RematerializedFrameTable> rematerializedFrames_; |
1662 | | |
1663 | | // This vector is used to remember the outcome of the evaluation of recover |
1664 | | // instructions. |
1665 | | // |
1666 | | // RInstructionResults are appended into this vector when Snapshot values |
1667 | | // have to be read, or when the evaluation has to run before some mutating |
1668 | | // code. Each RInstructionResults belongs to one frame which has to bailout |
1669 | | // as soon as we get back to it. |
1670 | | typedef Vector<RInstructionResults, 1> IonRecoveryMap; |
1671 | | IonRecoveryMap ionRecovery_; |
1672 | | |
1673 | | // If we are bailing out from Ion, then this field should be a non-null |
1674 | | // pointer which references the BailoutFrameInfo used to walk the inner |
1675 | | // frames. This field is used for all newly constructed JSJitFrameIters to |
1676 | | // read the innermost frame information from this bailout data instead of |
1677 | | // reading it from the stack. |
1678 | | BailoutFrameInfo* bailoutData_; |
1679 | | |
1680 | | // When profiling is enabled, these fields will be updated to reflect the |
1681 | | // last pushed frame for this activation, and if that frame has been |
1682 | | // left for a call, the native code site of the call. |
1683 | | mozilla::Atomic<void*, mozilla::Relaxed> lastProfilingFrame_; |
1684 | | mozilla::Atomic<void*, mozilla::Relaxed> lastProfilingCallSite_; |
1685 | | static_assert(sizeof(mozilla::Atomic<void*, mozilla::Relaxed>) == sizeof(void*), |
1686 | | "Atomic should have same memory format as underlying type."); |
1687 | | |
1688 | | // When wasm traps, the signal handler records some data for unwinding |
1689 | | // purposes. Wasm code can't trap reentrantly. |
1690 | | mozilla::Maybe<wasm::TrapData> wasmTrapData_; |
1691 | | |
1692 | | void clearRematerializedFrames(); |
1693 | | |
1694 | | #ifdef CHECK_OSIPOINT_REGISTERS |
1695 | | protected: |
1696 | | // Used to verify that live registers don't change between a VM call and |
1697 | | // the OsiPoint that follows it. Protected to silence Clang warning. |
1698 | | uint32_t checkRegs_ = 0; |
1699 | | RegisterDump regs_; |
1700 | | #endif |
1701 | | |
1702 | | public: |
1703 | | explicit JitActivation(JSContext* cx); |
1704 | | ~JitActivation(); |
1705 | | |
1706 | 1.62M | bool isProfiling() const { |
1707 | 1.62M | // All JitActivations can be profiled. |
1708 | 1.62M | return true; |
1709 | 1.62M | } |
1710 | | |
1711 | 0 | JitActivation* prevJitActivation() const { |
1712 | 0 | return prevJitActivation_; |
1713 | 0 | } |
1714 | 0 | static size_t offsetOfPrevJitActivation() { |
1715 | 0 | return offsetof(JitActivation, prevJitActivation_); |
1716 | 0 | } |
1717 | | |
1718 | 0 | bool hasExitFP() const { |
1719 | 0 | return !!packedExitFP_; |
1720 | 0 | } |
1721 | 0 | uint8_t* jsOrWasmExitFP() const { |
1722 | 0 | return (uint8_t*)(uintptr_t(packedExitFP_) & ~wasm::ExitOrJitEntryFPTag); |
1723 | 0 | } |
1724 | 720 | static size_t offsetOfPackedExitFP() { |
1725 | 720 | return offsetof(JitActivation, packedExitFP_); |
1726 | 720 | } |
1727 | | |
1728 | 26 | bool hasJSExitFP() const { |
1729 | 26 | return !(uintptr_t(packedExitFP_) & wasm::ExitOrJitEntryFPTag); |
1730 | 26 | } |
1731 | 50 | uint8_t* jsExitFP() const { |
1732 | 50 | MOZ_ASSERT(hasJSExitFP()); |
1733 | 50 | return packedExitFP_; |
1734 | 50 | } |
1735 | 8 | void setJSExitFP(uint8_t* fp) { |
1736 | 8 | packedExitFP_ = fp; |
1737 | 8 | } |
1738 | | |
1739 | | #ifdef CHECK_OSIPOINT_REGISTERS |
1740 | | void setCheckRegs(bool check) { |
1741 | | checkRegs_ = check; |
1742 | | } |
1743 | | static size_t offsetOfCheckRegs() { |
1744 | | return offsetof(JitActivation, checkRegs_); |
1745 | | } |
1746 | | static size_t offsetOfRegs() { |
1747 | | return offsetof(JitActivation, regs_); |
1748 | | } |
1749 | | #endif |
1750 | | |
1751 | | // Look up a rematerialized frame keyed by the fp, rematerializing the |
1752 | | // frame if one doesn't already exist. A frame can only be rematerialized |
1753 | | // if an IonFrameIterator pointing to the nearest uninlined frame can be |
1754 | | // provided, as values need to be read out of snapshots. |
1755 | | // |
1756 | | // The inlineDepth must be within bounds of the frame pointed to by iter. |
1757 | | RematerializedFrame* getRematerializedFrame(JSContext* cx, const JSJitFrameIter& iter, |
1758 | | size_t inlineDepth = 0); |
1759 | | |
1760 | | // Look up a rematerialized frame by the fp. If inlineDepth is out of |
1761 | | // bounds of what has been rematerialized, nullptr is returned. |
1762 | | RematerializedFrame* lookupRematerializedFrame(uint8_t* top, size_t inlineDepth = 0); |
1763 | | |
1764 | | // Remove all rematerialized frames associated with the fp top from the |
1765 | | // Debugger. |
1766 | | void removeRematerializedFramesFromDebugger(JSContext* cx, uint8_t* top); |
1767 | | |
1768 | 8 | bool hasRematerializedFrame(uint8_t* top, size_t inlineDepth = 0) { |
1769 | 8 | return !!lookupRematerializedFrame(top, inlineDepth); |
1770 | 8 | } |
1771 | | |
1772 | | // Remove a previous rematerialization by fp. |
1773 | | void removeRematerializedFrame(uint8_t* top); |
1774 | | |
1775 | | void traceRematerializedFrames(JSTracer* trc); |
1776 | | |
1777 | | // Register the results of on Ion frame recovery. |
1778 | | bool registerIonFrameRecovery(RInstructionResults&& results); |
1779 | | |
1780 | | // Return the pointer to the Ion frame recovery, if it is already registered. |
1781 | | RInstructionResults* maybeIonFrameRecovery(JitFrameLayout* fp); |
1782 | | |
1783 | | // If an Ion frame recovery exists for the |fp| frame exists, then remove it |
1784 | | // from the activation. |
1785 | | void removeIonFrameRecovery(JitFrameLayout* fp); |
1786 | | |
1787 | | void traceIonRecovery(JSTracer* trc); |
1788 | | |
1789 | | // Return the bailout information if it is registered. |
1790 | 162 | const BailoutFrameInfo* bailoutData() const { return bailoutData_; } |
1791 | | |
1792 | | // Register the bailout data when it is constructed. |
1793 | | void setBailoutData(BailoutFrameInfo* bailoutData); |
1794 | | |
1795 | | // Unregister the bailout data when the frame is reconstructed. |
1796 | | void cleanBailoutData(); |
1797 | | |
1798 | 20 | static size_t offsetOfLastProfilingFrame() { |
1799 | 20 | return offsetof(JitActivation, lastProfilingFrame_); |
1800 | 20 | } |
1801 | 0 | void* lastProfilingFrame() { |
1802 | 0 | return lastProfilingFrame_; |
1803 | 0 | } |
1804 | 0 | void setLastProfilingFrame(void* ptr) { |
1805 | 0 | lastProfilingFrame_ = ptr; |
1806 | 0 | } |
1807 | | |
1808 | 20 | static size_t offsetOfLastProfilingCallSite() { |
1809 | 20 | return offsetof(JitActivation, lastProfilingCallSite_); |
1810 | 20 | } |
1811 | 0 | void* lastProfilingCallSite() { |
1812 | 0 | return lastProfilingCallSite_; |
1813 | 0 | } |
1814 | 0 | void setLastProfilingCallSite(void* ptr) { |
1815 | 0 | lastProfilingCallSite_ = ptr; |
1816 | 0 | } |
1817 | | |
1818 | | // WebAssembly specific attributes. |
1819 | 0 | bool hasWasmExitFP() const { |
1820 | 0 | return uintptr_t(packedExitFP_) & wasm::ExitOrJitEntryFPTag; |
1821 | 0 | } |
1822 | 0 | wasm::Frame* wasmExitFP() const { |
1823 | 0 | MOZ_ASSERT(hasWasmExitFP()); |
1824 | 0 | return (wasm::Frame*)(uintptr_t(packedExitFP_) & ~wasm::ExitOrJitEntryFPTag); |
1825 | 0 | } |
1826 | 0 | void setWasmExitFP(const wasm::Frame* fp) { |
1827 | 0 | if (fp) { |
1828 | 0 | MOZ_ASSERT(!(uintptr_t(fp) & wasm::ExitOrJitEntryFPTag)); |
1829 | 0 | packedExitFP_ = (uint8_t*)(uintptr_t(fp) | wasm::ExitOrJitEntryFPTag); |
1830 | 0 | MOZ_ASSERT(hasWasmExitFP()); |
1831 | 0 | } else { |
1832 | 0 | packedExitFP_ = nullptr; |
1833 | 0 | } |
1834 | 0 | } |
1835 | 0 | wasm::ExitReason wasmExitReason() const { |
1836 | 0 | MOZ_ASSERT(hasWasmExitFP()); |
1837 | 0 | return wasm::ExitReason::Decode(encodedWasmExitReason_); |
1838 | 0 | } |
1839 | 0 | static size_t offsetOfEncodedWasmExitReason() { |
1840 | 0 | return offsetof(JitActivation, encodedWasmExitReason_); |
1841 | 0 | } |
1842 | | |
1843 | | void startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset, const wasm::RegisterState& state); |
1844 | | void finishWasmTrap(); |
1845 | 0 | bool isWasmTrapping() const { return !!wasmTrapData_; } |
1846 | 0 | const wasm::TrapData& wasmTrapData() { return *wasmTrapData_; } |
1847 | | }; |
1848 | | |
1849 | | // A filtering of the ActivationIterator to only stop at JitActivations. |
1850 | | class JitActivationIterator : public ActivationIterator |
1851 | | { |
1852 | 92 | void settle() { |
1853 | 92 | while (!done() && !activation_->isJit()) { |
1854 | 0 | ActivationIterator::operator++(); |
1855 | 0 | } |
1856 | 92 | } |
1857 | | |
1858 | | public: |
1859 | | explicit JitActivationIterator(JSContext* cx) |
1860 | | : ActivationIterator(cx) |
1861 | 66 | { |
1862 | 66 | settle(); |
1863 | 66 | } |
1864 | | |
1865 | 26 | JitActivationIterator& operator++() { |
1866 | 26 | ActivationIterator::operator++(); |
1867 | 26 | settle(); |
1868 | 26 | return *this; |
1869 | 26 | } |
1870 | | }; |
1871 | | |
1872 | | } // namespace jit |
1873 | | |
1874 | | inline bool |
1875 | | Activation::hasWasmExitFP() const |
1876 | 0 | { |
1877 | 0 | return isJit() && asJit()->hasWasmExitFP(); |
1878 | 0 | } |
1879 | | |
1880 | | // Iterates over the frames of a single InterpreterActivation. |
1881 | | class InterpreterFrameIterator |
1882 | | { |
1883 | | InterpreterActivation* activation_; |
1884 | | InterpreterFrame* fp_; |
1885 | | jsbytecode* pc_; |
1886 | | Value* sp_; |
1887 | | |
1888 | | public: |
1889 | | explicit InterpreterFrameIterator(InterpreterActivation* activation) |
1890 | | : activation_(activation), |
1891 | | fp_(nullptr), |
1892 | | pc_(nullptr), |
1893 | | sp_(nullptr) |
1894 | 16 | { |
1895 | 16 | if (activation) { |
1896 | 8 | fp_ = activation->current(); |
1897 | 8 | pc_ = activation->regs().pc; |
1898 | 8 | sp_ = activation->regs().sp; |
1899 | 8 | } |
1900 | 16 | } |
1901 | | |
1902 | 16 | InterpreterFrame* frame() const { |
1903 | 16 | MOZ_ASSERT(!done()); |
1904 | 16 | return fp_; |
1905 | 16 | } |
1906 | 8 | jsbytecode* pc() const { |
1907 | 8 | MOZ_ASSERT(!done()); |
1908 | 8 | return pc_; |
1909 | 8 | } |
1910 | 0 | Value* sp() const { |
1911 | 0 | MOZ_ASSERT(!done()); |
1912 | 0 | return sp_; |
1913 | 0 | } |
1914 | | |
1915 | | InterpreterFrameIterator& operator++(); |
1916 | | |
1917 | 0 | bool done() const { |
1918 | 0 | return fp_ == nullptr; |
1919 | 0 | } |
1920 | | }; |
1921 | | |
1922 | | // A JitFrameIter can iterate over all kind of frames emitted by our code |
1923 | | // generators, be they composed of JS jit frames or wasm frames, interleaved or |
1924 | | // not, in any order. |
1925 | | // |
1926 | | // In the following class: |
1927 | | // - code generated for JS is referred to as JSJit. |
1928 | | // - code generated for wasm is referred to as Wasm. |
1929 | | // Also, Jit refers to any one of them. |
1930 | | // |
1931 | | // JitFrameIter uses JSJitFrameIter to iterate over JSJit code or a |
1932 | | // WasmFrameIter to iterate over wasm code; only one of them is active at the |
1933 | | // time. When a sub-iterator is done, the JitFrameIter knows how to stop, move |
1934 | | // onto the next activation or move onto another kind of Jit code. |
1935 | | // |
1936 | | // For ease of use, there is also OnlyJSJitFrameIter, which skips all the |
1937 | | // non-JSJit frames. |
1938 | | // |
1939 | | // Note it is allowed to get a handle to the internal frame iterator via |
1940 | | // asJSJit() and asWasm(), but the user has to be careful not to have those be |
1941 | | // used after JitFrameIter leaves the scope or the operator++ is called. |
1942 | | // |
1943 | | // In particular, this can handle the transition from wasm to jit and from jit |
1944 | | // to wasm, since these can be interleaved in the same JitActivation. |
1945 | | class JitFrameIter |
1946 | | { |
1947 | | protected: |
1948 | | jit::JitActivation* act_; |
1949 | | mozilla::MaybeOneOf<jit::JSJitFrameIter, wasm::WasmFrameIter> iter_; |
1950 | | bool mustUnwindActivation_; |
1951 | | |
1952 | | void settle(); |
1953 | | |
1954 | | public: |
1955 | 8 | JitFrameIter() : act_(nullptr), iter_(), mustUnwindActivation_(false) {} |
1956 | | explicit JitFrameIter(jit::JitActivation* activation, bool mustUnwindActivation = false); |
1957 | | |
1958 | | explicit JitFrameIter(const JitFrameIter& another); |
1959 | | JitFrameIter& operator=(const JitFrameIter& another); |
1960 | | |
1961 | 410 | bool isSome() const { return !iter_.empty(); } |
1962 | 0 | void reset() { MOZ_ASSERT(isSome()); iter_.destroy(); } |
1963 | | |
1964 | 296 | bool isJSJit() const { return isSome() && iter_.constructed<jit::JSJitFrameIter>(); } |
1965 | 210 | jit::JSJitFrameIter& asJSJit() { return iter_.ref<jit::JSJitFrameIter>(); } |
1966 | 138 | const jit::JSJitFrameIter& asJSJit() const { return iter_.ref<jit::JSJitFrameIter>(); } |
1967 | | |
1968 | 0 | bool isWasm() const { return isSome() && iter_.constructed<wasm::WasmFrameIter>(); } |
1969 | 0 | wasm::WasmFrameIter& asWasm() { return iter_.ref<wasm::WasmFrameIter>(); } |
1970 | 0 | const wasm::WasmFrameIter& asWasm() const { return iter_.ref<wasm::WasmFrameIter>(); } |
1971 | | |
1972 | | // Operations common to all frame iterators. |
1973 | 0 | const jit::JitActivation* activation() const { return act_; } |
1974 | | bool done() const; |
1975 | | void operator++(); |
1976 | | |
1977 | | JS::Realm* realm() const; |
1978 | | |
1979 | | // Operations which have an effect only on JIT frames. |
1980 | | void skipNonScriptedJSFrames(); |
1981 | | |
1982 | | // Returns true iff this is a JIT frame with a self-hosted script. Note: be |
1983 | | // careful, JitFrameIter does not consider functions inlined by Ion. |
1984 | | bool isSelfHostedIgnoringInlining() const; |
1985 | | }; |
1986 | | |
1987 | | // A JitFrameIter that skips all the non-JSJit frames, skipping interleaved |
1988 | | // frames of any another kind. |
1989 | | |
1990 | | class OnlyJSJitFrameIter : public JitFrameIter |
1991 | | { |
1992 | 36 | void settle() { |
1993 | 36 | while (!done() && !isJSJit()) { |
1994 | 0 | JitFrameIter::operator++(); |
1995 | 0 | } |
1996 | 36 | } |
1997 | | |
1998 | | public: |
1999 | | explicit OnlyJSJitFrameIter(jit::JitActivation* act); |
2000 | | explicit OnlyJSJitFrameIter(JSContext* cx); |
2001 | | explicit OnlyJSJitFrameIter(const ActivationIterator& cx); |
2002 | | |
2003 | 24 | void operator++() { |
2004 | 24 | JitFrameIter::operator++(); |
2005 | 24 | settle(); |
2006 | 24 | } |
2007 | | |
2008 | 24 | const jit::JSJitFrameIter& frame() const { |
2009 | 24 | return asJSJit(); |
2010 | 24 | } |
2011 | | }; |
2012 | | |
2013 | | // A FrameIter walks over a context's stack of JS script activations, |
2014 | | // abstracting over whether the JS scripts were running in the interpreter or |
2015 | | // different modes of compiled code. |
2016 | | // |
2017 | | // FrameIter is parameterized by what it includes in the stack iteration: |
2018 | | // - When provided, the optional JSPrincipal argument will cause FrameIter to |
2019 | | // only show frames in globals whose JSPrincipals are subsumed (via |
2020 | | // JSSecurityCallbacks::subsume) by the given JSPrincipal. |
2021 | | // |
2022 | | // Additionally, there are derived FrameIter types that automatically skip |
2023 | | // certain frames: |
2024 | | // - ScriptFrameIter only shows frames that have an associated JSScript |
2025 | | // (currently everything other than wasm stack frames). When !hasScript(), |
2026 | | // clients must stick to the portion of the |
2027 | | // interface marked below. |
2028 | | // - NonBuiltinScriptFrameIter additionally filters out builtin (self-hosted) |
2029 | | // scripts. |
2030 | | class FrameIter |
2031 | | { |
2032 | | public: |
2033 | | enum DebuggerEvalOption { FOLLOW_DEBUGGER_EVAL_PREV_LINK, |
2034 | | IGNORE_DEBUGGER_EVAL_PREV_LINK }; |
2035 | | |
2036 | | enum State { |
2037 | | DONE, // when there are no more frames nor activations to unwind. |
2038 | | INTERP, // interpreter activation on the stack |
2039 | | JIT // jit or wasm activations on the stack |
2040 | | }; |
2041 | | |
2042 | | // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on |
2043 | | // the heap, so this structure should not contain any GC things. |
2044 | | struct Data |
2045 | | { |
2046 | | JSContext* cx_; |
2047 | | DebuggerEvalOption debuggerEvalOption_; |
2048 | | JSPrincipals* principals_; |
2049 | | |
2050 | | State state_; |
2051 | | |
2052 | | jsbytecode* pc_; |
2053 | | |
2054 | | InterpreterFrameIterator interpFrames_; |
2055 | | ActivationIterator activations_; |
2056 | | |
2057 | | JitFrameIter jitFrames_; |
2058 | | unsigned ionInlineFrameNo_; |
2059 | | |
2060 | | Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, JSPrincipals* principals); |
2061 | | Data(const Data& other); |
2062 | | }; |
2063 | | |
2064 | | explicit FrameIter(JSContext* cx, |
2065 | | DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK); |
2066 | | FrameIter(JSContext* cx, DebuggerEvalOption, JSPrincipals*); |
2067 | | FrameIter(const FrameIter& iter); |
2068 | | MOZ_IMPLICIT FrameIter(const Data& data); |
2069 | | MOZ_IMPLICIT FrameIter(AbstractFramePtr frame); |
2070 | | |
2071 | 8 | bool done() const { return data_.state_ == DONE; } |
2072 | | |
2073 | | // ------------------------------------------------------- |
2074 | | // The following functions can only be called when !done() |
2075 | | // ------------------------------------------------------- |
2076 | | |
2077 | | FrameIter& operator++(); |
2078 | | |
2079 | | JS::Realm* realm() const; |
2080 | | JS::Compartment* compartment() const; |
2081 | 0 | Activation* activation() const { return data_.activations_.activation(); } |
2082 | | |
2083 | 0 | bool isInterp() const { |
2084 | 0 | MOZ_ASSERT(!done()); |
2085 | 0 | return data_.state_ == INTERP; |
2086 | 0 | } |
2087 | 0 | bool isJSJit() const { |
2088 | 0 | MOZ_ASSERT(!done()); |
2089 | 0 | return data_.state_ == JIT && data_.jitFrames_.isJSJit(); |
2090 | 0 | } |
2091 | 8 | bool isWasm() const { |
2092 | 8 | MOZ_ASSERT(!done()); |
2093 | 8 | return data_.state_ == JIT && data_.jitFrames_.isWasm(); |
2094 | 8 | } |
2095 | | |
2096 | | inline bool isIon() const; |
2097 | | inline bool isBaseline() const; |
2098 | | inline bool isPhysicalJitFrame() const; |
2099 | | |
2100 | | bool isEvalFrame() const; |
2101 | | bool isFunctionFrame() const; |
2102 | 0 | bool hasArgs() const { return isFunctionFrame(); } |
2103 | | |
2104 | | ScriptSource* scriptSource() const; |
2105 | | const char* filename() const; |
2106 | | const char16_t* displayURL() const; |
2107 | | unsigned computeLine(uint32_t* column = nullptr) const; |
2108 | | JSAtom* maybeFunctionDisplayAtom() const; |
2109 | | bool mutedErrors() const; |
2110 | | |
2111 | 0 | bool hasScript() const { return !isWasm(); } |
2112 | | |
2113 | | // ----------------------------------------------------------- |
2114 | | // The following functions can only be called when isWasm() |
2115 | | // ----------------------------------------------------------- |
2116 | | |
2117 | | inline bool wasmDebugEnabled() const; |
2118 | | inline wasm::Instance* wasmInstance() const; |
2119 | | inline uint32_t wasmFuncIndex() const; |
2120 | | inline unsigned wasmBytecodeOffset() const; |
2121 | | void wasmUpdateBytecodeOffset(); |
2122 | | |
2123 | | // ----------------------------------------------------------- |
2124 | | // The following functions can only be called when hasScript() |
2125 | | // ----------------------------------------------------------- |
2126 | | |
2127 | | inline JSScript* script() const; |
2128 | | |
2129 | | bool isConstructing() const; |
2130 | 0 | jsbytecode* pc() const { MOZ_ASSERT(!done()); return data_.pc_; } |
2131 | | void updatePcQuadratic(); |
2132 | | |
2133 | | // The function |calleeTemplate()| returns either the function from which |
2134 | | // the current |callee| was cloned or the |callee| if it can be read. As |
2135 | | // long as we do not have to investigate the environment chain or build a |
2136 | | // new frame, we should prefer to use |calleeTemplate| instead of |
2137 | | // |callee|, as requesting the |callee| might cause the invalidation of |
2138 | | // the frame. (see js::Lambda) |
2139 | | JSFunction* calleeTemplate() const; |
2140 | | JSFunction* callee(JSContext* cx) const; |
2141 | | |
2142 | 0 | JSFunction* maybeCallee(JSContext* cx) const { |
2143 | 0 | return isFunctionFrame() ? callee(cx) : nullptr; |
2144 | 0 | } |
2145 | | |
2146 | | bool matchCallee(JSContext* cx, HandleFunction fun) const; |
2147 | | |
2148 | | unsigned numActualArgs() const; |
2149 | | unsigned numFormalArgs() const; |
2150 | | Value unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const; |
2151 | | template <class Op> inline void unaliasedForEachActual(JSContext* cx, Op op); |
2152 | | |
2153 | | JSObject* environmentChain(JSContext* cx) const; |
2154 | | CallObject& callObj(JSContext* cx) const; |
2155 | | |
2156 | | bool hasArgsObj() const; |
2157 | | ArgumentsObject& argsObj() const; |
2158 | | |
2159 | | // Get the original |this| value passed to this function. May not be the |
2160 | | // actual this-binding (for instance, derived class constructors will |
2161 | | // change their this-value later and non-strict functions will box |
2162 | | // primitives). |
2163 | | Value thisArgument(JSContext* cx) const; |
2164 | | |
2165 | | Value newTarget() const; |
2166 | | |
2167 | | Value returnValue() const; |
2168 | | void setReturnValue(const Value& v); |
2169 | | |
2170 | | // These are only valid for the top frame. |
2171 | | size_t numFrameSlots() const; |
2172 | | Value frameSlotValue(size_t index) const; |
2173 | | |
2174 | | // Ensures that we have rematerialized the top frame and its associated |
2175 | | // inline frames. Can only be called when isIon(). |
2176 | | bool ensureHasRematerializedFrame(JSContext* cx); |
2177 | | |
2178 | | // True when isInterp() or isBaseline(). True when isIon() if it |
2179 | | // has a rematerialized frame. False otherwise. |
2180 | | bool hasUsableAbstractFramePtr() const; |
2181 | | |
2182 | | // ----------------------------------------------------------- |
2183 | | // The following functions can only be called when isInterp(), |
2184 | | // isBaseline(), isWasm() or isIon(). Further, abstractFramePtr() can |
2185 | | // only be called when hasUsableAbstractFramePtr(). |
2186 | | // ----------------------------------------------------------- |
2187 | | |
2188 | | AbstractFramePtr abstractFramePtr() const; |
2189 | | Data* copyData() const; |
2190 | | |
2191 | | // This can only be called when isInterp(): |
2192 | | inline InterpreterFrame* interpFrame() const; |
2193 | | |
2194 | | // This can only be called when isPhysicalJitFrame(): |
2195 | | inline jit::CommonFrameLayout* physicalJitFrame() const; |
2196 | | |
2197 | | // This is used to provide a raw interface for debugging. |
2198 | | void* rawFramePtr() const; |
2199 | | |
2200 | | private: |
2201 | | Data data_; |
2202 | | jit::InlineFrameIterator ionInlineFrames_; |
2203 | | |
2204 | 0 | const jit::JSJitFrameIter& jsJitFrame() const { return data_.jitFrames_.asJSJit(); } |
2205 | 0 | const wasm::WasmFrameIter& wasmFrame() const { return data_.jitFrames_.asWasm(); } |
2206 | | |
2207 | 0 | jit::JSJitFrameIter& jsJitFrame() { return data_.jitFrames_.asJSJit(); } |
2208 | 0 | wasm::WasmFrameIter& wasmFrame() { return data_.jitFrames_.asWasm(); } |
2209 | | |
2210 | 0 | bool isIonScripted() const { return isJSJit() && jsJitFrame().isIonScripted(); } |
2211 | | |
2212 | | bool principalsSubsumeFrame() const; |
2213 | | |
2214 | | void popActivation(); |
2215 | | void popInterpreterFrame(); |
2216 | | void nextJitFrame(); |
2217 | | void popJitFrame(); |
2218 | | void settleOnActivation(); |
2219 | | }; |
2220 | | |
2221 | | class ScriptFrameIter : public FrameIter |
2222 | | { |
2223 | 0 | void settle() { |
2224 | 0 | while (!done() && !hasScript()) { |
2225 | 0 | FrameIter::operator++(); |
2226 | 0 | } |
2227 | 0 | } |
2228 | | |
2229 | | public: |
2230 | | explicit ScriptFrameIter(JSContext* cx, |
2231 | | DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK) |
2232 | | : FrameIter(cx, debuggerEvalOption) |
2233 | 0 | { |
2234 | 0 | settle(); |
2235 | 0 | } |
2236 | | |
2237 | | ScriptFrameIter(JSContext* cx, |
2238 | | DebuggerEvalOption debuggerEvalOption, |
2239 | | JSPrincipals* prin) |
2240 | | : FrameIter(cx, debuggerEvalOption, prin) |
2241 | 0 | { |
2242 | 0 | settle(); |
2243 | 0 | } |
2244 | | |
2245 | 0 | ScriptFrameIter(const ScriptFrameIter& iter) : FrameIter(iter) { settle(); } |
2246 | 0 | explicit ScriptFrameIter(const FrameIter::Data& data) : FrameIter(data) { settle(); } |
2247 | 0 | explicit ScriptFrameIter(AbstractFramePtr frame) : FrameIter(frame) { settle(); } |
2248 | | |
2249 | 0 | ScriptFrameIter& operator++() { |
2250 | 0 | FrameIter::operator++(); |
2251 | 0 | settle(); |
2252 | 0 | return *this; |
2253 | 0 | } |
2254 | | }; |
2255 | | |
2256 | | #ifdef DEBUG |
2257 | | bool SelfHostedFramesVisible(); |
2258 | | #else |
2259 | | static inline bool |
2260 | | SelfHostedFramesVisible() |
2261 | 0 | { |
2262 | 0 | return false; |
2263 | 0 | } Unexecuted instantiation: CTypes.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: StoreBuffer.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: jsutil.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: StructuredClone.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src0.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src1.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src10.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src11.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src12.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src14.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src15.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src17.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src18.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src19.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src2.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src20.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src21.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src22.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src23.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src24.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src25.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src26.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src27.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src28.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src29.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src3.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src30.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src31.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src32.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src33.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src34.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src35.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src36.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src37.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src38.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src39.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src4.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src40.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src41.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src42.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src43.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src44.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src45.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src5.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src6.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src7.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src8.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src9.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: RegExp.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: BinSource-auto.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: BinSource.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: BinToken.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: BinTokenReaderBase.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: BinTokenReaderMultipart.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: BinTokenReaderTester.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Parser.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Disassembler-x86-shared.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: jsmath.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Interpreter.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: VTuneWrapper.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src13.cpp:js::SelfHostedFramesVisible() Unexecuted instantiation: Unified_cpp_js_src16.cpp:js::SelfHostedFramesVisible() |
2264 | | #endif |
2265 | | |
2266 | | /* A filtering of the FrameIter to only stop at non-self-hosted scripts. */ |
2267 | | class NonBuiltinFrameIter : public FrameIter |
2268 | | { |
2269 | | void settle(); |
2270 | | |
2271 | | public: |
2272 | | explicit NonBuiltinFrameIter(JSContext* cx, |
2273 | | FrameIter::DebuggerEvalOption debuggerEvalOption = |
2274 | | FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK) |
2275 | | : FrameIter(cx, debuggerEvalOption) |
2276 | 0 | { |
2277 | 0 | settle(); |
2278 | 0 | } |
2279 | | |
2280 | | NonBuiltinFrameIter(JSContext* cx, |
2281 | | FrameIter::DebuggerEvalOption debuggerEvalOption, |
2282 | | JSPrincipals* principals) |
2283 | | : FrameIter(cx, debuggerEvalOption, principals) |
2284 | 0 | { |
2285 | 0 | settle(); |
2286 | 0 | } |
2287 | | |
2288 | | NonBuiltinFrameIter(JSContext* cx, JSPrincipals* principals) |
2289 | | : FrameIter(cx, FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK, principals) |
2290 | 0 | { |
2291 | 0 | settle(); |
2292 | 0 | } |
2293 | | |
2294 | | explicit NonBuiltinFrameIter(const FrameIter::Data& data) |
2295 | | : FrameIter(data) |
2296 | 0 | {} |
2297 | | |
2298 | 0 | NonBuiltinFrameIter& operator++() { |
2299 | 0 | FrameIter::operator++(); |
2300 | 0 | settle(); |
2301 | 0 | return *this; |
2302 | 0 | } |
2303 | | }; |
2304 | | |
2305 | | /* A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts. */ |
2306 | | class NonBuiltinScriptFrameIter : public ScriptFrameIter |
2307 | | { |
2308 | | void settle(); |
2309 | | |
2310 | | public: |
2311 | | explicit NonBuiltinScriptFrameIter(JSContext* cx, |
2312 | | ScriptFrameIter::DebuggerEvalOption debuggerEvalOption = |
2313 | | ScriptFrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK) |
2314 | | : ScriptFrameIter(cx, debuggerEvalOption) |
2315 | 0 | { |
2316 | 0 | settle(); |
2317 | 0 | } |
2318 | | |
2319 | | NonBuiltinScriptFrameIter(JSContext* cx, |
2320 | | ScriptFrameIter::DebuggerEvalOption debuggerEvalOption, |
2321 | | JSPrincipals* principals) |
2322 | | : ScriptFrameIter(cx, debuggerEvalOption, principals) |
2323 | 0 | { |
2324 | 0 | settle(); |
2325 | 0 | } |
2326 | | |
2327 | | explicit NonBuiltinScriptFrameIter(const ScriptFrameIter::Data& data) |
2328 | | : ScriptFrameIter(data) |
2329 | 0 | {} |
2330 | | |
2331 | 0 | NonBuiltinScriptFrameIter& operator++() { |
2332 | 0 | ScriptFrameIter::operator++(); |
2333 | 0 | settle(); |
2334 | 0 | return *this; |
2335 | 0 | } |
2336 | | }; |
2337 | | |
2338 | | /* |
2339 | | * Blindly iterate over all frames in the current thread's stack. These frames |
2340 | | * can be from different contexts and compartments, so beware. |
2341 | | */ |
2342 | | class AllFramesIter : public FrameIter |
2343 | | { |
2344 | | public: |
2345 | | explicit AllFramesIter(JSContext* cx) |
2346 | | : FrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) |
2347 | 0 | {} |
2348 | | }; |
2349 | | |
2350 | | /* Iterates over all script frame in the current thread's stack. |
2351 | | * See also AllFramesIter and ScriptFrameIter. |
2352 | | */ |
2353 | | class AllScriptFramesIter : public ScriptFrameIter |
2354 | | { |
2355 | | public: |
2356 | | explicit AllScriptFramesIter(JSContext* cx) |
2357 | | : ScriptFrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) |
2358 | 0 | {} |
2359 | | }; |
2360 | | |
2361 | | /* Popular inline definitions. */ |
2362 | | |
2363 | | inline JSScript* |
2364 | | FrameIter::script() const |
2365 | 0 | { |
2366 | 0 | MOZ_ASSERT(!done()); |
2367 | 0 | MOZ_ASSERT(hasScript()); |
2368 | 0 | if (data_.state_ == INTERP) { |
2369 | 0 | return interpFrame()->script(); |
2370 | 0 | } |
2371 | 0 | if (jsJitFrame().isIonJS()) { |
2372 | 0 | return ionInlineFrames_.script(); |
2373 | 0 | } |
2374 | 0 | return jsJitFrame().script(); |
2375 | 0 | } |
2376 | | |
2377 | | inline bool |
2378 | | FrameIter::wasmDebugEnabled() const |
2379 | 0 | { |
2380 | 0 | MOZ_ASSERT(!done()); |
2381 | 0 | MOZ_ASSERT(isWasm()); |
2382 | 0 | return wasmFrame().debugEnabled(); |
2383 | 0 | } |
2384 | | |
2385 | | inline wasm::Instance* |
2386 | | FrameIter::wasmInstance() const |
2387 | 0 | { |
2388 | 0 | MOZ_ASSERT(!done()); |
2389 | 0 | MOZ_ASSERT(isWasm()); |
2390 | 0 | return wasmFrame().instance(); |
2391 | 0 | } |
2392 | | |
2393 | | inline unsigned |
2394 | | FrameIter::wasmBytecodeOffset() const |
2395 | 0 | { |
2396 | 0 | MOZ_ASSERT(!done()); |
2397 | 0 | MOZ_ASSERT(isWasm()); |
2398 | 0 | return wasmFrame().lineOrBytecode(); |
2399 | 0 | } |
2400 | | |
2401 | | inline uint32_t |
2402 | | FrameIter::wasmFuncIndex() const |
2403 | 0 | { |
2404 | 0 | MOZ_ASSERT(!done()); |
2405 | 0 | MOZ_ASSERT(isWasm()); |
2406 | 0 | return wasmFrame().funcIndex(); |
2407 | 0 | } |
2408 | | |
2409 | | inline bool |
2410 | | FrameIter::isIon() const |
2411 | 0 | { |
2412 | 0 | return isJSJit() && jsJitFrame().isIonJS(); |
2413 | 0 | } |
2414 | | |
2415 | | inline bool |
2416 | | FrameIter::isBaseline() const |
2417 | 0 | { |
2418 | 0 | return isJSJit() && jsJitFrame().isBaselineJS(); |
2419 | 0 | } |
2420 | | |
2421 | | inline InterpreterFrame* |
2422 | | FrameIter::interpFrame() const |
2423 | 8 | { |
2424 | 8 | MOZ_ASSERT(data_.state_ == INTERP); |
2425 | 8 | return data_.interpFrames_.frame(); |
2426 | 8 | } |
2427 | | |
2428 | | inline bool |
2429 | | FrameIter::isPhysicalJitFrame() const |
2430 | 0 | { |
2431 | 0 | if (!isJSJit()) { |
2432 | 0 | return false; |
2433 | 0 | } |
2434 | 0 | |
2435 | 0 | auto& jitFrame = jsJitFrame(); |
2436 | 0 |
|
2437 | 0 | if (jitFrame.isBaselineJS()) { |
2438 | 0 | return true; |
2439 | 0 | } |
2440 | 0 | |
2441 | 0 | if (jitFrame.isIonScripted()) { |
2442 | 0 | // Only the bottom of a group of inlined Ion frames is a physical frame. |
2443 | 0 | return ionInlineFrames_.frameNo() == 0; |
2444 | 0 | } |
2445 | 0 | |
2446 | 0 | return false; |
2447 | 0 | } |
2448 | | |
2449 | | inline jit::CommonFrameLayout* |
2450 | | FrameIter::physicalJitFrame() const |
2451 | 0 | { |
2452 | 0 | MOZ_ASSERT(isPhysicalJitFrame()); |
2453 | 0 | return jsJitFrame().current(); |
2454 | 0 | } |
2455 | | |
2456 | | } /* namespace js */ |
2457 | | |
2458 | | #endif /* vm_Stack_h */ |