FrameIter.h (17594B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 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_FrameIter_h 8 #define vm_FrameIter_h 9 10 #include "mozilla/Assertions.h" // MOZ_ASSERT 11 #include "mozilla/Attributes.h" // MOZ_IMPLICIT, MOZ_RAII 12 #include "mozilla/MaybeOneOf.h" // mozilla::MaybeOneOf 13 14 #include <stddef.h> // size_t 15 #include <stdint.h> // uint8_t, uint32_t, uintptr_t 16 17 #include "jstypes.h" // JS_PUBLIC_API 18 19 #include "jit/JSJitFrameIter.h" // js::jit::{InlineFrameIterator,JSJitFrameIter} 20 #include "js/ColumnNumber.h" // JS::TaggedColumnNumberOneOrigin 21 #include "js/RootingAPI.h" // JS::Handle 22 #include "js/TypeDecls.h" // jsbytecode, JSContext, JSAtom, JSFunction, JSObject, JSScript 23 #include "js/Value.h" // JS::Value 24 #include "vm/Activation.h" // js::InterpreterActivation 25 #include "vm/Stack.h" // js::{AbstractFramePtr,MaybeCheckAliasing} 26 #include "wasm/WasmFrameIter.h" // js::wasm::{ExitReason,RegisterState,WasmFrameIter} 27 28 struct JSPrincipals; 29 30 namespace JS { 31 32 class JS_PUBLIC_API Compartment; 33 class JS_PUBLIC_API Realm; 34 35 } // namespace JS 36 37 namespace js { 38 39 class ArgumentsObject; 40 class CallObject; 41 42 namespace jit { 43 class CommonFrameLayout; 44 class JitActivation; 45 } // namespace jit 46 47 namespace wasm { 48 class Instance; 49 } // namespace wasm 50 51 // Iterates over the frames of a single InterpreterActivation. 52 class InterpreterFrameIterator { 53 InterpreterActivation* activation_; 54 InterpreterFrame* fp_; 55 jsbytecode* pc_; 56 JS::Value* sp_; 57 58 public: 59 explicit InterpreterFrameIterator(InterpreterActivation* activation) 60 : activation_(activation), fp_(nullptr), pc_(nullptr), sp_(nullptr) { 61 if (activation) { 62 fp_ = activation->current(); 63 pc_ = activation->regs().pc; 64 sp_ = activation->regs().sp; 65 } 66 } 67 68 InterpreterFrame* frame() const { 69 MOZ_ASSERT(!done()); 70 return fp_; 71 } 72 jsbytecode* pc() const { 73 MOZ_ASSERT(!done()); 74 return pc_; 75 } 76 JS::Value* sp() const { 77 MOZ_ASSERT(!done()); 78 return sp_; 79 } 80 81 InterpreterFrameIterator& operator++(); 82 83 bool done() const { return fp_ == nullptr; } 84 }; 85 86 // A JitFrameIter can iterate over all kind of frames emitted by our code 87 // generators, be they composed of JS jit frames or wasm frames, interleaved or 88 // not, in any order. 89 // 90 // In the following class: 91 // - code generated for JS is referred to as JSJit. 92 // - code generated for wasm is referred to as Wasm. 93 // Also, Jit refers to any one of them. 94 // 95 // JitFrameIter uses JSJitFrameIter to iterate over JSJit code or a 96 // WasmFrameIter to iterate over wasm code; only one of them is active at the 97 // time. When a sub-iterator is done, the JitFrameIter knows how to stop, move 98 // onto the next activation or move onto another kind of Jit code. 99 // 100 // For ease of use, there is also OnlyJSJitFrameIter, which skips all the 101 // non-JSJit frames. 102 // 103 // Note it is allowed to get a handle to the internal frame iterator via 104 // asJSJit() and asWasm(), but the user has to be careful not to have those be 105 // used after JitFrameIter leaves the scope or the operator++ is called. 106 // 107 // In particular, this can handle the transition from wasm to jit and from jit 108 // to wasm, since these can be interleaved in the same JitActivation. 109 class JitFrameIter { 110 protected: 111 jit::JitActivation* act_ = nullptr; 112 mozilla::MaybeOneOf<jit::JSJitFrameIter, wasm::WasmFrameIter> iter_ = {}; 113 bool mustUnwindActivation_ = false; 114 115 void settle(); 116 117 public: 118 JitFrameIter() = default; 119 120 explicit JitFrameIter(jit::JitActivation* activation, 121 bool mustUnwindActivation = false); 122 123 explicit JitFrameIter(const JitFrameIter& another); 124 JitFrameIter& operator=(const JitFrameIter& another); 125 126 bool isSome() const { return !iter_.empty(); } 127 void reset() { 128 MOZ_ASSERT(isSome()); 129 iter_.destroy(); 130 } 131 132 bool isJSJit() const { 133 return isSome() && iter_.constructed<jit::JSJitFrameIter>(); 134 } 135 jit::JSJitFrameIter& asJSJit() { return iter_.ref<jit::JSJitFrameIter>(); } 136 const jit::JSJitFrameIter& asJSJit() const { 137 return iter_.ref<jit::JSJitFrameIter>(); 138 } 139 140 bool isWasm() const { 141 return isSome() && iter_.constructed<wasm::WasmFrameIter>(); 142 } 143 wasm::WasmFrameIter& asWasm() { return iter_.ref<wasm::WasmFrameIter>(); } 144 const wasm::WasmFrameIter& asWasm() const { 145 return iter_.ref<wasm::WasmFrameIter>(); 146 } 147 148 // Operations common to all frame iterators. 149 const jit::JitActivation* activation() const { return act_; } 150 bool done() const; 151 void operator++(); 152 153 JS::Realm* realm() const; 154 155 // Returns the address of the next instruction that will execute in this 156 // frame, once control returns to this frame. 157 uint8_t* resumePCinCurrentFrame() const; 158 159 // Operations which have an effect only on JIT frames. 160 void skipNonScriptedJSFrames(); 161 162 // Returns true iff this is a JIT frame with a self-hosted script. Note: be 163 // careful, JitFrameIter does not consider functions inlined by Ion. 164 bool isSelfHostedIgnoringInlining() const; 165 }; 166 167 // A JitFrameIter that skips all the non-JSJit frames, skipping interleaved 168 // frames of any another kind. 169 170 class OnlyJSJitFrameIter : public JitFrameIter { 171 void settle() { 172 while (!done() && !isJSJit()) { 173 JitFrameIter::operator++(); 174 } 175 } 176 177 public: 178 explicit OnlyJSJitFrameIter(jit::JitActivation* act); 179 explicit OnlyJSJitFrameIter(const ActivationIterator& cx); 180 181 void operator++() { 182 JitFrameIter::operator++(); 183 settle(); 184 } 185 186 const jit::JSJitFrameIter& frame() const { return asJSJit(); } 187 }; 188 189 class ScriptSource; 190 191 // A FrameIter walks over a context's stack of JS script activations, 192 // abstracting over whether the JS scripts were running in the interpreter or 193 // different modes of compiled code. 194 // 195 // FrameIter is parameterized by what it includes in the stack iteration: 196 // - When provided, the optional JSPrincipal argument will cause FrameIter to 197 // only show frames in globals whose JSPrincipals are subsumed (via 198 // JSSecurityCallbacks::subsume) by the given JSPrincipal. 199 // 200 // Additionally, there are derived FrameIter types that automatically skip 201 // certain frames: 202 // - ScriptFrameIter only shows frames that have an associated JSScript 203 // (currently everything other than wasm stack frames). When !hasScript(), 204 // clients must stick to the portion of the 205 // interface marked below. 206 // - NonBuiltinScriptFrameIter additionally filters out builtin (self-hosted) 207 // scripts. 208 class FrameIter { 209 public: 210 enum DebuggerEvalOption { 211 FOLLOW_DEBUGGER_EVAL_PREV_LINK, 212 IGNORE_DEBUGGER_EVAL_PREV_LINK 213 }; 214 215 enum State { 216 DONE, // when there are no more frames nor activations to unwind. 217 INTERP, // interpreter activation on the stack 218 JIT // jit or wasm activations on the stack 219 }; 220 221 // Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on 222 // the heap, so this structure should not contain any GC things. 223 struct Data { 224 JSContext* cx_; 225 DebuggerEvalOption debuggerEvalOption_; 226 JSPrincipals* principals_; 227 228 State state_; 229 230 jsbytecode* pc_; 231 232 InterpreterFrameIterator interpFrames_; 233 ActivationIterator activations_; 234 235 JitFrameIter jitFrames_; 236 unsigned ionInlineFrameNo_; 237 238 Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, 239 JSPrincipals* principals); 240 Data(const Data& other); 241 }; 242 243 explicit FrameIter(JSContext* cx, 244 DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK); 245 FrameIter(JSContext* cx, DebuggerEvalOption, JSPrincipals*); 246 FrameIter(const FrameIter& iter); 247 MOZ_IMPLICIT FrameIter(const Data& data); 248 249 bool done() const { return data_.state_ == DONE; } 250 251 // ------------------------------------------------------- 252 // The following functions can only be called when !done() 253 // ------------------------------------------------------- 254 255 FrameIter& operator++(); 256 257 JS::Realm* realm() const; 258 JS::Compartment* compartment() const; 259 Activation* activation() const { return data_.activations_.activation(); } 260 261 bool isInterp() const { 262 MOZ_ASSERT(!done()); 263 return data_.state_ == INTERP; 264 } 265 bool isJSJit() const { 266 MOZ_ASSERT(!done()); 267 return data_.state_ == JIT && data_.jitFrames_.isJSJit(); 268 } 269 bool isWasm() const { 270 MOZ_ASSERT(!done()); 271 return data_.state_ == JIT && data_.jitFrames_.isWasm(); 272 } 273 274 inline bool isIon() const; 275 inline bool isBaseline() const; 276 inline bool isPhysicalJitFrame() const; 277 278 bool isEvalFrame() const; 279 bool isModuleFrame() const; 280 bool isFunctionFrame() const; 281 bool hasArgs() const { return isFunctionFrame(); } 282 283 ScriptSource* scriptSource() const; 284 const char* filename() const; 285 const char16_t* displayURL() const; 286 unsigned computeLine(JS::TaggedColumnNumberOneOrigin* column = nullptr) const; 287 JSAtom* maybeFunctionDisplayAtom() const; 288 bool mutedErrors() const; 289 290 bool hasScript() const { return !isWasm(); } 291 292 // ----------------------------------------------------------- 293 // The following functions can only be called when isWasm() 294 // ----------------------------------------------------------- 295 296 inline bool wasmDebugEnabled() const; 297 inline wasm::Instance* wasmInstance() const; 298 inline uint32_t wasmFuncIndex() const; 299 inline unsigned wasmBytecodeOffset() const; 300 void wasmUpdateBytecodeOffset(); 301 302 // ----------------------------------------------------------- 303 // The following functions can only be called when hasScript() 304 // ----------------------------------------------------------- 305 306 inline JSScript* script() const; 307 308 bool isConstructing() const; 309 jsbytecode* pc() const { 310 MOZ_ASSERT(!done()); 311 return data_.pc_; 312 } 313 void updatePcQuadratic(); 314 315 // The function |calleeTemplate()| returns either the function from which 316 // the current |callee| was cloned or the |callee| if it can be read. As 317 // long as we do not have to investigate the environment chain or build a 318 // new frame, we should prefer to use |calleeTemplate| instead of 319 // |callee|, as requesting the |callee| might cause the invalidation of 320 // the frame. (see js::Lambda) 321 JSFunction* calleeTemplate() const; 322 JSFunction* callee(JSContext* cx) const; 323 324 JSFunction* maybeCallee(JSContext* cx) const { 325 return isFunctionFrame() ? callee(cx) : nullptr; 326 } 327 328 bool matchCallee(JSContext* cx, JS::Handle<JSFunction*> fun) const; 329 330 unsigned numActualArgs() const; 331 unsigned numFormalArgs() const; 332 JS::Value unaliasedActual(unsigned i, 333 MaybeCheckAliasing = CHECK_ALIASING) const; 334 template <class Op> 335 inline void unaliasedForEachActual(JSContext* cx, Op op); 336 337 JSObject* environmentChain(JSContext* cx) const; 338 bool hasInitialEnvironment(JSContext* cx) const; 339 CallObject& callObj(JSContext* cx) const; 340 341 bool hasArgsObj() const; 342 ArgumentsObject& argsObj() const; 343 344 // Get the original |this| value passed to this function. May not be the 345 // actual this-binding (for instance, derived class constructors will 346 // change their this-value later and non-strict functions will box 347 // primitives). 348 JS::Value thisArgument(JSContext* cx) const; 349 350 JS::Value returnValue() const; 351 void setReturnValue(const JS::Value& v); 352 353 // These are only valid for the top frame. 354 size_t numFrameSlots() const; 355 JS::Value frameSlotValue(size_t index) const; 356 357 // Ensures that we have rematerialized the top frame and its associated 358 // inline frames. Can only be called when isIon(). 359 bool ensureHasRematerializedFrame(JSContext* cx); 360 361 // True when isInterp() or isBaseline(). True when isIon() if it 362 // has a rematerialized frame. False otherwise. 363 bool hasUsableAbstractFramePtr() const; 364 365 // ----------------------------------------------------------- 366 // The following functions can only be called when isInterp(), 367 // isBaseline(), isWasm() or isIon(). Further, abstractFramePtr() can 368 // only be called when hasUsableAbstractFramePtr(). 369 // ----------------------------------------------------------- 370 371 AbstractFramePtr abstractFramePtr() const; 372 Data* copyData() const; 373 374 // This can only be called when isInterp(): 375 inline InterpreterFrame* interpFrame() const; 376 377 // This can only be called when isPhysicalJitFrame(): 378 inline jit::CommonFrameLayout* physicalJitFrame() const; 379 380 // This is used to provide a raw interface for debugging. 381 void* rawFramePtr() const; 382 383 bool inPrologue() const; 384 385 const wasm::WasmFrameIter& wasmFrame() const { 386 return data_.jitFrames_.asWasm(); 387 } 388 wasm::WasmFrameIter& wasmFrame() { return data_.jitFrames_.asWasm(); } 389 390 private: 391 Data data_; 392 jit::InlineFrameIterator ionInlineFrames_; 393 394 const jit::JSJitFrameIter& jsJitFrame() const { 395 return data_.jitFrames_.asJSJit(); 396 } 397 398 jit::JSJitFrameIter& jsJitFrame() { return data_.jitFrames_.asJSJit(); } 399 400 bool isIonScripted() const { 401 return isJSJit() && jsJitFrame().isIonScripted(); 402 } 403 404 bool principalsSubsumeFrame() const; 405 406 void popActivation(); 407 void popInterpreterFrame(); 408 void nextJitFrame(); 409 void popJitFrame(); 410 void settleOnActivation(); 411 }; 412 413 class ScriptFrameIter : public FrameIter { 414 void settle() { 415 while (!done() && !hasScript()) { 416 FrameIter::operator++(); 417 } 418 } 419 420 public: 421 explicit ScriptFrameIter( 422 JSContext* cx, 423 DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK) 424 : FrameIter(cx, debuggerEvalOption) { 425 settle(); 426 } 427 428 ScriptFrameIter& operator++() { 429 FrameIter::operator++(); 430 settle(); 431 return *this; 432 } 433 }; 434 435 #ifdef DEBUG 436 bool SelfHostedFramesVisible(); 437 #else 438 static inline bool SelfHostedFramesVisible() { return false; } 439 #endif 440 441 /* A filtering of the FrameIter to only stop at non-self-hosted scripts. */ 442 class NonBuiltinFrameIter : public FrameIter { 443 void settle(); 444 445 public: 446 explicit NonBuiltinFrameIter( 447 JSContext* cx, FrameIter::DebuggerEvalOption debuggerEvalOption = 448 FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK) 449 : FrameIter(cx, debuggerEvalOption) { 450 settle(); 451 } 452 453 NonBuiltinFrameIter(JSContext* cx, 454 FrameIter::DebuggerEvalOption debuggerEvalOption, 455 JSPrincipals* principals) 456 : FrameIter(cx, debuggerEvalOption, principals) { 457 settle(); 458 } 459 460 NonBuiltinFrameIter(JSContext* cx, JSPrincipals* principals) 461 : FrameIter(cx, FrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK, principals) { 462 settle(); 463 } 464 465 NonBuiltinFrameIter& operator++() { 466 FrameIter::operator++(); 467 settle(); 468 return *this; 469 } 470 }; 471 472 // A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts. 473 class NonBuiltinScriptFrameIter : public ScriptFrameIter { 474 void settle(); 475 476 public: 477 explicit NonBuiltinScriptFrameIter( 478 JSContext* cx, ScriptFrameIter::DebuggerEvalOption debuggerEvalOption = 479 ScriptFrameIter::FOLLOW_DEBUGGER_EVAL_PREV_LINK) 480 : ScriptFrameIter(cx, debuggerEvalOption) { 481 settle(); 482 } 483 484 NonBuiltinScriptFrameIter& operator++() { 485 ScriptFrameIter::operator++(); 486 settle(); 487 return *this; 488 } 489 }; 490 491 /* 492 * Blindly iterate over all frames in the current thread's stack. These frames 493 * can be from different contexts and compartments, so beware. 494 */ 495 class AllFramesIter : public FrameIter { 496 public: 497 explicit AllFramesIter(JSContext* cx) 498 : FrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) {} 499 }; 500 501 /* Iterates over all script frame in the current thread's stack. 502 * See also AllFramesIter and ScriptFrameIter. 503 */ 504 class AllScriptFramesIter : public ScriptFrameIter { 505 public: 506 explicit AllScriptFramesIter(JSContext* cx) 507 : ScriptFrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) {} 508 }; 509 510 /* Popular inline definitions. */ 511 512 inline JSScript* FrameIter::script() const { 513 MOZ_ASSERT(!done()); 514 MOZ_ASSERT(hasScript()); 515 if (data_.state_ == INTERP) { 516 return interpFrame()->script(); 517 } 518 if (jsJitFrame().isIonJS()) { 519 return ionInlineFrames_.script(); 520 } 521 return jsJitFrame().script(); 522 } 523 524 inline bool FrameIter::wasmDebugEnabled() const { 525 MOZ_ASSERT(!done()); 526 MOZ_ASSERT(isWasm()); 527 return wasmFrame().debugEnabled(); 528 } 529 530 inline wasm::Instance* FrameIter::wasmInstance() const { 531 MOZ_ASSERT(!done()); 532 MOZ_ASSERT(isWasm()); 533 return wasmFrame().instance(); 534 } 535 536 inline unsigned FrameIter::wasmBytecodeOffset() const { 537 MOZ_ASSERT(!done()); 538 MOZ_ASSERT(isWasm()); 539 return wasmFrame().lineOrBytecode(); 540 } 541 542 inline uint32_t FrameIter::wasmFuncIndex() const { 543 MOZ_ASSERT(!done()); 544 MOZ_ASSERT(isWasm()); 545 return wasmFrame().funcIndex(); 546 } 547 548 inline bool FrameIter::isIon() const { 549 return isJSJit() && jsJitFrame().isIonJS(); 550 } 551 552 inline bool FrameIter::isBaseline() const { 553 return isJSJit() && jsJitFrame().isBaselineJS(); 554 } 555 556 inline InterpreterFrame* FrameIter::interpFrame() const { 557 MOZ_ASSERT(data_.state_ == INTERP); 558 return data_.interpFrames_.frame(); 559 } 560 561 inline bool FrameIter::isPhysicalJitFrame() const { 562 if (!isJSJit()) { 563 return false; 564 } 565 566 auto& jitFrame = jsJitFrame(); 567 568 if (jitFrame.isBaselineJS()) { 569 return true; 570 } 571 572 if (jitFrame.isIonScripted()) { 573 // Only the bottom of a group of inlined Ion frames is a physical frame. 574 return ionInlineFrames_.frameNo() == 0; 575 } 576 577 return false; 578 } 579 580 inline jit::CommonFrameLayout* FrameIter::physicalJitFrame() const { 581 MOZ_ASSERT(isPhysicalJitFrame()); 582 return jsJitFrame().current(); 583 } 584 585 } // namespace js 586 587 #endif // vm_FrameIter_h