1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "vm/Stack-inl.h"
8 
9 #include "gc/Marking.h"
10 #include "jit/BaselineFrame.h"
11 #include "jit/JitcodeMap.h"
12 #include "jit/JitCompartment.h"
13 #include "vm/Debugger.h"
14 #include "vm/JSContext.h"
15 #include "vm/Opcodes.h"
16 
17 #include "jit/JSJitFrameIter-inl.h"
18 #include "vm/EnvironmentObject-inl.h"
19 #include "vm/Interpreter-inl.h"
20 #include "vm/Probes-inl.h"
21 
22 using namespace js;
23 
24 using mozilla::ArrayLength;
25 using mozilla::DebugOnly;
26 using mozilla::Maybe;
27 
28 /*****************************************************************************/
29 
initExecuteFrame(JSContext * cx,HandleScript script,AbstractFramePtr evalInFramePrev,const Value & newTargetValue,HandleObject envChain)30 void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script,
31                                         AbstractFramePtr evalInFramePrev,
32                                         const Value& newTargetValue,
33                                         HandleObject envChain) {
34   flags_ = 0;
35   script_ = script;
36 
37   // newTarget = NullValue is an initial sentinel for "please fill me in from
38   // the stack". It should never be passed from Ion code.
39   RootedValue newTarget(cx, newTargetValue);
40   if (script->isDirectEvalInFunction()) {
41     FrameIter iter(cx);
42     if (newTarget.isNull() && iter.hasScript() &&
43         iter.script()->bodyScope()->hasOnChain(ScopeKind::Function)) {
44       newTarget = iter.newTarget();
45     }
46   } else if (evalInFramePrev) {
47     if (newTarget.isNull() && evalInFramePrev.hasScript() &&
48         evalInFramePrev.script()->bodyScope()->hasOnChain(
49             ScopeKind::Function)) {
50       newTarget = evalInFramePrev.newTarget();
51     }
52   }
53 
54   Value* dstvp = (Value*)this - 1;
55   dstvp[0] = newTarget;
56 
57   envChain_ = envChain.get();
58   prev_ = nullptr;
59   prevpc_ = nullptr;
60   prevsp_ = nullptr;
61 
62   evalInFramePrev_ = evalInFramePrev;
63   MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
64 
65   if (script->isDebuggee()) setIsDebuggee();
66 
67 #ifdef DEBUG
68   Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
69 #endif
70 }
71 
isNonGlobalEvalFrame() const72 bool InterpreterFrame::isNonGlobalEvalFrame() const {
73   return isEvalFrame() && script()->bodyScope()->as<EvalScope>().isNonGlobal();
74 }
75 
createRestParameter(JSContext * cx)76 ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) {
77   MOZ_ASSERT(script()->hasRest());
78   unsigned nformal = callee().nargs() - 1, nactual = numActualArgs();
79   unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
80   Value* restvp = argv() + nformal;
81   return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject,
82                                      ObjectGroup::NewArrayKind::UnknownIndex);
83 }
84 
AssertScopeMatchesEnvironment(Scope * scope,JSObject * originalEnv)85 static inline void AssertScopeMatchesEnvironment(Scope* scope,
86                                                  JSObject* originalEnv) {
87 #ifdef DEBUG
88   JSObject* env = originalEnv;
89   for (ScopeIter si(scope); si; si++) {
90     if (si.kind() == ScopeKind::NonSyntactic) {
91       while (env->is<WithEnvironmentObject>() ||
92              env->is<NonSyntacticVariablesObject>() ||
93              (env->is<LexicalEnvironmentObject>() &&
94               !env->as<LexicalEnvironmentObject>().isSyntactic())) {
95         MOZ_ASSERT(!IsSyntacticEnvironment(env));
96         env = &env->as<EnvironmentObject>().enclosingEnvironment();
97       }
98     } else if (si.hasSyntacticEnvironment()) {
99       switch (si.kind()) {
100         case ScopeKind::Function:
101           MOZ_ASSERT(
102               env->as<CallObject>().callee().existingScriptNonDelazifying() ==
103               si.scope()->as<FunctionScope>().script());
104           env = &env->as<CallObject>().enclosingEnvironment();
105           break;
106 
107         case ScopeKind::FunctionBodyVar:
108         case ScopeKind::ParameterExpressionVar:
109           MOZ_ASSERT(&env->as<VarEnvironmentObject>().scope() == si.scope());
110           env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
111           break;
112 
113         case ScopeKind::Lexical:
114         case ScopeKind::SimpleCatch:
115         case ScopeKind::Catch:
116         case ScopeKind::NamedLambda:
117         case ScopeKind::StrictNamedLambda:
118           MOZ_ASSERT(&env->as<LexicalEnvironmentObject>().scope() ==
119                      si.scope());
120           env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
121           break;
122 
123         case ScopeKind::With:
124           MOZ_ASSERT(&env->as<WithEnvironmentObject>().scope() == si.scope());
125           env = &env->as<WithEnvironmentObject>().enclosingEnvironment();
126           break;
127 
128         case ScopeKind::Eval:
129         case ScopeKind::StrictEval:
130           env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
131           break;
132 
133         case ScopeKind::Global:
134           MOZ_ASSERT(env->as<LexicalEnvironmentObject>().isGlobal());
135           env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
136           MOZ_ASSERT(env->is<GlobalObject>());
137           break;
138 
139         case ScopeKind::NonSyntactic:
140           MOZ_CRASH("NonSyntactic should not have a syntactic environment");
141           break;
142 
143         case ScopeKind::Module:
144           MOZ_ASSERT(env->as<ModuleEnvironmentObject>().module().script() ==
145                      si.scope()->as<ModuleScope>().script());
146           env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
147           break;
148 
149         case ScopeKind::WasmInstance:
150           env =
151               &env->as<WasmInstanceEnvironmentObject>().enclosingEnvironment();
152           break;
153 
154         case ScopeKind::WasmFunction:
155           env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
156           break;
157       }
158     }
159   }
160 
161   // In the case of a non-syntactic env chain, the immediate parent of the
162   // outermost non-syntactic env may be the global lexical env, or, if
163   // called from Debugger, a DebugEnvironmentProxy.
164   //
165   // In the case of a syntactic env chain, the outermost env is always a
166   // GlobalObject.
167   MOZ_ASSERT(env->is<GlobalObject>() || IsGlobalLexicalEnvironment(env) ||
168              env->is<DebugEnvironmentProxy>());
169 #endif
170 }
171 
AssertScopeMatchesEnvironment(InterpreterFrame * fp,jsbytecode * pc)172 static inline void AssertScopeMatchesEnvironment(InterpreterFrame* fp,
173                                                  jsbytecode* pc) {
174 #ifdef DEBUG
175   // If we OOMed before fully initializing the environment chain, the scope
176   // and environment will definitely mismatch.
177   if (fp->script()->initialEnvironmentShape() && fp->hasInitialEnvironment())
178     AssertScopeMatchesEnvironment(fp->script()->innermostScope(pc),
179                                   fp->environmentChain());
180 #endif
181 }
182 
initFunctionEnvironmentObjects(JSContext * cx)183 bool InterpreterFrame::initFunctionEnvironmentObjects(JSContext* cx) {
184   return js::InitFunctionEnvironmentObjects(cx, this);
185 }
186 
prologue(JSContext * cx)187 bool InterpreterFrame::prologue(JSContext* cx) {
188   RootedScript script(cx, this->script());
189 
190   MOZ_ASSERT(cx->interpreterRegs().pc == script->code());
191 
192   if (isEvalFrame()) {
193     if (!script->bodyScope()->hasEnvironment()) {
194       MOZ_ASSERT(!script->strict());
195       // Non-strict eval may introduce var bindings that conflict with
196       // lexical bindings in an enclosing lexical scope.
197       RootedObject varObjRoot(cx, &varObj());
198       if (!CheckEvalDeclarationConflicts(cx, script, environmentChain(),
199                                          varObjRoot))
200         return false;
201     }
202     return probes::EnterScript(cx, script, nullptr, this);
203   }
204 
205   if (isGlobalFrame()) {
206     Rooted<LexicalEnvironmentObject*> lexicalEnv(cx);
207     RootedObject varObjRoot(cx);
208     if (script->hasNonSyntacticScope()) {
209       lexicalEnv = &extensibleLexicalEnvironment();
210       varObjRoot = &varObj();
211     } else {
212       lexicalEnv = &cx->global()->lexicalEnvironment();
213       varObjRoot = cx->global();
214     }
215     if (!CheckGlobalDeclarationConflicts(cx, script, lexicalEnv, varObjRoot))
216       return false;
217     return probes::EnterScript(cx, script, nullptr, this);
218   }
219 
220   if (isModuleFrame()) return probes::EnterScript(cx, script, nullptr, this);
221 
222   // At this point, we've yet to push any environments. Check that they
223   // match the enclosing scope.
224   AssertScopeMatchesEnvironment(script->enclosingScope(), environmentChain());
225 
226   MOZ_ASSERT(isFunctionFrame());
227   if (callee().needsFunctionEnvironmentObjects() &&
228       !initFunctionEnvironmentObjects(cx))
229     return false;
230 
231   MOZ_ASSERT_IF(isConstructing(),
232                 thisArgument().isObject() ||
233                     thisArgument().isMagic(JS_UNINITIALIZED_LEXICAL));
234 
235   return probes::EnterScript(cx, script, script->functionNonDelazifying(),
236                              this);
237 }
238 
epilogue(JSContext * cx,jsbytecode * pc)239 void InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) {
240   RootedScript script(cx, this->script());
241   probes::ExitScript(cx, script, script->functionNonDelazifying(),
242                      hasPushedGeckoProfilerFrame());
243 
244   // Check that the scope matches the environment at the point of leaving
245   // the frame.
246   AssertScopeMatchesEnvironment(this, pc);
247 
248   EnvironmentIter ei(cx, this, pc);
249   UnwindAllEnvironmentsInFrame(cx, ei);
250 
251   if (isFunctionFrame()) {
252     if (!callee().isGenerator() && !callee().isAsync() && isConstructing() &&
253         thisArgument().isObject() && returnValue().isPrimitive()) {
254       setReturnValue(thisArgument());
255     }
256 
257     return;
258   }
259 
260   MOZ_ASSERT(isEvalFrame() || isGlobalFrame() || isModuleFrame());
261 }
262 
checkReturn(JSContext * cx,HandleValue thisv)263 bool InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv) {
264   MOZ_ASSERT(script()->isDerivedClassConstructor());
265   MOZ_ASSERT(isFunctionFrame());
266   MOZ_ASSERT(callee().isClassConstructor());
267 
268   HandleValue retVal = returnValue();
269   if (retVal.isObject()) return true;
270 
271   if (!retVal.isUndefined()) {
272     ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal,
273                      nullptr);
274     return false;
275   }
276 
277   if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL))
278     return ThrowUninitializedThis(cx, this);
279 
280   setReturnValue(thisv);
281   return true;
282 }
283 
pushVarEnvironment(JSContext * cx,HandleScope scope)284 bool InterpreterFrame::pushVarEnvironment(JSContext* cx, HandleScope scope) {
285   return js::PushVarEnvironmentObject(cx, scope, this);
286 }
287 
pushLexicalEnvironment(JSContext * cx,Handle<LexicalScope * > scope)288 bool InterpreterFrame::pushLexicalEnvironment(JSContext* cx,
289                                               Handle<LexicalScope*> scope) {
290   LexicalEnvironmentObject* env =
291       LexicalEnvironmentObject::create(cx, scope, this);
292   if (!env) return false;
293 
294   pushOnEnvironmentChain(*env);
295   return true;
296 }
297 
freshenLexicalEnvironment(JSContext * cx)298 bool InterpreterFrame::freshenLexicalEnvironment(JSContext* cx) {
299   Rooted<LexicalEnvironmentObject*> env(
300       cx, &envChain_->as<LexicalEnvironmentObject>());
301   LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::clone(cx, env);
302   if (!fresh) return false;
303 
304   replaceInnermostEnvironment(*fresh);
305   return true;
306 }
307 
recreateLexicalEnvironment(JSContext * cx)308 bool InterpreterFrame::recreateLexicalEnvironment(JSContext* cx) {
309   Rooted<LexicalEnvironmentObject*> env(
310       cx, &envChain_->as<LexicalEnvironmentObject>());
311   LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::recreate(cx, env);
312   if (!fresh) return false;
313 
314   replaceInnermostEnvironment(*fresh);
315   return true;
316 }
317 
trace(JSTracer * trc,Value * sp,jsbytecode * pc)318 void InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc) {
319   TraceRoot(trc, &envChain_, "env chain");
320   TraceRoot(trc, &script_, "script");
321 
322   if (flags_ & HAS_ARGS_OBJ) TraceRoot(trc, &argsObj_, "arguments");
323 
324   if (hasReturnValue()) TraceRoot(trc, &rval_, "rval");
325 
326   MOZ_ASSERT(sp >= slots());
327 
328   if (hasArgs()) {
329     // Trace the callee and |this|. When we're doing a moving GC, we
330     // need to fix up the callee pointer before we use it below, under
331     // numFormalArgs() and script().
332     TraceRootRange(trc, 2, argv_ - 2, "fp callee and this");
333 
334     // Trace arguments.
335     unsigned argc = Max(numActualArgs(), numFormalArgs());
336     TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv");
337   } else {
338     // Trace newTarget.
339     TraceRoot(trc, ((Value*)this) - 1, "stack newTarget");
340   }
341 
342   JSScript* script = this->script();
343   size_t nfixed = script->nfixed();
344   size_t nlivefixed = script->calculateLiveFixed(pc);
345 
346   if (nfixed == nlivefixed) {
347     // All locals are live.
348     traceValues(trc, 0, sp - slots());
349   } else {
350     // Trace operand stack.
351     traceValues(trc, nfixed, sp - slots());
352 
353     // Clear dead block-scoped locals.
354     while (nfixed > nlivefixed) unaliasedLocal(--nfixed).setUndefined();
355 
356     // Trace live locals.
357     traceValues(trc, 0, nlivefixed);
358   }
359 
360   if (script->compartment()->debugEnvs)
361     script->compartment()->debugEnvs->traceLiveFrame(trc, this);
362 }
363 
traceValues(JSTracer * trc,unsigned start,unsigned end)364 void InterpreterFrame::traceValues(JSTracer* trc, unsigned start,
365                                    unsigned end) {
366   if (start < end)
367     TraceRootRange(trc, end - start, slots() + start, "vm_stack");
368 }
369 
TraceInterpreterActivation(JSTracer * trc,InterpreterActivation * act)370 static void TraceInterpreterActivation(JSTracer* trc,
371                                        InterpreterActivation* act) {
372   for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
373     InterpreterFrame* fp = frames.frame();
374     fp->trace(trc, frames.sp(), frames.pc());
375   }
376 }
377 
TraceInterpreterActivations(JSContext * cx,const CooperatingContext & target,JSTracer * trc)378 void js::TraceInterpreterActivations(JSContext* cx,
379                                      const CooperatingContext& target,
380                                      JSTracer* trc) {
381   for (ActivationIterator iter(cx, target); !iter.done(); ++iter) {
382     Activation* act = iter.activation();
383     if (act->isInterpreter())
384       TraceInterpreterActivation(trc, act->asInterpreter());
385   }
386 }
387 
388 /*****************************************************************************/
389 
390 // Unlike the other methods of this class, this method is defined here so that
391 // we don't have to #include jsautooplen.h in vm/Stack.h.
setToEndOfScript()392 void InterpreterRegs::setToEndOfScript() { sp = fp()->base(); }
393 
394 /*****************************************************************************/
395 
pushInvokeFrame(JSContext * cx,const CallArgs & args,MaybeConstruct constructing)396 InterpreterFrame* InterpreterStack::pushInvokeFrame(
397     JSContext* cx, const CallArgs& args, MaybeConstruct constructing) {
398   LifoAlloc::Mark mark = allocator_.mark();
399 
400   RootedFunction fun(cx, &args.callee().as<JSFunction>());
401   RootedScript script(cx, fun->nonLazyScript());
402 
403   Value* argv;
404   InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv);
405   if (!fp) return nullptr;
406 
407   fp->mark_ = mark;
408   fp->initCallFrame(nullptr, nullptr, nullptr, *fun, script, argv,
409                     args.length(), constructing);
410   return fp;
411 }
412 
pushExecuteFrame(JSContext * cx,HandleScript script,const Value & newTargetValue,HandleObject envChain,AbstractFramePtr evalInFrame)413 InterpreterFrame* InterpreterStack::pushExecuteFrame(
414     JSContext* cx, HandleScript script, const Value& newTargetValue,
415     HandleObject envChain, AbstractFramePtr evalInFrame) {
416   LifoAlloc::Mark mark = allocator_.mark();
417 
418   unsigned nvars = 1 /* newTarget */ + script->nslots();
419   uint8_t* buffer =
420       allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
421   if (!buffer) return nullptr;
422 
423   InterpreterFrame* fp =
424       reinterpret_cast<InterpreterFrame*>(buffer + 1 * sizeof(Value));
425   fp->mark_ = mark;
426   fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, envChain);
427   fp->initLocals();
428 
429   return fp;
430 }
431 
432 /*****************************************************************************/
433 
JitFrameIter(const JitFrameIter & another)434 JitFrameIter::JitFrameIter(const JitFrameIter& another) { *this = another; }
435 
operator =(const JitFrameIter & another)436 JitFrameIter& JitFrameIter::operator=(const JitFrameIter& another) {
437   MOZ_ASSERT(this != &another);
438 
439   act_ = another.act_;
440   mustUnwindActivation_ = another.mustUnwindActivation_;
441 
442   if (isSome()) iter_.destroy();
443   if (!another.isSome()) return *this;
444 
445   if (another.isJSJit()) {
446     iter_.construct<jit::JSJitFrameIter>(another.asJSJit());
447   } else {
448     MOZ_ASSERT(another.isWasm());
449     iter_.construct<wasm::WasmFrameIter>(another.asWasm());
450   }
451 
452   return *this;
453 }
454 
JitFrameIter(jit::JitActivation * act,bool mustUnwindActivation)455 JitFrameIter::JitFrameIter(jit::JitActivation* act, bool mustUnwindActivation) {
456   act_ = act;
457   mustUnwindActivation_ = mustUnwindActivation;
458   MOZ_ASSERT(act->hasExitFP(),
459              "packedExitFP is used to determine if JSJit or wasm");
460   if (act->hasJSExitFP()) {
461     iter_.construct<jit::JSJitFrameIter>(act);
462   } else {
463     MOZ_ASSERT(act->hasWasmExitFP());
464     iter_.construct<wasm::WasmFrameIter>(act);
465   }
466   settle();
467 }
468 
skipNonScriptedJSFrames()469 void JitFrameIter::skipNonScriptedJSFrames() {
470   if (isJSJit()) {
471     // Stop at the first scripted frame.
472     jit::JSJitFrameIter& frames = asJSJit();
473     while (!frames.isScripted() && !frames.done()) ++frames;
474     settle();
475   }
476 }
477 
done() const478 bool JitFrameIter::done() const {
479   if (!isSome()) return true;
480   if (isJSJit()) return asJSJit().done();
481   if (isWasm()) return asWasm().done();
482   MOZ_CRASH("unhandled case");
483 }
484 
settle()485 void JitFrameIter::settle() {
486   if (isJSJit()) {
487     const jit::JSJitFrameIter& jitFrame = asJSJit();
488     if (jitFrame.type() != jit::JitFrame_WasmToJSJit) return;
489 
490     // Transition from js jit frames to wasm frames: we're on the
491     // wasm-to-jit fast path. The current stack layout is as follows:
492     // (stack grows downward)
493     //
494     // [--------------------]
495     // [WASM FUNC           ]
496     // [WASM JIT EXIT FRAME ]
497     // [JIT WASM ENTRY FRAME] <-- we're here.
498     //
499     // So prevFP points to the wasm jit exit FP, maintaing the invariant in
500     // WasmFrameIter that the first frame is an exit frame and can be
501     // popped.
502 
503     wasm::Frame* prevFP = (wasm::Frame*)jitFrame.prevFp();
504 
505     if (mustUnwindActivation_) act_->setWasmExitFP(prevFP);
506 
507     iter_.destroy();
508     iter_.construct<wasm::WasmFrameIter>(act_, prevFP);
509     MOZ_ASSERT(!asWasm().done());
510     return;
511   }
512 
513   if (isWasm()) {
514     const wasm::WasmFrameIter& wasmFrame = asWasm();
515     if (!wasmFrame.unwoundIonCallerFP()) return;
516 
517     // Transition from wasm frames to jit frames: we're on the
518     // jit-to-wasm fast path. The current stack layout is as follows:
519     // (stack grows downward)
520     //
521     // [--------------------]
522     // [JIT FRAME           ]
523     // [WASM JIT ENTRY FRAME] <-- we're here
524     //
525     // The wasm iterator has saved the previous jit frame pointer for us.
526 
527     MOZ_ASSERT(wasmFrame.done());
528     uint8_t* prevFP = wasmFrame.unwoundIonCallerFP();
529 
530     if (mustUnwindActivation_) act_->setJSExitFP(prevFP);
531 
532     iter_.destroy();
533     iter_.construct<jit::JSJitFrameIter>(act_, prevFP);
534     MOZ_ASSERT(!asJSJit().done());
535     return;
536   }
537 }
538 
operator ++()539 void JitFrameIter::operator++() {
540   MOZ_ASSERT(isSome());
541   if (isJSJit()) {
542     const jit::JSJitFrameIter& jitFrame = asJSJit();
543 
544     jit::JitFrameLayout* prevFrame = nullptr;
545     if (mustUnwindActivation_ && jitFrame.isScripted())
546       prevFrame = jitFrame.jsFrame();
547 
548     ++asJSJit();
549 
550     if (prevFrame) {
551       // Unwind the frame by updating packedExitFP. This is necessary
552       // so that (1) debugger exception unwind and leave frame hooks
553       // don't see this frame when they use ScriptFrameIter, and (2)
554       // ScriptFrameIter does not crash when accessing an IonScript
555       // that's destroyed by the ionScript->decref call.
556       EnsureBareExitFrame(act_, prevFrame);
557     }
558   } else if (isWasm()) {
559     ++asWasm();
560   } else {
561     MOZ_CRASH("unhandled case");
562   }
563   settle();
564 }
565 
OnlyJSJitFrameIter(jit::JitActivation * act)566 OnlyJSJitFrameIter::OnlyJSJitFrameIter(jit::JitActivation* act)
567     : JitFrameIter(act) {
568   settle();
569 }
570 
OnlyJSJitFrameIter(JSContext * cx)571 OnlyJSJitFrameIter::OnlyJSJitFrameIter(JSContext* cx)
572     : OnlyJSJitFrameIter(cx->activation()->asJit()) {}
573 
OnlyJSJitFrameIter(const ActivationIterator & iter)574 OnlyJSJitFrameIter::OnlyJSJitFrameIter(const ActivationIterator& iter)
575     : OnlyJSJitFrameIter(iter->asJit()) {}
576 
577 /*****************************************************************************/
578 
popActivation()579 void FrameIter::popActivation() {
580   ++data_.activations_;
581   settleOnActivation();
582 }
583 
popInterpreterFrame()584 void FrameIter::popInterpreterFrame() {
585   MOZ_ASSERT(data_.state_ == INTERP);
586 
587   ++data_.interpFrames_;
588 
589   if (data_.interpFrames_.done())
590     popActivation();
591   else
592     data_.pc_ = data_.interpFrames_.pc();
593 }
594 
settleOnActivation()595 void FrameIter::settleOnActivation() {
596   MOZ_ASSERT(!data_.cx_->inUnsafeCallWithABI);
597 
598   while (true) {
599     if (data_.activations_.done()) {
600       data_.state_ = DONE;
601       return;
602     }
603 
604     Activation* activation = data_.activations_.activation();
605 
606     // If the caller supplied principals, only show activations which are
607     // subsumed (of the same origin or of an origin accessible) by these
608     // principals.
609     if (data_.principals_) {
610       JSContext* cx = data_.cx_;
611       if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) {
612         if (!subsumes(data_.principals_,
613                       activation->compartment()->principals())) {
614           ++data_.activations_;
615           continue;
616         }
617       }
618     }
619 
620     if (activation->isJit()) {
621       data_.jitFrames_ = JitFrameIter(activation->asJit());
622       data_.jitFrames_.skipNonScriptedJSFrames();
623       if (data_.jitFrames_.done()) {
624         // It's possible to have an JitActivation with no scripted
625         // frames, for instance if we hit an over-recursion during
626         // bailout.
627         ++data_.activations_;
628         continue;
629       }
630       data_.state_ = JIT;
631       nextJitFrame();
632       return;
633     }
634 
635     MOZ_ASSERT(activation->isInterpreter());
636 
637     InterpreterActivation* interpAct = activation->asInterpreter();
638     data_.interpFrames_ = InterpreterFrameIterator(interpAct);
639 
640     // If we OSR'ed into JIT code, skip the interpreter frame so that
641     // the same frame is not reported twice.
642     if (data_.interpFrames_.frame()->runningInJit()) {
643       ++data_.interpFrames_;
644       if (data_.interpFrames_.done()) {
645         ++data_.activations_;
646         continue;
647       }
648     }
649 
650     MOZ_ASSERT(!data_.interpFrames_.frame()->runningInJit());
651     data_.pc_ = data_.interpFrames_.pc();
652     data_.state_ = INTERP;
653     return;
654   }
655 }
656 
Data(JSContext * cx,DebuggerEvalOption debuggerEvalOption,JSPrincipals * principals)657 FrameIter::Data::Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
658                       JSPrincipals* principals)
659     : cx_(cx),
660       debuggerEvalOption_(debuggerEvalOption),
661       principals_(principals),
662       state_(DONE),
663       pc_(nullptr),
664       interpFrames_(nullptr),
665       activations_(cx),
666       ionInlineFrameNo_(0) {}
667 
Data(JSContext * cx,const CooperatingContext & target,DebuggerEvalOption debuggerEvalOption)668 FrameIter::Data::Data(JSContext* cx, const CooperatingContext& target,
669                       DebuggerEvalOption debuggerEvalOption)
670     : cx_(cx),
671       debuggerEvalOption_(debuggerEvalOption),
672       principals_(nullptr),
673       state_(DONE),
674       pc_(nullptr),
675       interpFrames_(nullptr),
676       activations_(cx, target),
677       ionInlineFrameNo_(0) {}
678 
Data(const FrameIter::Data & other)679 FrameIter::Data::Data(const FrameIter::Data& other)
680     : cx_(other.cx_),
681       debuggerEvalOption_(other.debuggerEvalOption_),
682       principals_(other.principals_),
683       state_(other.state_),
684       pc_(other.pc_),
685       interpFrames_(other.interpFrames_),
686       activations_(other.activations_),
687       jitFrames_(other.jitFrames_),
688       ionInlineFrameNo_(other.ionInlineFrameNo_) {}
689 
FrameIter(JSContext * cx,const CooperatingContext & target,DebuggerEvalOption debuggerEvalOption)690 FrameIter::FrameIter(JSContext* cx, const CooperatingContext& target,
691                      DebuggerEvalOption debuggerEvalOption)
692     : data_(cx, target, debuggerEvalOption),
693       ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
694   // settleOnActivation can only GC if principals are given.
695   JS::AutoSuppressGCAnalysis nogc;
696   settleOnActivation();
697 }
698 
FrameIter(JSContext * cx,DebuggerEvalOption debuggerEvalOption)699 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption)
700     : data_(cx, debuggerEvalOption, nullptr),
701       ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
702   // settleOnActivation can only GC if principals are given.
703   JS::AutoSuppressGCAnalysis nogc;
704   settleOnActivation();
705 }
706 
FrameIter(JSContext * cx,DebuggerEvalOption debuggerEvalOption,JSPrincipals * principals)707 FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption,
708                      JSPrincipals* principals)
709     : data_(cx, debuggerEvalOption, principals),
710       ionInlineFrames_(cx, (js::jit::JSJitFrameIter*)nullptr) {
711   settleOnActivation();
712 }
713 
FrameIter(const FrameIter & other)714 FrameIter::FrameIter(const FrameIter& other)
715     : data_(other.data_),
716       ionInlineFrames_(other.data_.cx_,
717                        isIonScripted() ? &other.ionInlineFrames_ : nullptr) {}
718 
FrameIter(const Data & data)719 FrameIter::FrameIter(const Data& data)
720     : data_(data),
721       ionInlineFrames_(data.cx_, isIonScripted() ? &jsJitFrame() : nullptr) {
722   MOZ_ASSERT(data.cx_);
723   if (isIonScripted()) {
724     while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_)
725       ++ionInlineFrames_;
726   }
727 }
728 
nextJitFrame()729 void FrameIter::nextJitFrame() {
730   MOZ_ASSERT(data_.jitFrames_.isSome());
731 
732   if (isJSJit()) {
733     if (jsJitFrame().isIonScripted()) {
734       ionInlineFrames_.resetOn(&jsJitFrame());
735       data_.pc_ = ionInlineFrames_.pc();
736     } else {
737       MOZ_ASSERT(jsJitFrame().isBaselineJS());
738       jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_);
739     }
740     return;
741   }
742 
743   MOZ_ASSERT(isWasm());
744   data_.pc_ = nullptr;
745 }
746 
popJitFrame()747 void FrameIter::popJitFrame() {
748   MOZ_ASSERT(data_.state_ == JIT);
749   MOZ_ASSERT(data_.jitFrames_.isSome());
750 
751   if (isJSJit() && jsJitFrame().isIonScripted() && ionInlineFrames_.more()) {
752     ++ionInlineFrames_;
753     data_.pc_ = ionInlineFrames_.pc();
754     return;
755   }
756 
757   ++data_.jitFrames_;
758   data_.jitFrames_.skipNonScriptedJSFrames();
759 
760   if (!data_.jitFrames_.done()) {
761     nextJitFrame();
762   } else {
763     data_.jitFrames_.reset();
764     popActivation();
765   }
766 }
767 
operator ++()768 FrameIter& FrameIter::operator++() {
769   switch (data_.state_) {
770     case DONE:
771       MOZ_CRASH("Unexpected state");
772     case INTERP:
773       if (interpFrame()->isDebuggerEvalFrame() &&
774           data_.debuggerEvalOption_ == FOLLOW_DEBUGGER_EVAL_PREV_LINK) {
775         AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
776 
777         popInterpreterFrame();
778 
779         while (!hasUsableAbstractFramePtr() || abstractFramePtr() != eifPrev) {
780           if (data_.state_ == JIT)
781             popJitFrame();
782           else
783             popInterpreterFrame();
784         }
785 
786         break;
787       }
788       popInterpreterFrame();
789       break;
790     case JIT:
791       popJitFrame();
792       break;
793   }
794   return *this;
795 }
796 
copyData() const797 FrameIter::Data* FrameIter::copyData() const {
798   Data* data = data_.cx_->new_<Data>(data_);
799   if (!data) return nullptr;
800 
801   if (data && isIonScripted())
802     data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
803   return data;
804 }
805 
copyDataAsAbstractFramePtr() const806 AbstractFramePtr FrameIter::copyDataAsAbstractFramePtr() const {
807   AbstractFramePtr frame;
808   if (Data* data = copyData()) frame.ptr_ = uintptr_t(data);
809   return frame;
810 }
811 
rawFramePtr() const812 void* FrameIter::rawFramePtr() const {
813   switch (data_.state_) {
814     case DONE:
815       return nullptr;
816     case INTERP:
817       return interpFrame();
818     case JIT:
819       if (isJSJit()) return jsJitFrame().fp();
820       MOZ_ASSERT(isWasm());
821       return nullptr;
822   }
823   MOZ_CRASH("Unexpected state");
824 }
825 
compartment() const826 JSCompartment* FrameIter::compartment() const {
827   switch (data_.state_) {
828     case DONE:
829       break;
830     case INTERP:
831     case JIT:
832       return data_.activations_->compartment();
833   }
834   MOZ_CRASH("Unexpected state");
835 }
836 
isEvalFrame() const837 bool FrameIter::isEvalFrame() const {
838   switch (data_.state_) {
839     case DONE:
840       break;
841     case INTERP:
842       return interpFrame()->isEvalFrame();
843     case JIT:
844       if (isJSJit()) {
845         if (jsJitFrame().isBaselineJS())
846           return jsJitFrame().baselineFrame()->isEvalFrame();
847         MOZ_ASSERT(!script()->isForEval());
848         return false;
849       }
850       MOZ_ASSERT(isWasm());
851       return false;
852   }
853   MOZ_CRASH("Unexpected state");
854 }
855 
isFunctionFrame() const856 bool FrameIter::isFunctionFrame() const {
857   MOZ_ASSERT(!done());
858   switch (data_.state_) {
859     case DONE:
860       break;
861     case INTERP:
862       return interpFrame()->isFunctionFrame();
863     case JIT:
864       if (isJSJit()) {
865         if (jsJitFrame().isBaselineJS())
866           return jsJitFrame().baselineFrame()->isFunctionFrame();
867         return script()->functionNonDelazifying();
868       }
869       MOZ_ASSERT(isWasm());
870       return false;
871   }
872   MOZ_CRASH("Unexpected state");
873 }
874 
functionDisplayAtom() const875 JSAtom* FrameIter::functionDisplayAtom() const {
876   switch (data_.state_) {
877     case DONE:
878       break;
879     case INTERP:
880     case JIT:
881       if (isWasm()) return wasmFrame().functionDisplayAtom();
882       MOZ_ASSERT(isFunctionFrame());
883       return calleeTemplate()->displayAtom();
884   }
885 
886   MOZ_CRASH("Unexpected state");
887 }
888 
scriptSource() const889 ScriptSource* FrameIter::scriptSource() const {
890   switch (data_.state_) {
891     case DONE:
892       break;
893     case INTERP:
894     case JIT:
895       return script()->scriptSource();
896   }
897 
898   MOZ_CRASH("Unexpected state");
899 }
900 
filename() const901 const char* FrameIter::filename() const {
902   switch (data_.state_) {
903     case DONE:
904       break;
905     case INTERP:
906     case JIT:
907       if (isWasm()) return wasmFrame().filename();
908       return script()->filename();
909   }
910 
911   MOZ_CRASH("Unexpected state");
912 }
913 
displayURL() const914 const char16_t* FrameIter::displayURL() const {
915   switch (data_.state_) {
916     case DONE:
917       break;
918     case INTERP:
919     case JIT:
920       if (isWasm()) return wasmFrame().displayURL();
921       ScriptSource* ss = script()->scriptSource();
922       return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
923   }
924   MOZ_CRASH("Unexpected state");
925 }
926 
computeLine(uint32_t * column) const927 unsigned FrameIter::computeLine(uint32_t* column) const {
928   switch (data_.state_) {
929     case DONE:
930       break;
931     case INTERP:
932     case JIT:
933       if (isWasm()) {
934         if (column) *column = 0;
935         return wasmFrame().lineOrBytecode();
936       }
937       return PCToLineNumber(script(), pc(), column);
938   }
939 
940   MOZ_CRASH("Unexpected state");
941 }
942 
mutedErrors() const943 bool FrameIter::mutedErrors() const {
944   switch (data_.state_) {
945     case DONE:
946       break;
947     case INTERP:
948     case JIT:
949       if (isWasm()) return wasmFrame().mutedErrors();
950       return script()->mutedErrors();
951   }
952   MOZ_CRASH("Unexpected state");
953 }
954 
isConstructing() const955 bool FrameIter::isConstructing() const {
956   switch (data_.state_) {
957     case DONE:
958       break;
959     case JIT:
960       MOZ_ASSERT(isJSJit());
961       if (jsJitFrame().isIonScripted())
962         return ionInlineFrames_.isConstructing();
963       MOZ_ASSERT(jsJitFrame().isBaselineJS());
964       return jsJitFrame().isConstructing();
965     case INTERP:
966       return interpFrame()->isConstructing();
967   }
968 
969   MOZ_CRASH("Unexpected state");
970 }
971 
ensureHasRematerializedFrame(JSContext * cx)972 bool FrameIter::ensureHasRematerializedFrame(JSContext* cx) {
973   MOZ_ASSERT(isIon());
974   return !!activation()->asJit()->getRematerializedFrame(cx, jsJitFrame());
975 }
976 
hasUsableAbstractFramePtr() const977 bool FrameIter::hasUsableAbstractFramePtr() const {
978   switch (data_.state_) {
979     case DONE:
980       return false;
981     case JIT:
982       if (isJSJit()) {
983         if (jsJitFrame().isBaselineJS()) return true;
984 
985         MOZ_ASSERT(jsJitFrame().isIonScripted());
986         return !!activation()->asJit()->lookupRematerializedFrame(
987             jsJitFrame().fp(), ionInlineFrames_.frameNo());
988       }
989       MOZ_ASSERT(isWasm());
990       return wasmFrame().debugEnabled();
991     case INTERP:
992       return true;
993   }
994   MOZ_CRASH("Unexpected state");
995 }
996 
abstractFramePtr() const997 AbstractFramePtr FrameIter::abstractFramePtr() const {
998   MOZ_ASSERT(hasUsableAbstractFramePtr());
999   switch (data_.state_) {
1000     case DONE:
1001       break;
1002     case JIT: {
1003       if (isJSJit()) {
1004         if (jsJitFrame().isBaselineJS()) return jsJitFrame().baselineFrame();
1005         MOZ_ASSERT(isIonScripted());
1006         return activation()->asJit()->lookupRematerializedFrame(
1007             jsJitFrame().fp(), ionInlineFrames_.frameNo());
1008       }
1009       MOZ_ASSERT(isWasm());
1010       MOZ_ASSERT(wasmFrame().debugEnabled());
1011       return wasmFrame().debugFrame();
1012     }
1013     case INTERP:
1014       MOZ_ASSERT(interpFrame());
1015       return AbstractFramePtr(interpFrame());
1016   }
1017   MOZ_CRASH("Unexpected state");
1018 }
1019 
updatePcQuadratic()1020 void FrameIter::updatePcQuadratic() {
1021   switch (data_.state_) {
1022     case DONE:
1023       break;
1024     case INTERP: {
1025       InterpreterFrame* frame = interpFrame();
1026       InterpreterActivation* activation = data_.activations_->asInterpreter();
1027 
1028       // Look for the current frame.
1029       data_.interpFrames_ = InterpreterFrameIterator(activation);
1030       while (data_.interpFrames_.frame() != frame) ++data_.interpFrames_;
1031 
1032       // Update the pc.
1033       MOZ_ASSERT(data_.interpFrames_.frame() == frame);
1034       data_.pc_ = data_.interpFrames_.pc();
1035       return;
1036     }
1037     case JIT:
1038       if (jsJitFrame().isBaselineJS()) {
1039         jit::BaselineFrame* frame = jsJitFrame().baselineFrame();
1040         jit::JitActivation* activation = data_.activations_->asJit();
1041 
1042         // activation's exitFP may be invalid, so create a new
1043         // activation iterator.
1044         data_.activations_ = ActivationIterator(data_.cx_);
1045         while (data_.activations_.activation() != activation)
1046           ++data_.activations_;
1047 
1048         // Look for the current frame.
1049         data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
1050         while (!jsJitFrame().isBaselineJS() ||
1051                jsJitFrame().baselineFrame() != frame)
1052           ++data_.jitFrames_;
1053 
1054         // Update the pc.
1055         MOZ_ASSERT(jsJitFrame().baselineFrame() == frame);
1056         jsJitFrame().baselineScriptAndPc(nullptr, &data_.pc_);
1057         return;
1058       }
1059       break;
1060   }
1061   MOZ_CRASH("Unexpected state");
1062 }
1063 
wasmUpdateBytecodeOffset()1064 void FrameIter::wasmUpdateBytecodeOffset() {
1065   MOZ_RELEASE_ASSERT(isWasm(), "Unexpected state");
1066 
1067   wasm::DebugFrame* frame = wasmFrame().debugFrame();
1068 
1069   // Relookup the current frame, updating the bytecode offset in the process.
1070   data_.jitFrames_ = JitFrameIter(data_.activations_->asJit());
1071   while (wasmFrame().debugFrame() != frame) ++data_.jitFrames_;
1072 
1073   MOZ_ASSERT(wasmFrame().debugFrame() == frame);
1074 }
1075 
calleeTemplate() const1076 JSFunction* FrameIter::calleeTemplate() const {
1077   switch (data_.state_) {
1078     case DONE:
1079       break;
1080     case INTERP:
1081       MOZ_ASSERT(isFunctionFrame());
1082       return &interpFrame()->callee();
1083     case JIT:
1084       if (jsJitFrame().isBaselineJS()) return jsJitFrame().callee();
1085       MOZ_ASSERT(jsJitFrame().isIonScripted());
1086       return ionInlineFrames_.calleeTemplate();
1087   }
1088   MOZ_CRASH("Unexpected state");
1089 }
1090 
callee(JSContext * cx) const1091 JSFunction* FrameIter::callee(JSContext* cx) const {
1092   switch (data_.state_) {
1093     case DONE:
1094       break;
1095     case INTERP:
1096       return calleeTemplate();
1097     case JIT:
1098       if (isIonScripted()) {
1099         jit::MaybeReadFallback recover(cx, activation()->asJit(),
1100                                        &jsJitFrame());
1101         return ionInlineFrames_.callee(recover);
1102       }
1103       MOZ_ASSERT(jsJitFrame().isBaselineJS());
1104       return calleeTemplate();
1105   }
1106   MOZ_CRASH("Unexpected state");
1107 }
1108 
matchCallee(JSContext * cx,HandleFunction fun) const1109 bool FrameIter::matchCallee(JSContext* cx, HandleFunction fun) const {
1110   RootedFunction currentCallee(cx, calleeTemplate());
1111 
1112   // As we do not know if the calleeTemplate is the real function, or the
1113   // template from which it would be cloned, we compare properties which are
1114   // stable across the cloning of JSFunctions.
1115   if (((currentCallee->flags() ^ fun->flags()) &
1116        JSFunction::STABLE_ACROSS_CLONES) != 0 ||
1117       currentCallee->nargs() != fun->nargs()) {
1118     return false;
1119   }
1120 
1121   // Use the same condition as |js::CloneFunctionObject|, to know if we should
1122   // expect both functions to have the same JSScript. If so, and if they are
1123   // different, then they cannot be equal.
1124   RootedObject global(cx, &fun->global());
1125   bool useSameScript =
1126       CanReuseScriptForClone(fun->compartment(), currentCallee, global);
1127   if (useSameScript &&
1128       (currentCallee->hasScript() != fun->hasScript() ||
1129        currentCallee->nonLazyScript() != fun->nonLazyScript())) {
1130     return false;
1131   }
1132 
1133   // If none of the previous filters worked, then take the risk of
1134   // invalidating the frame to identify the JSFunction.
1135   return callee(cx) == fun;
1136 }
1137 
numActualArgs() const1138 unsigned FrameIter::numActualArgs() const {
1139   switch (data_.state_) {
1140     case DONE:
1141       break;
1142     case INTERP:
1143       MOZ_ASSERT(isFunctionFrame());
1144       return interpFrame()->numActualArgs();
1145     case JIT:
1146       if (isIonScripted()) return ionInlineFrames_.numActualArgs();
1147       MOZ_ASSERT(jsJitFrame().isBaselineJS());
1148       return jsJitFrame().numActualArgs();
1149   }
1150   MOZ_CRASH("Unexpected state");
1151 }
1152 
numFormalArgs() const1153 unsigned FrameIter::numFormalArgs() const {
1154   return script()->functionNonDelazifying()->nargs();
1155 }
1156 
unaliasedActual(unsigned i,MaybeCheckAliasing checkAliasing) const1157 Value FrameIter::unaliasedActual(unsigned i,
1158                                  MaybeCheckAliasing checkAliasing) const {
1159   return abstractFramePtr().unaliasedActual(i, checkAliasing);
1160 }
1161 
environmentChain(JSContext * cx) const1162 JSObject* FrameIter::environmentChain(JSContext* cx) const {
1163   switch (data_.state_) {
1164     case DONE:
1165       break;
1166     case JIT:
1167       if (isJSJit()) {
1168         if (isIonScripted()) {
1169           jit::MaybeReadFallback recover(cx, activation()->asJit(),
1170                                          &jsJitFrame());
1171           return ionInlineFrames_.environmentChain(recover);
1172         }
1173         return jsJitFrame().baselineFrame()->environmentChain();
1174       }
1175       MOZ_ASSERT(isWasm());
1176       return wasmFrame().debugFrame()->environmentChain();
1177     case INTERP:
1178       return interpFrame()->environmentChain();
1179   }
1180   MOZ_CRASH("Unexpected state");
1181 }
1182 
callObj(JSContext * cx) const1183 CallObject& FrameIter::callObj(JSContext* cx) const {
1184   MOZ_ASSERT(calleeTemplate()->needsCallObject());
1185 
1186   JSObject* pobj = environmentChain(cx);
1187   while (!pobj->is<CallObject>()) pobj = pobj->enclosingEnvironment();
1188   return pobj->as<CallObject>();
1189 }
1190 
hasArgsObj() const1191 bool FrameIter::hasArgsObj() const { return abstractFramePtr().hasArgsObj(); }
1192 
argsObj() const1193 ArgumentsObject& FrameIter::argsObj() const {
1194   MOZ_ASSERT(hasArgsObj());
1195   return abstractFramePtr().argsObj();
1196 }
1197 
thisArgument(JSContext * cx) const1198 Value FrameIter::thisArgument(JSContext* cx) const {
1199   MOZ_ASSERT(isFunctionFrame());
1200 
1201   switch (data_.state_) {
1202     case DONE:
1203       break;
1204     case JIT:
1205       if (isIonScripted()) {
1206         jit::MaybeReadFallback recover(cx, activation()->asJit(),
1207                                        &jsJitFrame());
1208         return ionInlineFrames_.thisArgument(recover);
1209       }
1210       return jsJitFrame().baselineFrame()->thisArgument();
1211     case INTERP:
1212       return interpFrame()->thisArgument();
1213   }
1214   MOZ_CRASH("Unexpected state");
1215 }
1216 
newTarget() const1217 Value FrameIter::newTarget() const {
1218   switch (data_.state_) {
1219     case DONE:
1220       break;
1221     case INTERP:
1222       return interpFrame()->newTarget();
1223     case JIT:
1224       MOZ_ASSERT(jsJitFrame().isBaselineJS());
1225       return jsJitFrame().baselineFrame()->newTarget();
1226   }
1227   MOZ_CRASH("Unexpected state");
1228 }
1229 
returnValue() const1230 Value FrameIter::returnValue() const {
1231   switch (data_.state_) {
1232     case DONE:
1233       break;
1234     case JIT:
1235       if (jsJitFrame().isBaselineJS())
1236         return jsJitFrame().baselineFrame()->returnValue();
1237       break;
1238     case INTERP:
1239       return interpFrame()->returnValue();
1240   }
1241   MOZ_CRASH("Unexpected state");
1242 }
1243 
setReturnValue(const Value & v)1244 void FrameIter::setReturnValue(const Value& v) {
1245   switch (data_.state_) {
1246     case DONE:
1247       break;
1248     case JIT:
1249       if (jsJitFrame().isBaselineJS()) {
1250         jsJitFrame().baselineFrame()->setReturnValue(v);
1251         return;
1252       }
1253       break;
1254     case INTERP:
1255       interpFrame()->setReturnValue(v);
1256       return;
1257   }
1258   MOZ_CRASH("Unexpected state");
1259 }
1260 
numFrameSlots() const1261 size_t FrameIter::numFrameSlots() const {
1262   switch (data_.state_) {
1263     case DONE:
1264       break;
1265     case JIT: {
1266       if (isIonScripted()) {
1267         return ionInlineFrames_.snapshotIterator().numAllocations() -
1268                ionInlineFrames_.script()->nfixed();
1269       }
1270       jit::BaselineFrame* frame = jsJitFrame().baselineFrame();
1271       return frame->numValueSlots() - jsJitFrame().script()->nfixed();
1272     }
1273     case INTERP:
1274       MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
1275       return data_.interpFrames_.sp() - interpFrame()->base();
1276   }
1277   MOZ_CRASH("Unexpected state");
1278 }
1279 
frameSlotValue(size_t index) const1280 Value FrameIter::frameSlotValue(size_t index) const {
1281   switch (data_.state_) {
1282     case DONE:
1283       break;
1284     case JIT:
1285       if (isIonScripted()) {
1286         jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
1287         index += ionInlineFrames_.script()->nfixed();
1288         return si.maybeReadAllocByIndex(index);
1289       }
1290       index += jsJitFrame().script()->nfixed();
1291       return *jsJitFrame().baselineFrame()->valueSlot(index);
1292     case INTERP:
1293       return interpFrame()->base()[index];
1294   }
1295   MOZ_CRASH("Unexpected state");
1296 }
1297 
1298 #ifdef DEBUG
SelfHostedFramesVisible()1299 bool js::SelfHostedFramesVisible() {
1300   static bool checked = false;
1301   static bool visible = false;
1302   if (!checked) {
1303     checked = true;
1304     char* env = getenv("MOZ_SHOW_ALL_JS_FRAMES");
1305     visible = !!env;
1306   }
1307   return visible;
1308 }
1309 #endif
1310 
settle()1311 void NonBuiltinFrameIter::settle() {
1312   if (!SelfHostedFramesVisible()) {
1313     while (!done() && hasScript() && script()->selfHosted())
1314       FrameIter::operator++();
1315   }
1316 }
1317 
settle()1318 void NonBuiltinScriptFrameIter::settle() {
1319   if (!SelfHostedFramesVisible()) {
1320     while (!done() && script()->selfHosted()) ScriptFrameIter::operator++();
1321   }
1322 }
1323 
ActivationEntryMonitor(JSContext * cx)1324 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx)
1325     : cx_(cx), entryMonitor_(cx->entryMonitor) {
1326   cx->entryMonitor = nullptr;
1327 }
1328 
asyncStack(JSContext * cx)1329 Value ActivationEntryMonitor::asyncStack(JSContext* cx) {
1330   RootedValue stack(cx, ObjectOrNullValue(cx->asyncStackForNewActivations()));
1331   if (!cx->compartment()->wrap(cx, &stack)) {
1332     cx->clearPendingException();
1333     return UndefinedValue();
1334   }
1335   return stack;
1336 }
1337 
ActivationEntryMonitor(JSContext * cx,InterpreterFrame * entryFrame)1338 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx,
1339                                                InterpreterFrame* entryFrame)
1340     : ActivationEntryMonitor(cx) {
1341   if (entryMonitor_) {
1342     // The InterpreterFrame is not yet part of an Activation, so it won't
1343     // be traced if we trigger GC here. Suppress GC to avoid this.
1344     gc::AutoSuppressGC suppressGC(cx);
1345     RootedValue stack(cx, asyncStack(cx));
1346     const char* asyncCause = cx->asyncCauseForNewActivations;
1347     if (entryFrame->isFunctionFrame())
1348       entryMonitor_->Entry(cx, &entryFrame->callee(), stack, asyncCause);
1349     else
1350       entryMonitor_->Entry(cx, entryFrame->script(), stack, asyncCause);
1351   }
1352 }
1353 
ActivationEntryMonitor(JSContext * cx,jit::CalleeToken entryToken)1354 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx,
1355                                                jit::CalleeToken entryToken)
1356     : ActivationEntryMonitor(cx) {
1357   if (entryMonitor_) {
1358     // The CalleeToken is not traced at this point and we also don't want
1359     // a GC to discard the code we're about to enter, so we suppress GC.
1360     gc::AutoSuppressGC suppressGC(cx);
1361     RootedValue stack(cx, asyncStack(cx));
1362     const char* asyncCause = cx->asyncCauseForNewActivations;
1363     if (jit::CalleeTokenIsFunction(entryToken))
1364       entryMonitor_->Entry(cx_, jit::CalleeTokenToFunction(entryToken), stack,
1365                            asyncCause);
1366     else
1367       entryMonitor_->Entry(cx_, jit::CalleeTokenToScript(entryToken), stack,
1368                            asyncCause);
1369   }
1370 }
1371 
1372 /*****************************************************************************/
1373 
JitActivation(JSContext * cx)1374 jit::JitActivation::JitActivation(JSContext* cx)
1375     : Activation(cx, Jit),
1376       packedExitFP_(nullptr),
1377       encodedWasmExitReason_(0),
1378       prevJitActivation_(cx->jitActivation),
1379       rematerializedFrames_(nullptr),
1380       ionRecovery_(cx),
1381       bailoutData_(nullptr),
1382       lastProfilingFrame_(nullptr),
1383       lastProfilingCallSite_(nullptr) {
1384   cx->jitActivation = this;
1385   registerProfiling();
1386 }
1387 
~JitActivation()1388 jit::JitActivation::~JitActivation() {
1389   if (isProfiling()) unregisterProfiling();
1390   cx_->jitActivation = prevJitActivation_;
1391 
1392   // All reocvered value are taken from activation during the bailout.
1393   MOZ_ASSERT(ionRecovery_.empty());
1394 
1395   // The BailoutFrameInfo should have unregistered itself from the
1396   // JitActivations.
1397   MOZ_ASSERT(!bailoutData_);
1398 
1399   MOZ_ASSERT(!isWasmInterrupted());
1400   MOZ_ASSERT(!isWasmTrapping());
1401 
1402   clearRematerializedFrames();
1403   js_delete(rematerializedFrames_);
1404 }
1405 
setBailoutData(jit::BailoutFrameInfo * bailoutData)1406 void jit::JitActivation::setBailoutData(jit::BailoutFrameInfo* bailoutData) {
1407   MOZ_ASSERT(!bailoutData_);
1408   bailoutData_ = bailoutData;
1409 }
1410 
cleanBailoutData()1411 void jit::JitActivation::cleanBailoutData() {
1412   MOZ_ASSERT(bailoutData_);
1413   bailoutData_ = nullptr;
1414 }
1415 
removeRematerializedFrame(uint8_t * top)1416 void jit::JitActivation::removeRematerializedFrame(uint8_t* top) {
1417   if (!rematerializedFrames_) return;
1418 
1419   if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
1420     RematerializedFrame::FreeInVector(p->value());
1421     rematerializedFrames_->remove(p);
1422   }
1423 }
1424 
clearRematerializedFrames()1425 void jit::JitActivation::clearRematerializedFrames() {
1426   if (!rematerializedFrames_) return;
1427 
1428   for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty();
1429        e.popFront()) {
1430     RematerializedFrame::FreeInVector(e.front().value());
1431     e.removeFront();
1432   }
1433 }
1434 
getRematerializedFrame(JSContext * cx,const JSJitFrameIter & iter,size_t inlineDepth)1435 jit::RematerializedFrame* jit::JitActivation::getRematerializedFrame(
1436     JSContext* cx, const JSJitFrameIter& iter, size_t inlineDepth) {
1437   MOZ_ASSERT(iter.activation() == this);
1438   MOZ_ASSERT(iter.isIonScripted());
1439 
1440   if (!rematerializedFrames_) {
1441     rematerializedFrames_ = cx->new_<RematerializedFrameTable>(cx);
1442     if (!rematerializedFrames_) return nullptr;
1443     if (!rematerializedFrames_->init()) {
1444       rematerializedFrames_ = nullptr;
1445       ReportOutOfMemory(cx);
1446       return nullptr;
1447     }
1448   }
1449 
1450   uint8_t* top = iter.fp();
1451   RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
1452   if (!p) {
1453     RematerializedFrameVector frames(cx);
1454 
1455     // The unit of rematerialization is an uninlined frame and its inlined
1456     // frames. Since inlined frames do not exist outside of snapshots, it
1457     // is impossible to synchronize their rematerialized copies to
1458     // preserve identity. Therefore, we always rematerialize an uninlined
1459     // frame and all its inlined frames at once.
1460     InlineFrameIterator inlineIter(cx, &iter);
1461     MaybeReadFallback recover(cx, this, &iter);
1462 
1463     // Frames are often rematerialized with the cx inside a Debugger's
1464     // compartment. To recover slots and to create CallObjects, we need to
1465     // be in the activation's compartment.
1466     AutoCompartmentUnchecked ac(cx, compartment_);
1467 
1468     if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter,
1469                                                         recover, frames))
1470       return nullptr;
1471 
1472     if (!rematerializedFrames_->add(p, top, Move(frames))) {
1473       ReportOutOfMemory(cx);
1474       return nullptr;
1475     }
1476 
1477     // See comment in unsetPrevUpToDateUntil.
1478     DebugEnvironments::unsetPrevUpToDateUntil(cx, p->value()[inlineDepth]);
1479   }
1480 
1481   return p->value()[inlineDepth];
1482 }
1483 
lookupRematerializedFrame(uint8_t * top,size_t inlineDepth)1484 jit::RematerializedFrame* jit::JitActivation::lookupRematerializedFrame(
1485     uint8_t* top, size_t inlineDepth) {
1486   if (!rematerializedFrames_) return nullptr;
1487   if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top))
1488     return inlineDepth < p->value().length() ? p->value()[inlineDepth]
1489                                              : nullptr;
1490   return nullptr;
1491 }
1492 
removeRematerializedFramesFromDebugger(JSContext * cx,uint8_t * top)1493 void jit::JitActivation::removeRematerializedFramesFromDebugger(JSContext* cx,
1494                                                                 uint8_t* top) {
1495   // Ion bailout can fail due to overrecursion and OOM. In such cases we
1496   // cannot honor any further Debugger hooks on the frame, and need to
1497   // ensure that its Debugger.Frame entry is cleaned up.
1498   if (!cx->compartment()->isDebuggee() || !rematerializedFrames_) return;
1499   if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
1500     for (uint32_t i = 0; i < p->value().length(); i++)
1501       Debugger::handleUnrecoverableIonBailoutError(cx, p->value()[i]);
1502   }
1503 }
1504 
traceRematerializedFrames(JSTracer * trc)1505 void jit::JitActivation::traceRematerializedFrames(JSTracer* trc) {
1506   if (!rematerializedFrames_) return;
1507   for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty();
1508        e.popFront())
1509     e.front().value().trace(trc);
1510 }
1511 
registerIonFrameRecovery(RInstructionResults && results)1512 bool jit::JitActivation::registerIonFrameRecovery(
1513     RInstructionResults&& results) {
1514   // Check that there is no entry in the vector yet.
1515   MOZ_ASSERT(!maybeIonFrameRecovery(results.frame()));
1516   if (!ionRecovery_.append(mozilla::Move(results))) return false;
1517 
1518   return true;
1519 }
1520 
maybeIonFrameRecovery(JitFrameLayout * fp)1521 jit::RInstructionResults* jit::JitActivation::maybeIonFrameRecovery(
1522     JitFrameLayout* fp) {
1523   for (RInstructionResults* it = ionRecovery_.begin();
1524        it != ionRecovery_.end();) {
1525     if (it->frame() == fp) return it;
1526   }
1527 
1528   return nullptr;
1529 }
1530 
removeIonFrameRecovery(JitFrameLayout * fp)1531 void jit::JitActivation::removeIonFrameRecovery(JitFrameLayout* fp) {
1532   RInstructionResults* elem = maybeIonFrameRecovery(fp);
1533   if (!elem) return;
1534 
1535   ionRecovery_.erase(elem);
1536 }
1537 
traceIonRecovery(JSTracer * trc)1538 void jit::JitActivation::traceIonRecovery(JSTracer* trc) {
1539   for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end();
1540        it++)
1541     it->trace(trc);
1542 }
1543 
startWasmInterrupt(const JS::ProfilingFrameIterator::RegisterState & state)1544 bool jit::JitActivation::startWasmInterrupt(
1545     const JS::ProfilingFrameIterator::RegisterState& state) {
1546   // fp may be null when first entering wasm code from an interpreter entry
1547   // stub.
1548   if (!state.fp) return false;
1549 
1550   MOZ_ASSERT(state.pc);
1551 
1552   // Execution can only be interrupted in function code. Afterwards, control
1553   // flow does not reenter function code and thus there can be no
1554   // interrupt-during-interrupt.
1555 
1556   bool unwound;
1557   wasm::UnwindState unwindState;
1558   MOZ_ALWAYS_TRUE(wasm::StartUnwinding(state, &unwindState, &unwound));
1559 
1560   void* pc = unwindState.pc;
1561 
1562   if (unwound) {
1563     // In the prologue/epilogue, FP might have been fixed up to the
1564     // caller's FP, and the caller could be the jit entry. Ignore this
1565     // interrupt, in this case, because FP points to a jit frame and not a
1566     // wasm one.
1567     if (!wasm::LookupCode(pc)->lookupFuncRange(pc)) return false;
1568   }
1569 
1570   cx_->runtime()->wasmUnwindData.ref().construct<wasm::InterruptData>(pc,
1571                                                                       state.pc);
1572   setWasmExitFP(unwindState.fp);
1573 
1574   MOZ_ASSERT(compartment() == unwindState.fp->tls->instance->compartment());
1575   MOZ_ASSERT(isWasmInterrupted());
1576   return true;
1577 }
1578 
finishWasmInterrupt()1579 void jit::JitActivation::finishWasmInterrupt() {
1580   MOZ_ASSERT(isWasmInterrupted());
1581 
1582   cx_->runtime()->wasmUnwindData.ref().destroy();
1583   packedExitFP_ = nullptr;
1584 }
1585 
isWasmInterrupted() const1586 bool jit::JitActivation::isWasmInterrupted() const {
1587   JSRuntime* rt = cx_->runtime();
1588   if (!rt->wasmUnwindData.ref().constructed<wasm::InterruptData>())
1589     return false;
1590 
1591   Activation* act = cx_->activation();
1592   while (act && !act->hasWasmExitFP()) act = act->prev();
1593 
1594   if (act != this) return false;
1595 
1596   DebugOnly<const wasm::Frame*> fp = wasmExitFP();
1597   DebugOnly<void*> unwindPC = rt->wasmInterruptData().unwindPC;
1598   MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
1599   return true;
1600 }
1601 
wasmInterruptUnwindPC() const1602 void* jit::JitActivation::wasmInterruptUnwindPC() const {
1603   MOZ_ASSERT(isWasmInterrupted());
1604   return cx_->runtime()->wasmInterruptData().unwindPC;
1605 }
1606 
wasmInterruptResumePC() const1607 void* jit::JitActivation::wasmInterruptResumePC() const {
1608   MOZ_ASSERT(isWasmInterrupted());
1609   return cx_->runtime()->wasmInterruptData().resumePC;
1610 }
1611 
startWasmTrap(wasm::Trap trap,uint32_t bytecodeOffset,const wasm::RegisterState & state)1612 void jit::JitActivation::startWasmTrap(wasm::Trap trap, uint32_t bytecodeOffset,
1613                                        const wasm::RegisterState& state) {
1614   bool unwound;
1615   wasm::UnwindState unwindState;
1616   MOZ_ALWAYS_TRUE(wasm::StartUnwinding(state, &unwindState, &unwound));
1617   MOZ_ASSERT(unwound == (trap == wasm::Trap::IndirectCallBadSig));
1618 
1619   void* pc = unwindState.pc;
1620   wasm::Frame* fp = unwindState.fp;
1621 
1622   const wasm::Code& code = fp->tls->instance->code();
1623   MOZ_RELEASE_ASSERT(&code == wasm::LookupCode(pc));
1624 
1625   // If the frame was unwound, the bytecodeOffset must be recovered from the
1626   // callsite so that it is accurate.
1627   if (unwound) bytecodeOffset = code.lookupCallSite(pc)->lineOrBytecode();
1628 
1629   cx_->runtime()->wasmUnwindData.ref().construct<wasm::TrapData>(
1630       pc, trap, bytecodeOffset);
1631   setWasmExitFP(fp);
1632 }
1633 
finishWasmTrap()1634 void jit::JitActivation::finishWasmTrap() {
1635   MOZ_ASSERT(isWasmTrapping());
1636 
1637   cx_->runtime()->wasmUnwindData.ref().destroy();
1638   packedExitFP_ = nullptr;
1639 }
1640 
isWasmTrapping() const1641 bool jit::JitActivation::isWasmTrapping() const {
1642   JSRuntime* rt = cx_->runtime();
1643   if (!rt->wasmUnwindData.ref().constructed<wasm::TrapData>()) return false;
1644 
1645   Activation* act = cx_->activation();
1646   while (act && !act->hasWasmExitFP()) act = act->prev();
1647 
1648   if (act != this) return false;
1649 
1650   DebugOnly<const wasm::Frame*> fp = wasmExitFP();
1651   DebugOnly<void*> unwindPC = rt->wasmTrapData().pc;
1652   MOZ_ASSERT(fp->instance()->code().containsCodePC(unwindPC));
1653   return true;
1654 }
1655 
wasmTrapPC() const1656 void* jit::JitActivation::wasmTrapPC() const {
1657   MOZ_ASSERT(isWasmTrapping());
1658   return cx_->runtime()->wasmTrapData().pc;
1659 }
1660 
wasmTrapBytecodeOffset() const1661 uint32_t jit::JitActivation::wasmTrapBytecodeOffset() const {
1662   MOZ_ASSERT(isWasmTrapping());
1663   return cx_->runtime()->wasmTrapData().bytecodeOffset;
1664 }
1665 
operator ++()1666 InterpreterFrameIterator& InterpreterFrameIterator::operator++() {
1667   MOZ_ASSERT(!done());
1668   if (fp_ != activation_->entryFrame_) {
1669     pc_ = fp_->prevpc();
1670     sp_ = fp_->prevsp();
1671     fp_ = fp_->prev();
1672   } else {
1673     pc_ = nullptr;
1674     sp_ = nullptr;
1675     fp_ = nullptr;
1676   }
1677   return *this;
1678 }
1679 
registerProfiling()1680 void Activation::registerProfiling() {
1681   MOZ_ASSERT(isProfiling());
1682   cx_->profilingActivation_ = this;
1683 }
1684 
unregisterProfiling()1685 void Activation::unregisterProfiling() {
1686   MOZ_ASSERT(isProfiling());
1687   MOZ_ASSERT(cx_->profilingActivation_ == this);
1688   cx_->profilingActivation_ = prevProfiling_;
1689 }
1690 
ActivationIterator(JSContext * cx)1691 ActivationIterator::ActivationIterator(JSContext* cx)
1692     : activation_(cx->activation_) {
1693   MOZ_ASSERT(cx == TlsContext.get());
1694 }
1695 
ActivationIterator(JSContext * cx,const CooperatingContext & target)1696 ActivationIterator::ActivationIterator(JSContext* cx,
1697                                        const CooperatingContext& target) {
1698   MOZ_ASSERT(cx == TlsContext.get());
1699 
1700   // If target was specified --- even if it is the same as cx itself --- then
1701   // we must be in a scope where changes of the active context are prohibited.
1702   // Otherwise our state would be corrupted if the target thread resumed
1703   // execution while we are iterating over its state.
1704   MOZ_ASSERT(cx->runtime()->activeContextChangeProhibited() ||
1705              !cx->runtime()->gc.canChangeActiveContext(cx));
1706 
1707   // Tolerate a null target context, in case we are iterating over the
1708   // activations for a zone group that is not in use by any thread.
1709   activation_ =
1710       target.context() ? target.context()->activation_.ref() : nullptr;
1711 }
1712 
operator ++()1713 ActivationIterator& ActivationIterator::operator++() {
1714   MOZ_ASSERT(activation_);
1715   activation_ = activation_->prev();
1716   return *this;
1717 }
1718 
ProfilingFrameIterator(JSContext * cx,const RegisterState & state,const Maybe<uint64_t> & samplePositionInProfilerBuffer)1719 JS::ProfilingFrameIterator::ProfilingFrameIterator(
1720     JSContext* cx, const RegisterState& state,
1721     const Maybe<uint64_t>& samplePositionInProfilerBuffer)
1722     : cx_(cx),
1723       samplePositionInProfilerBuffer_(samplePositionInProfilerBuffer),
1724       activation_(nullptr) {
1725   if (!cx->runtime()->geckoProfiler().enabled())
1726     MOZ_CRASH(
1727         "ProfilingFrameIterator called when geckoProfiler not enabled for "
1728         "runtime.");
1729 
1730   if (!cx->profilingActivation()) return;
1731 
1732   // If profiler sampling is not enabled, skip.
1733   if (!cx->isProfilerSamplingEnabled()) return;
1734 
1735   activation_ = cx->profilingActivation();
1736 
1737   MOZ_ASSERT(activation_->isProfiling());
1738 
1739   static_assert(sizeof(wasm::ProfilingFrameIterator) <= StorageSpace &&
1740                     sizeof(jit::JSJitProfilingFrameIterator) <= StorageSpace,
1741                 "ProfilingFrameIterator::storage_ is too small");
1742   static_assert(alignof(void*) >= alignof(wasm::ProfilingFrameIterator) &&
1743                     alignof(void*) >= alignof(jit::JSJitProfilingFrameIterator),
1744                 "ProfilingFrameIterator::storage_ is too weakly aligned");
1745 
1746   iteratorConstruct(state);
1747   settle();
1748 }
1749 
~ProfilingFrameIterator()1750 JS::ProfilingFrameIterator::~ProfilingFrameIterator() {
1751   if (!done()) {
1752     MOZ_ASSERT(activation_->isProfiling());
1753     iteratorDestroy();
1754   }
1755 }
1756 
operator ++()1757 void JS::ProfilingFrameIterator::operator++() {
1758   MOZ_ASSERT(!done());
1759   MOZ_ASSERT(activation_->isJit());
1760   if (isWasm())
1761     ++wasmIter();
1762   else
1763     ++jsJitIter();
1764   settle();
1765 }
1766 
settleFrames()1767 void JS::ProfilingFrameIterator::settleFrames() {
1768   // Handle transition frames (see comment in JitFrameIter::operator++).
1769   if (isJSJit() && !jsJitIter().done() &&
1770       jsJitIter().frameType() == jit::JitFrame_WasmToJSJit) {
1771     wasm::Frame* fp = (wasm::Frame*)jsJitIter().fp();
1772     iteratorDestroy();
1773     new (storage()) wasm::ProfilingFrameIterator(*activation_->asJit(), fp);
1774     kind_ = Kind::Wasm;
1775     MOZ_ASSERT(!wasmIter().done());
1776     return;
1777   }
1778 
1779   if (isWasm() && wasmIter().done() && wasmIter().unwoundIonCallerFP()) {
1780     uint8_t* fp = wasmIter().unwoundIonCallerFP();
1781     iteratorDestroy();
1782     // Using this ctor will skip the first ion->wasm frame, which is
1783     // needed because the profiling iterator doesn't know how to unwind
1784     // when the callee has no script.
1785     new (storage())
1786         jit::JSJitProfilingFrameIterator((jit::CommonFrameLayout*)fp);
1787     kind_ = Kind::JSJit;
1788     MOZ_ASSERT(!jsJitIter().done());
1789     return;
1790   }
1791 }
1792 
settle()1793 void JS::ProfilingFrameIterator::settle() {
1794   settleFrames();
1795   while (iteratorDone()) {
1796     iteratorDestroy();
1797     activation_ = activation_->prevProfiling();
1798     if (!activation_) return;
1799     iteratorConstruct();
1800     settleFrames();
1801   }
1802 }
1803 
iteratorConstruct(const RegisterState & state)1804 void JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state) {
1805   MOZ_ASSERT(!done());
1806   MOZ_ASSERT(activation_->isJit());
1807 
1808   jit::JitActivation* activation = activation_->asJit();
1809 
1810   // We want to know if we should start with a wasm profiling frame iterator
1811   // or not. To determine this, there are three possibilities:
1812   // - we've exited to C++ from wasm, in which case the activation
1813   //   exitFP low bit is tagged and we can test hasWasmExitFP().
1814   // - we're in wasm code, so we can do a lookup on PC.
1815   // - in all the other cases, we're not in wasm or we haven't exited from
1816   //   wasm.
1817   if (activation->hasWasmExitFP() || wasm::InCompiledCode(state.pc)) {
1818     new (storage()) wasm::ProfilingFrameIterator(*activation, state);
1819     kind_ = Kind::Wasm;
1820     return;
1821   }
1822 
1823   new (storage()) jit::JSJitProfilingFrameIterator(cx_, state.pc);
1824   kind_ = Kind::JSJit;
1825 }
1826 
iteratorConstruct()1827 void JS::ProfilingFrameIterator::iteratorConstruct() {
1828   MOZ_ASSERT(!done());
1829   MOZ_ASSERT(activation_->isJit());
1830 
1831   jit::JitActivation* activation = activation_->asJit();
1832 
1833   // The same reasoning as in the above iteratorConstruct variant applies
1834   // here, except that it's even simpler: since this activation is higher up
1835   // on the stack, it can only have exited to C++, through wasm or ion.
1836   if (activation->hasWasmExitFP()) {
1837     new (storage()) wasm::ProfilingFrameIterator(*activation);
1838     kind_ = Kind::Wasm;
1839     return;
1840   }
1841 
1842   auto* fp = (jit::ExitFrameLayout*)activation->jsExitFP();
1843   new (storage()) jit::JSJitProfilingFrameIterator(fp);
1844   kind_ = Kind::JSJit;
1845 }
1846 
iteratorDestroy()1847 void JS::ProfilingFrameIterator::iteratorDestroy() {
1848   MOZ_ASSERT(!done());
1849   MOZ_ASSERT(activation_->isJit());
1850 
1851   if (isWasm()) {
1852     wasmIter().~ProfilingFrameIterator();
1853     return;
1854   }
1855 
1856   jsJitIter().~JSJitProfilingFrameIterator();
1857 }
1858 
iteratorDone()1859 bool JS::ProfilingFrameIterator::iteratorDone() {
1860   MOZ_ASSERT(!done());
1861   MOZ_ASSERT(activation_->isJit());
1862 
1863   if (isWasm()) return wasmIter().done();
1864 
1865   return jsJitIter().done();
1866 }
1867 
stackAddress() const1868 void* JS::ProfilingFrameIterator::stackAddress() const {
1869   MOZ_ASSERT(!done());
1870   MOZ_ASSERT(activation_->isJit());
1871 
1872   if (isWasm()) return wasmIter().stackAddress();
1873 
1874   return jsJitIter().stackAddress();
1875 }
1876 
1877 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry * entry) const1878 JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(
1879     jit::JitcodeGlobalEntry* entry) const {
1880   void* stackAddr = stackAddress();
1881 
1882   if (isWasm()) {
1883     Frame frame;
1884     frame.kind = Frame_Wasm;
1885     frame.stackAddress = stackAddr;
1886     frame.returnAddress = nullptr;
1887     frame.activation = activation_;
1888     frame.label = nullptr;
1889     return mozilla::Some(frame);
1890   }
1891 
1892   MOZ_ASSERT(isJSJit());
1893 
1894   // Look up an entry for the return address.
1895   void* returnAddr = jsJitIter().returnAddressToFp();
1896   jit::JitcodeGlobalTable* table =
1897       cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
1898   if (samplePositionInProfilerBuffer_)
1899     *entry = table->lookupForSamplerInfallible(
1900         returnAddr, cx_->runtime(), *samplePositionInProfilerBuffer_);
1901   else
1902     *entry = table->lookupInfallible(returnAddr);
1903 
1904   MOZ_ASSERT(entry->isIon() || entry->isIonCache() || entry->isBaseline() ||
1905              entry->isDummy());
1906 
1907   // Dummy frames produce no stack frames.
1908   if (entry->isDummy()) return mozilla::Nothing();
1909 
1910   Frame frame;
1911   frame.kind = entry->isBaseline() ? Frame_Baseline : Frame_Ion;
1912   frame.stackAddress = stackAddr;
1913   frame.returnAddress = returnAddr;
1914   frame.activation = activation_;
1915   frame.label = nullptr;
1916   return mozilla::Some(frame);
1917 }
1918 
extractStack(Frame * frames,uint32_t offset,uint32_t end) const1919 uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
1920                                                   uint32_t offset,
1921                                                   uint32_t end) const {
1922   if (offset >= end) return 0;
1923 
1924   jit::JitcodeGlobalEntry entry;
1925   Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
1926 
1927   // Dummy frames produce no stack frames.
1928   if (physicalFrame.isNothing()) return 0;
1929 
1930   if (isWasm()) {
1931     frames[offset] = physicalFrame.value();
1932     frames[offset].label = wasmIter().label();
1933     return 1;
1934   }
1935 
1936   // Extract the stack for the entry.  Assume maximum inlining depth is <64
1937   const char* labels[64];
1938   uint32_t depth =
1939       entry.callStackAtAddr(cx_->runtime(), jsJitIter().returnAddressToFp(),
1940                             labels, ArrayLength(labels));
1941   MOZ_ASSERT(depth < ArrayLength(labels));
1942   for (uint32_t i = 0; i < depth; i++) {
1943     if (offset + i >= end) return i;
1944     frames[offset + i] = physicalFrame.value();
1945     frames[offset + i].label = labels[i];
1946   }
1947 
1948   return depth;
1949 }
1950 
1951 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameWithoutLabel() const1952 JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const {
1953   jit::JitcodeGlobalEntry unused;
1954   return getPhysicalFrameAndEntry(&unused);
1955 }
1956 
isWasm() const1957 bool JS::ProfilingFrameIterator::isWasm() const {
1958   MOZ_ASSERT(!done());
1959   return kind_ == Kind::Wasm;
1960 }
1961 
isJSJit() const1962 bool JS::ProfilingFrameIterator::isJSJit() const {
1963   return kind_ == Kind::JSJit;
1964 }
1965