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