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