FrameIter.cpp (28478B)
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 #include "vm/FrameIter-inl.h" 8 9 #include "mozilla/Assertions.h" // MOZ_ASSERT, MOZ_CRASH 10 #include "mozilla/MaybeOneOf.h" // mozilla::MaybeOneOf 11 12 #include <stddef.h> // size_t 13 #include <stdint.h> // uint8_t, uint32_t 14 #include <stdlib.h> // getenv 15 16 #include "jit/BaselineFrame.h" // js::jit::BaselineFrame 17 #include "jit/JitFrames.h" // js::jit::EnsureUnwoundJitExitFrame 18 #include "jit/JSJitFrameIter.h" // js::jit::{FrameType,InlineFrameIterator,JSJitFrameIter,MaybeReadFallback,SnapshotIterator} 19 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::TaggedColumnNumberOneOrigin 20 #include "js/GCAPI.h" // JS::AutoSuppressGCAnalysis 21 #include "js/Principals.h" // JSSubsumesOp 22 #include "vm/Activation.h" // js::Activation{,Iterator} 23 #include "vm/EnvironmentObject.h" // js::CallObject 24 #include "vm/JitActivation.h" // js::jit::JitActivation 25 #include "vm/JSContext.h" // JSContext 26 #include "vm/JSFunction.h" // JSFunction 27 #include "vm/JSScript.h" // js::PCToLineNumber, JSScript, js::ScriptSource 28 #include "vm/Runtime.h" // JSRuntime 29 #include "vm/Stack.h" // js::{AbstractFramePtr,InterpreterFrame,MaybeCheckAliasing} 30 #include "wasm/WasmFrameIter.h" // js::wasm::WasmFrameIter 31 #include "wasm/WasmInstance.h" // js::wasm::Instance 32 33 #include "jit/JSJitFrameIter-inl.h" // js::jit::JSJitFrameIter::baselineFrame{,NumValueSlots} 34 #include "vm/Stack-inl.h" // js::AbstractFramePtr::* 35 36 namespace JS { 37 class JS_PUBLIC_API Realm; 38 } // namespace JS 39 40 namespace js { 41 class ArgumentsObject; 42 } // namespace js 43 44 using JS::Realm; 45 using JS::Value; 46 47 using js::AbstractFramePtr; 48 using js::ArgumentsObject; 49 using js::CallObject; 50 using js::FrameIter; 51 using js::JitFrameIter; 52 using js::NonBuiltinFrameIter; 53 using js::NonBuiltinScriptFrameIter; 54 using js::OnlyJSJitFrameIter; 55 using js::ScriptSource; 56 using js::jit::JSJitFrameIter; 57 58 JitFrameIter::JitFrameIter(const JitFrameIter& another) { *this = another; } 59 60 JitFrameIter& JitFrameIter::operator=(const JitFrameIter& another) { 61 MOZ_ASSERT(this != &another); 62 63 act_ = another.act_; 64 mustUnwindActivation_ = another.mustUnwindActivation_; 65 66 if (isSome()) { 67 iter_.destroy(); 68 } 69 if (!another.isSome()) { 70 return *this; 71 } 72 73 if (another.isJSJit()) { 74 iter_.construct<jit::JSJitFrameIter>(another.asJSJit()); 75 } else { 76 MOZ_ASSERT(another.isWasm()); 77 iter_.construct<wasm::WasmFrameIter>(another.asWasm()); 78 } 79 80 return *this; 81 } 82 83 JitFrameIter::JitFrameIter(jit::JitActivation* act, bool mustUnwindActivation) { 84 act_ = act; 85 mustUnwindActivation_ = mustUnwindActivation; 86 MOZ_ASSERT(act->hasExitFP(), 87 "packedExitFP is used to determine if JSJit or wasm"); 88 if (act->hasJSExitFP()) { 89 iter_.construct<jit::JSJitFrameIter>(act); 90 } else { 91 MOZ_ASSERT(act->hasWasmExitFP()); 92 iter_.construct<wasm::WasmFrameIter>(act); 93 } 94 settle(); 95 } 96 97 void JitFrameIter::skipNonScriptedJSFrames() { 98 if (isJSJit()) { 99 // Stop at the first scripted frame. 100 jit::JSJitFrameIter& frames = asJSJit(); 101 while (!frames.isScripted() && !frames.done()) { 102 ++frames; 103 } 104 settle(); 105 } 106 } 107 108 bool JitFrameIter::isSelfHostedIgnoringInlining() const { 109 MOZ_ASSERT(!done()); 110 111 if (isWasm()) { 112 return false; 113 } 114 115 return asJSJit().script()->selfHosted(); 116 } 117 118 JS::Realm* JitFrameIter::realm() const { 119 MOZ_ASSERT(!done()); 120 121 if (isWasm()) { 122 return asWasm().instance()->realm(); 123 } 124 125 if (asJSJit().isScripted()) { 126 return asJSJit().script()->realm(); 127 } 128 129 MOZ_RELEASE_ASSERT(asJSJit().isTrampolineNative()); 130 return asJSJit().callee()->realm(); 131 } 132 133 uint8_t* JitFrameIter::resumePCinCurrentFrame() const { 134 if (isWasm()) { 135 return asWasm().resumePCinCurrentFrame(); 136 } 137 return asJSJit().resumePCinCurrentFrame(); 138 } 139 140 bool JitFrameIter::done() const { 141 if (!isSome()) { 142 return true; 143 } 144 if (isJSJit()) { 145 return asJSJit().done(); 146 } 147 if (isWasm()) { 148 return asWasm().done(); 149 } 150 MOZ_CRASH("unhandled case"); 151 } 152 153 void JitFrameIter::settle() { 154 if (isJSJit()) { 155 const jit::JSJitFrameIter& jitFrame = asJSJit(); 156 if (jitFrame.type() != jit::FrameType::WasmToJSJit) { 157 return; 158 } 159 160 // Transition from js jit frames to wasm frames: we're on the 161 // wasm-to-jit fast path. The current stack layout is as follows: 162 // (stack grows downward) 163 // 164 // [--------------------] 165 // [WASM FUNC ] 166 // [WASM JIT EXIT FRAME ] 167 // [JIT WASM ENTRY FRAME] <-- we're here. 168 // 169 // So prevFP points to the wasm jit exit FP, maintaing the invariant in 170 // WasmFrameIter that the first frame is an exit frame and can be 171 // popped. 172 173 wasm::Frame* prevFP = (wasm::Frame*)jitFrame.prevFp(); 174 175 if (mustUnwindActivation_) { 176 // The JS-JIT exit frame doesn't support stack switching and will only be 177 // used if original wasm func was on the main stack. 178 #ifdef ENABLE_WASM_JSPI 179 MOZ_ASSERT(!act_->cx()->wasm().findSuspenderForStackAddress( 180 prevFP->wasmCaller())); 181 #endif 182 act_->setWasmExitFP(prevFP, nullptr); 183 } 184 185 iter_.destroy(); 186 iter_.construct<wasm::WasmFrameIter>(act_, prevFP); 187 MOZ_ASSERT(!asWasm().done()); 188 return; 189 } 190 191 if (isWasm()) { 192 const wasm::WasmFrameIter& wasmFrame = asWasm(); 193 if (!wasmFrame.done() || !wasmFrame.unwoundCallerFPIsJSJit()) { 194 return; 195 } 196 197 // Transition from wasm frames to jit frames: we're on the 198 // jit-to-wasm fast path. The current stack layout is as follows: 199 // (stack grows downward) 200 // 201 // [--------------------] 202 // [JIT FRAME ] 203 // [WASM JIT ENTRY FRAME] <-- we're here 204 // 205 // The wasm iterator has saved the previous jit frame pointer for us. 206 207 uint8_t* prevFP = wasmFrame.unwoundCallerFP(); 208 209 if (mustUnwindActivation_) { 210 act_->setJSExitFP(prevFP); 211 } 212 213 iter_.destroy(); 214 iter_.construct<jit::JSJitFrameIter>(act_, prevFP, mustUnwindActivation_); 215 MOZ_ASSERT(!asJSJit().done()); 216 return; 217 } 218 } 219 220 void JitFrameIter::operator++() { 221 MOZ_ASSERT(isSome()); 222 if (isJSJit()) { 223 const jit::JSJitFrameIter& jitFrame = asJSJit(); 224 225 jit::JitFrameLayout* prevFrame = nullptr; 226 if (mustUnwindActivation_ && jitFrame.isScripted()) { 227 prevFrame = jitFrame.jsFrame(); 228 } 229 230 ++asJSJit(); 231 232 if (prevFrame) { 233 // Unwind the frame by updating packedExitFP. This is necessary 234 // so that (1) debugger exception unwind and leave frame hooks 235 // don't see this frame when they use ScriptFrameIter, and (2) 236 // ScriptFrameIter does not crash when accessing an IonScript 237 // that's destroyed by the ionScript->decref call. 238 EnsureUnwoundJitExitFrame(act_, prevFrame); 239 } 240 } else if (isWasm()) { 241 ++asWasm(); 242 } else { 243 MOZ_CRASH("unhandled case"); 244 } 245 settle(); 246 } 247 248 OnlyJSJitFrameIter::OnlyJSJitFrameIter(jit::JitActivation* act) 249 : JitFrameIter(act) { 250 settle(); 251 } 252 253 OnlyJSJitFrameIter::OnlyJSJitFrameIter(const ActivationIterator& iter) 254 : OnlyJSJitFrameIter(iter->asJit()) {} 255 256 /*****************************************************************************/ 257 258 void FrameIter::popActivation() { 259 ++data_.activations_; 260 settleOnActivation(); 261 } 262 263 bool FrameIter::principalsSubsumeFrame() const { 264 // If the caller supplied principals, only show frames which are 265 // subsumed (of the same origin or of an origin accessible) by these 266 // principals. 267 268 MOZ_ASSERT(!done()); 269 270 if (!data_.principals_) { 271 return true; 272 } 273 274 JSSubsumesOp subsumes = data_.cx_->runtime()->securityCallbacks->subsumes; 275 if (!subsumes) { 276 return true; 277 } 278 279 JS::AutoSuppressGCAnalysis nogc; 280 return subsumes(data_.principals_, realm()->principals()); 281 } 282 283 void FrameIter::popInterpreterFrame() { 284 MOZ_ASSERT(data_.state_ == INTERP); 285 286 ++data_.interpFrames_; 287 288 if (data_.interpFrames_.done()) { 289 popActivation(); 290 } else { 291 data_.pc_ = data_.interpFrames_.pc(); 292 } 293 } 294 295 void FrameIter::settleOnActivation() { 296 MOZ_ASSERT(!data_.cx_->inUnsafeCallWithABI); 297 298 while (true) { 299 if (data_.activations_.done()) { 300 data_.state_ = DONE; 301 return; 302 } 303 304 Activation* activation = data_.activations_.activation(); 305 306 if (activation->isJit()) { 307 data_.jitFrames_ = JitFrameIter(activation->asJit()); 308 data_.jitFrames_.skipNonScriptedJSFrames(); 309 if (data_.jitFrames_.done()) { 310 // It's possible to have an JitActivation with no scripted 311 // frames, for instance if we hit an over-recursion during 312 // bailout. 313 ++data_.activations_; 314 continue; 315 } 316 data_.state_ = JIT; 317 nextJitFrame(); 318 return; 319 } 320 321 MOZ_ASSERT(activation->isInterpreter()); 322 323 InterpreterActivation* interpAct = activation->asInterpreter(); 324 data_.interpFrames_ = InterpreterFrameIterator(interpAct); 325 326 // If we OSR'ed into JIT code, skip the interpreter frame so that 327 // the same frame is not reported twice. 328 if (data_.interpFrames_.frame()->runningInJit()) { 329 ++data_.interpFrames_; 330 if (data_.interpFrames_.done()) { 331 ++data_.activations_; 332 continue; 333 } 334 } 335 336 MOZ_ASSERT(!data_.interpFrames_.frame()->runningInJit()); 337 data_.pc_ = data_.interpFrames_.pc(); 338 data_.state_ = INTERP; 339 return; 340 } 341 } 342 343 FrameIter::Data::Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, 344 JSPrincipals* principals) 345 : cx_(cx), 346 debuggerEvalOption_(debuggerEvalOption), 347 principals_(principals), 348 state_(DONE), 349 pc_(nullptr), 350 interpFrames_(nullptr), 351 activations_(cx), 352 ionInlineFrameNo_(0) {} 353 354 FrameIter::Data::Data(const FrameIter::Data& other) = default; 355 356 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption) 357 : data_(cx, debuggerEvalOption, nullptr), 358 ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) { 359 settleOnActivation(); 360 361 // No principals so we can see all frames. 362 MOZ_ASSERT_IF(!done(), principalsSubsumeFrame()); 363 } 364 365 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption, 366 JSPrincipals* principals) 367 : data_(cx, debuggerEvalOption, principals), 368 ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) { 369 settleOnActivation(); 370 371 // If we're not allowed to see this frame, call operator++ to skip this (and 372 // other) cross-origin frames. 373 if (!done() && !principalsSubsumeFrame()) { 374 ++*this; 375 } 376 } 377 378 FrameIter::FrameIter(const FrameIter& other) 379 : data_(other.data_), 380 ionInlineFrames_(other.data_.cx_, 381 isIonScripted() ? &other.ionInlineFrames_ : nullptr) {} 382 383 FrameIter::FrameIter(const Data& data) 384 : data_(data), 385 ionInlineFrames_(data.cx_, isIonScripted() ? &jsJitFrame() : nullptr) { 386 MOZ_ASSERT(data.cx_); 387 if (isIonScripted()) { 388 while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_) { 389 ++ionInlineFrames_; 390 } 391 } 392 } 393 394 void FrameIter::nextJitFrame() { 395 MOZ_ASSERT(data_.jitFrames_.isSome()); 396 397 if (isJSJit()) { 398 if (jsJitFrame().isIonScripted()) { 399 ionInlineFrames_.resetOn(&jsJitFrame()); 400 data_.pc_ = ionInlineFrames_.pc(); 401 } else { 402 MOZ_ASSERT(jsJitFrame().isBaselineJS()); 403 jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_); 404 } 405 return; 406 } 407 408 MOZ_ASSERT(isWasm()); 409 wasmFrame().enableInlinedFrames(); 410 data_.pc_ = nullptr; 411 } 412 413 void FrameIter::popJitFrame() { 414 MOZ_ASSERT(data_.state_ == JIT); 415 MOZ_ASSERT(data_.jitFrames_.isSome()); 416 417 if (isJSJit() && jsJitFrame().isIonScripted() && ionInlineFrames_.more()) { 418 ++ionInlineFrames_; 419 data_.pc_ = ionInlineFrames_.pc(); 420 return; 421 } 422 423 ++data_.jitFrames_; 424 data_.jitFrames_.skipNonScriptedJSFrames(); 425 426 if (!data_.jitFrames_.done()) { 427 nextJitFrame(); 428 } else { 429 data_.jitFrames_.reset(); 430 popActivation(); 431 } 432 } 433 434 FrameIter& FrameIter::operator++() { 435 while (true) { 436 switch (data_.state_) { 437 case DONE: 438 MOZ_CRASH("Unexpected state"); 439 case INTERP: 440 if (interpFrame()->isDebuggerEvalFrame() && 441 data_.debuggerEvalOption_ == FOLLOW_DEBUGGER_EVAL_PREV_LINK) { 442 AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev(); 443 444 popInterpreterFrame(); 445 446 while (!hasUsableAbstractFramePtr() || 447 abstractFramePtr() != eifPrev) { 448 if (data_.state_ == JIT) { 449 popJitFrame(); 450 } else { 451 popInterpreterFrame(); 452 } 453 } 454 455 break; 456 } 457 popInterpreterFrame(); 458 break; 459 case JIT: 460 popJitFrame(); 461 break; 462 } 463 464 if (done() || principalsSubsumeFrame()) { 465 break; 466 } 467 } 468 469 return *this; 470 } 471 472 FrameIter::Data* FrameIter::copyData() const { 473 Data* data = data_.cx_->new_<Data>(data_); 474 if (!data) { 475 return nullptr; 476 } 477 478 if (data && isIonScripted()) { 479 data->ionInlineFrameNo_ = ionInlineFrames_.frameNo(); 480 } 481 return data; 482 } 483 484 void* FrameIter::rawFramePtr() const { 485 switch (data_.state_) { 486 case DONE: 487 return nullptr; 488 case INTERP: 489 return interpFrame(); 490 case JIT: 491 if (isJSJit()) { 492 return jsJitFrame().fp(); 493 } 494 MOZ_ASSERT(isWasm()); 495 return nullptr; 496 } 497 MOZ_CRASH("Unexpected state"); 498 } 499 500 JS::Compartment* FrameIter::compartment() const { 501 switch (data_.state_) { 502 case DONE: 503 break; 504 case INTERP: 505 case JIT: 506 return data_.activations_->compartment(); 507 } 508 MOZ_CRASH("Unexpected state"); 509 } 510 511 Realm* FrameIter::realm() const { 512 MOZ_ASSERT(!done()); 513 514 if (hasScript()) { 515 return script()->realm(); 516 } 517 518 return wasmInstance()->realm(); 519 } 520 521 bool FrameIter::isEvalFrame() const { 522 switch (data_.state_) { 523 case DONE: 524 break; 525 case INTERP: 526 return interpFrame()->isEvalFrame(); 527 case JIT: 528 if (isJSJit()) { 529 if (jsJitFrame().isBaselineJS()) { 530 return jsJitFrame().baselineFrame()->isEvalFrame(); 531 } 532 MOZ_ASSERT(!script()->isForEval()); 533 return false; 534 } 535 MOZ_ASSERT(isWasm()); 536 return false; 537 } 538 MOZ_CRASH("Unexpected state"); 539 } 540 541 bool FrameIter::isModuleFrame() const { 542 MOZ_ASSERT(!done()); 543 544 if (hasScript()) { 545 return script()->isModule(); 546 } 547 MOZ_CRASH("Unexpected state"); 548 } 549 550 bool FrameIter::isFunctionFrame() const { 551 MOZ_ASSERT(!done()); 552 switch (data_.state_) { 553 case DONE: 554 break; 555 case INTERP: 556 return interpFrame()->isFunctionFrame(); 557 case JIT: 558 if (isJSJit()) { 559 if (jsJitFrame().isBaselineJS()) { 560 return jsJitFrame().baselineFrame()->isFunctionFrame(); 561 } 562 return script()->isFunction(); 563 } 564 MOZ_ASSERT(isWasm()); 565 return false; 566 } 567 MOZ_CRASH("Unexpected state"); 568 } 569 570 JSAtom* FrameIter::maybeFunctionDisplayAtom() const { 571 switch (data_.state_) { 572 case DONE: 573 break; 574 case INTERP: 575 case JIT: 576 if (isWasm()) { 577 return wasmFrame().functionDisplayAtom(); 578 } 579 if (isFunctionFrame()) { 580 return calleeTemplate()->fullDisplayAtom(); 581 } 582 return nullptr; 583 } 584 585 MOZ_CRASH("Unexpected state"); 586 } 587 588 ScriptSource* FrameIter::scriptSource() const { 589 switch (data_.state_) { 590 case DONE: 591 break; 592 case INTERP: 593 case JIT: 594 return script()->scriptSource(); 595 } 596 597 MOZ_CRASH("Unexpected state"); 598 } 599 600 const char* FrameIter::filename() const { 601 switch (data_.state_) { 602 case DONE: 603 break; 604 case INTERP: 605 case JIT: 606 if (isWasm()) { 607 return wasmFrame().filename(); 608 } 609 return script()->filename(); 610 } 611 612 MOZ_CRASH("Unexpected state"); 613 } 614 615 const char16_t* FrameIter::displayURL() const { 616 switch (data_.state_) { 617 case DONE: 618 break; 619 case INTERP: 620 case JIT: 621 if (isWasm()) { 622 return wasmFrame().displayURL(); 623 } 624 ScriptSource* ss = script()->scriptSource(); 625 return ss->hasDisplayURL() ? ss->displayURL() : nullptr; 626 } 627 MOZ_CRASH("Unexpected state"); 628 } 629 630 unsigned FrameIter::computeLine(JS::TaggedColumnNumberOneOrigin* column) const { 631 switch (data_.state_) { 632 case DONE: 633 break; 634 case INTERP: 635 case JIT: 636 if (isWasm()) { 637 return wasmFrame().computeLine(column); 638 } 639 JS::LimitedColumnNumberOneOrigin columnNumber; 640 unsigned lineNumber = PCToLineNumber(script(), pc(), &columnNumber); 641 if (column) { 642 *column = JS::TaggedColumnNumberOneOrigin(columnNumber); 643 } 644 return lineNumber; 645 } 646 647 MOZ_CRASH("Unexpected state"); 648 } 649 650 bool FrameIter::mutedErrors() const { 651 switch (data_.state_) { 652 case DONE: 653 break; 654 case INTERP: 655 case JIT: 656 if (isWasm()) { 657 return wasmFrame().mutedErrors(); 658 } 659 return script()->mutedErrors(); 660 } 661 MOZ_CRASH("Unexpected state"); 662 } 663 664 bool FrameIter::isConstructing() const { 665 switch (data_.state_) { 666 case DONE: 667 break; 668 case JIT: 669 MOZ_ASSERT(isJSJit()); 670 if (jsJitFrame().isIonScripted()) { 671 return ionInlineFrames_.isConstructing(); 672 } 673 MOZ_ASSERT(jsJitFrame().isBaselineJS()); 674 return jsJitFrame().isConstructing(); 675 case INTERP: 676 return interpFrame()->isConstructing(); 677 } 678 679 MOZ_CRASH("Unexpected state"); 680 } 681 682 bool FrameIter::ensureHasRematerializedFrame(JSContext* cx) { 683 MOZ_ASSERT(isIon()); 684 return !!activation()->asJit()->getRematerializedFrame(cx, jsJitFrame()); 685 } 686 687 bool FrameIter::hasUsableAbstractFramePtr() const { 688 switch (data_.state_) { 689 case DONE: 690 return false; 691 case JIT: 692 if (isJSJit()) { 693 if (jsJitFrame().isBaselineJS()) { 694 return true; 695 } 696 697 MOZ_ASSERT(jsJitFrame().isIonScripted()); 698 return !!activation()->asJit()->lookupRematerializedFrame( 699 jsJitFrame().fp(), ionInlineFrames_.frameNo()); 700 } 701 MOZ_ASSERT(isWasm()); 702 return wasmFrame().debugEnabled(); 703 case INTERP: 704 return true; 705 } 706 MOZ_CRASH("Unexpected state"); 707 } 708 709 AbstractFramePtr FrameIter::abstractFramePtr() const { 710 MOZ_ASSERT(hasUsableAbstractFramePtr()); 711 switch (data_.state_) { 712 case DONE: 713 break; 714 case JIT: { 715 if (isJSJit()) { 716 if (jsJitFrame().isBaselineJS()) { 717 return jsJitFrame().baselineFrame(); 718 } 719 MOZ_ASSERT(isIonScripted()); 720 return activation()->asJit()->lookupRematerializedFrame( 721 jsJitFrame().fp(), ionInlineFrames_.frameNo()); 722 } 723 MOZ_ASSERT(isWasm()); 724 MOZ_ASSERT(wasmFrame().debugEnabled()); 725 return wasmFrame().debugFrame(); 726 } 727 case INTERP: 728 MOZ_ASSERT(interpFrame()); 729 return AbstractFramePtr(interpFrame()); 730 } 731 MOZ_CRASH("Unexpected state"); 732 } 733 734 void FrameIter::updatePcQuadratic() { 735 switch (data_.state_) { 736 case DONE: 737 break; 738 case INTERP: { 739 InterpreterFrame* frame = interpFrame(); 740 InterpreterActivation* activation = data_.activations_->asInterpreter(); 741 742 // Look for the current frame. 743 data_.interpFrames_ = InterpreterFrameIterator(activation); 744 while (data_.interpFrames_.frame() != frame) { 745 ++data_.interpFrames_; 746 } 747 748 // Update the pc. 749 MOZ_ASSERT(data_.interpFrames_.frame() == frame); 750 data_.pc_ = data_.interpFrames_.pc(); 751 return; 752 } 753 case JIT: 754 if (jsJitFrame().isBaselineJS()) { 755 jit::BaselineFrame* frame = jsJitFrame().baselineFrame(); 756 jit::JitActivation* activation = data_.activations_->asJit(); 757 758 // activation's exitFP may be invalid, so create a new 759 // activation iterator. 760 data_.activations_ = ActivationIterator(data_.cx_); 761 while (data_.activations_.activation() != activation) { 762 ++data_.activations_; 763 } 764 765 // Look for the current frame. 766 data_.jitFrames_ = JitFrameIter(data_.activations_->asJit()); 767 while (!isJSJit() || !jsJitFrame().isBaselineJS() || 768 jsJitFrame().baselineFrame() != frame) { 769 ++data_.jitFrames_; 770 } 771 772 // Update the pc. 773 MOZ_ASSERT(jsJitFrame().baselineFrame() == frame); 774 jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_); 775 return; 776 } 777 break; 778 } 779 MOZ_CRASH("Unexpected state"); 780 } 781 782 void FrameIter::wasmUpdateBytecodeOffset() { 783 MOZ_RELEASE_ASSERT(isWasm(), "Unexpected state"); 784 785 wasm::DebugFrame* frame = wasmFrame().debugFrame(); 786 787 // Relookup the current frame, updating the bytecode offset in the process. 788 data_.jitFrames_ = JitFrameIter(data_.activations_->asJit()); 789 while (!isWasm() || wasmFrame().debugFrame() != frame) { 790 ++data_.jitFrames_; 791 } 792 793 MOZ_ASSERT(wasmFrame().debugFrame() == frame); 794 } 795 796 JSFunction* FrameIter::calleeTemplate() const { 797 switch (data_.state_) { 798 case DONE: 799 break; 800 case INTERP: 801 MOZ_ASSERT(isFunctionFrame()); 802 return &interpFrame()->callee(); 803 case JIT: 804 if (jsJitFrame().isBaselineJS()) { 805 return jsJitFrame().callee(); 806 } 807 MOZ_ASSERT(jsJitFrame().isIonScripted()); 808 return ionInlineFrames_.calleeTemplate(); 809 } 810 MOZ_CRASH("Unexpected state"); 811 } 812 813 JSFunction* FrameIter::callee(JSContext* cx) const { 814 switch (data_.state_) { 815 case DONE: 816 break; 817 case INTERP: 818 return calleeTemplate(); 819 case JIT: 820 if (isIonScripted()) { 821 jit::MaybeReadFallback recover(cx, activation()->asJit(), 822 &jsJitFrame()); 823 return ionInlineFrames_.callee(recover); 824 } 825 MOZ_ASSERT(jsJitFrame().isBaselineJS()); 826 return calleeTemplate(); 827 } 828 MOZ_CRASH("Unexpected state"); 829 } 830 831 bool FrameIter::matchCallee(JSContext* cx, JS::Handle<JSFunction*> fun) const { 832 // Use the calleeTemplate to rule out a match without needing to invalidate to 833 // find the actual callee. The real callee my be a clone of the template which 834 // should *not* be considered a match. 835 JSFunction* currentCallee = calleeTemplate(); 836 837 if (currentCallee->nargs() != fun->nargs()) { 838 return false; 839 } 840 841 if (currentCallee->flags().stableAcrossClones() != 842 fun->flags().stableAcrossClones()) { 843 return false; 844 } 845 846 // The calleeTemplate for a callee will always have the same BaseScript. If 847 // the script clones do not use the same script, they also have a different 848 // group and Ion will not inline them interchangeably. 849 // 850 // See: js::jit::InlineFrameIterator::findNextFrame() 851 if (currentCallee->hasBaseScript() && fun->hasBaseScript()) { 852 if (currentCallee->baseScript() != fun->baseScript()) { 853 return false; 854 } 855 } 856 857 return callee(cx) == fun; 858 } 859 860 unsigned FrameIter::numActualArgs() const { 861 switch (data_.state_) { 862 case DONE: 863 break; 864 case INTERP: 865 MOZ_ASSERT(isFunctionFrame()); 866 return interpFrame()->numActualArgs(); 867 case JIT: 868 if (isIonScripted()) { 869 return ionInlineFrames_.numActualArgs(); 870 } 871 MOZ_ASSERT(jsJitFrame().isBaselineJS()); 872 return jsJitFrame().numActualArgs(); 873 } 874 MOZ_CRASH("Unexpected state"); 875 } 876 877 unsigned FrameIter::numFormalArgs() const { 878 return script()->function()->nargs(); 879 } 880 881 Value FrameIter::unaliasedActual(unsigned i, 882 MaybeCheckAliasing checkAliasing) const { 883 return abstractFramePtr().unaliasedActual(i, checkAliasing); 884 } 885 886 JSObject* FrameIter::environmentChain(JSContext* cx) const { 887 switch (data_.state_) { 888 case DONE: 889 break; 890 case JIT: 891 if (isJSJit()) { 892 if (isIonScripted()) { 893 jit::MaybeReadFallback recover(cx, activation()->asJit(), 894 &jsJitFrame()); 895 return ionInlineFrames_.environmentChain(recover); 896 } 897 return jsJitFrame().baselineFrame()->environmentChain(); 898 } 899 MOZ_ASSERT(isWasm()); 900 return wasmFrame().debugFrame()->environmentChain(); 901 case INTERP: 902 return interpFrame()->environmentChain(); 903 } 904 MOZ_CRASH("Unexpected state"); 905 } 906 907 bool FrameIter::hasInitialEnvironment(JSContext* cx) const { 908 if (hasUsableAbstractFramePtr()) { 909 return abstractFramePtr().hasInitialEnvironment(); 910 } 911 912 if (isWasm()) { 913 // See JSFunction::needsFunctionEnvironmentObjects(). 914 return false; 915 } 916 917 MOZ_ASSERT(isJSJit()); 918 MOZ_ASSERT(isIonScripted()); 919 920 bool hasInitialEnv = false; 921 jit::MaybeReadFallback recover(cx, activation()->asJit(), &jsJitFrame()); 922 ionInlineFrames_.environmentChain(recover, &hasInitialEnv); 923 924 return hasInitialEnv; 925 } 926 927 CallObject& FrameIter::callObj(JSContext* cx) const { 928 MOZ_ASSERT(calleeTemplate()->needsCallObject()); 929 MOZ_ASSERT(hasInitialEnvironment(cx)); 930 931 JSObject* pobj = environmentChain(cx); 932 while (!pobj->is<CallObject>()) { 933 pobj = pobj->enclosingEnvironment(); 934 } 935 return pobj->as<CallObject>(); 936 } 937 938 bool FrameIter::hasArgsObj() const { return abstractFramePtr().hasArgsObj(); } 939 940 ArgumentsObject& FrameIter::argsObj() const { 941 MOZ_ASSERT(hasArgsObj()); 942 return abstractFramePtr().argsObj(); 943 } 944 945 Value FrameIter::thisArgument(JSContext* cx) const { 946 MOZ_ASSERT(isFunctionFrame()); 947 948 switch (data_.state_) { 949 case DONE: 950 break; 951 case JIT: 952 if (isIonScripted()) { 953 jit::MaybeReadFallback recover(cx, activation()->asJit(), 954 &jsJitFrame()); 955 return ionInlineFrames_.thisArgument(recover); 956 } 957 return jsJitFrame().baselineFrame()->thisArgument(); 958 case INTERP: 959 return interpFrame()->thisArgument(); 960 } 961 MOZ_CRASH("Unexpected state"); 962 } 963 964 Value FrameIter::returnValue() const { 965 switch (data_.state_) { 966 case DONE: 967 break; 968 case JIT: 969 if (jsJitFrame().isBaselineJS()) { 970 return jsJitFrame().baselineFrame()->returnValue(); 971 } 972 break; 973 case INTERP: 974 return interpFrame()->returnValue(); 975 } 976 MOZ_CRASH("Unexpected state"); 977 } 978 979 void FrameIter::setReturnValue(const Value& v) { 980 switch (data_.state_) { 981 case DONE: 982 break; 983 case JIT: 984 if (jsJitFrame().isBaselineJS()) { 985 jsJitFrame().baselineFrame()->setReturnValue(v); 986 return; 987 } 988 break; 989 case INTERP: 990 interpFrame()->setReturnValue(v); 991 return; 992 } 993 MOZ_CRASH("Unexpected state"); 994 } 995 996 size_t FrameIter::numFrameSlots() const { 997 switch (data_.state_) { 998 case DONE: 999 break; 1000 case JIT: { 1001 if (isIonScripted()) { 1002 return ionInlineFrames_.snapshotIterator().numAllocations() - 1003 ionInlineFrames_.script()->nfixed(); 1004 } 1005 uint32_t numValueSlots = jsJitFrame().baselineFrameNumValueSlots(); 1006 return numValueSlots - jsJitFrame().script()->nfixed(); 1007 } 1008 case INTERP: 1009 MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base()); 1010 return data_.interpFrames_.sp() - interpFrame()->base(); 1011 } 1012 MOZ_CRASH("Unexpected state"); 1013 } 1014 1015 Value FrameIter::frameSlotValue(size_t index) const { 1016 switch (data_.state_) { 1017 case DONE: 1018 break; 1019 case JIT: 1020 if (isIonScripted()) { 1021 jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator()); 1022 index += ionInlineFrames_.script()->nfixed(); 1023 return si.maybeReadAllocByIndex(index); 1024 } 1025 index += jsJitFrame().script()->nfixed(); 1026 return *jsJitFrame().baselineFrame()->valueSlot(index); 1027 case INTERP: 1028 return interpFrame()->base()[index]; 1029 } 1030 MOZ_CRASH("Unexpected state"); 1031 } 1032 1033 #ifdef DEBUG 1034 bool js::SelfHostedFramesVisible() { 1035 static bool checked = false; 1036 static bool visible = false; 1037 if (!checked) { 1038 checked = true; 1039 char* env = getenv("MOZ_SHOW_ALL_JS_FRAMES"); 1040 visible = !!env; 1041 } 1042 return visible; 1043 } 1044 #endif 1045 1046 void NonBuiltinFrameIter::settle() { 1047 if (!SelfHostedFramesVisible()) { 1048 while (!done() && hasScript() && script()->selfHosted()) { 1049 FrameIter::operator++(); 1050 } 1051 } 1052 } 1053 1054 void NonBuiltinScriptFrameIter::settle() { 1055 if (!SelfHostedFramesVisible()) { 1056 while (!done() && script()->selfHosted()) { 1057 ScriptFrameIter::operator++(); 1058 } 1059 } 1060 } 1061 1062 bool FrameIter::inPrologue() const { 1063 if (pc() < script()->main()) { 1064 return true; 1065 } 1066 // If we do a VM call before pushing locals in baseline, the stack frame will 1067 // not include space for those locals. 1068 if (pc() == script()->code() && isBaseline() && 1069 jsJitFrame().baselineFrameNumValueSlots() < script()->nfixed()) { 1070 return true; 1071 } 1072 1073 return false; 1074 }