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/Stack-inl.h"
8 
9 #include "mozilla/ArrayUtils.h"  // mozilla::ArrayLength
10 #include "mozilla/Maybe.h"       // mozilla::Maybe
11 
12 #include <algorithm>  // std::max
13 #include <stddef.h>   // size_t
14 #include <stdint.h>   // uint8_t, uint32_t
15 #include <utility>    // std::move
16 
17 #include "debugger/DebugAPI.h"
18 #include "gc/Marking.h"
19 #include "gc/Tracer.h"  // js::TraceRoot
20 #include "jit/JitcodeMap.h"
21 #include "js/Value.h"      // JS::Value
22 #include "vm/FrameIter.h"  // js::FrameIter
23 #include "vm/JSContext.h"
24 #include "vm/Opcodes.h"
25 #include "wasm/WasmInstance.h"
26 
27 #include "jit/JSJitFrameIter-inl.h"
28 #include "vm/Compartment-inl.h"
29 #include "vm/EnvironmentObject-inl.h"
30 #include "vm/Interpreter-inl.h"
31 #include "vm/Probes-inl.h"
32 
33 using namespace js;
34 
35 using mozilla::ArrayLength;
36 using mozilla::Maybe;
37 
38 using JS::Value;
39 
40 /*****************************************************************************/
41 
initExecuteFrame(JSContext * cx,HandleScript script,AbstractFramePtr evalInFramePrev,HandleValue newTargetValue,HandleObject envChain)42 void InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script,
43                                         AbstractFramePtr evalInFramePrev,
44                                         HandleValue newTargetValue,
45                                         HandleObject envChain) {
46   flags_ = 0;
47   script_ = script;
48 
49   Value* dstvp = (Value*)this - 1;
50   dstvp[0] = newTargetValue;
51 
52   envChain_ = envChain.get();
53   prev_ = nullptr;
54   prevpc_ = nullptr;
55   prevsp_ = nullptr;
56 
57   evalInFramePrev_ = evalInFramePrev;
58   MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
59 
60   if (script->isDebuggee()) {
61     setIsDebuggee();
62   }
63 
64 #ifdef DEBUG
65   Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
66 #endif
67 }
68 
createRestParameter(JSContext * cx)69 ArrayObject* InterpreterFrame::createRestParameter(JSContext* cx) {
70   MOZ_ASSERT(script()->hasRest());
71   unsigned nformal = callee().nargs() - 1, nactual = numActualArgs();
72   unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
73   Value* restvp = argv() + nformal;
74   return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject,
75                                      ObjectGroup::NewArrayKind::UnknownIndex);
76 }
77 
AssertScopeMatchesEnvironment(Scope * scope,JSObject * originalEnv)78 static inline void AssertScopeMatchesEnvironment(Scope* scope,
79                                                  JSObject* originalEnv) {
80 #ifdef DEBUG
81   JSObject* env = originalEnv;
82   for (ScopeIter si(scope); si; si++) {
83     if (si.kind() == ScopeKind::NonSyntactic) {
84       while (env->is<WithEnvironmentObject>() ||
85              env->is<NonSyntacticVariablesObject>() ||
86              (env->is<LexicalEnvironmentObject>() &&
87               !env->as<LexicalEnvironmentObject>().isSyntactic())) {
88         MOZ_ASSERT(!IsSyntacticEnvironment(env));
89         env = &env->as<EnvironmentObject>().enclosingEnvironment();
90       }
91     } else if (si.hasSyntacticEnvironment()) {
92       switch (si.kind()) {
93         case ScopeKind::Function:
94           MOZ_ASSERT(env->as<CallObject>()
95                          .callee()
96                          .maybeCanonicalFunction()
97                          ->nonLazyScript() ==
98                      si.scope()->as<FunctionScope>().script());
99           env = &env->as<CallObject>().enclosingEnvironment();
100           break;
101 
102         case ScopeKind::FunctionBodyVar:
103           MOZ_ASSERT(&env->as<VarEnvironmentObject>().scope() == si.scope());
104           env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
105           break;
106 
107         case ScopeKind::Lexical:
108         case ScopeKind::SimpleCatch:
109         case ScopeKind::Catch:
110         case ScopeKind::NamedLambda:
111         case ScopeKind::StrictNamedLambda:
112         case ScopeKind::FunctionLexical:
113           MOZ_ASSERT(&env->as<LexicalEnvironmentObject>().scope() ==
114                      si.scope());
115           env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
116           break;
117 
118         case ScopeKind::With:
119           MOZ_ASSERT(&env->as<WithEnvironmentObject>().scope() == si.scope());
120           env = &env->as<WithEnvironmentObject>().enclosingEnvironment();
121           break;
122 
123         case ScopeKind::Eval:
124         case ScopeKind::StrictEval:
125           env = &env->as<VarEnvironmentObject>().enclosingEnvironment();
126           break;
127 
128         case ScopeKind::Global:
129           MOZ_ASSERT(env->as<LexicalEnvironmentObject>().isGlobal());
130           env = &env->as<LexicalEnvironmentObject>().enclosingEnvironment();
131           MOZ_ASSERT(env->is<GlobalObject>());
132           break;
133 
134         case ScopeKind::NonSyntactic:
135           MOZ_CRASH("NonSyntactic should not have a syntactic environment");
136           break;
137 
138         case ScopeKind::Module:
139           MOZ_ASSERT(&env->as<ModuleEnvironmentObject>().module() ==
140                      si.scope()->as<ModuleScope>().module());
141           env = &env->as<ModuleEnvironmentObject>().enclosingEnvironment();
142           break;
143 
144         case ScopeKind::WasmInstance:
145           env =
146               &env->as<WasmInstanceEnvironmentObject>().enclosingEnvironment();
147           break;
148 
149         case ScopeKind::WasmFunction:
150           env = &env->as<WasmFunctionCallObject>().enclosingEnvironment();
151           break;
152       }
153     }
154   }
155 
156   // In the case of a non-syntactic env chain, the immediate parent of the
157   // outermost non-syntactic env may be the global lexical env, or, if
158   // called from Debugger, a DebugEnvironmentProxy.
159   //
160   // In the case of a syntactic env chain, the outermost env is always a
161   // GlobalObject.
162   MOZ_ASSERT(env->is<GlobalObject>() || IsGlobalLexicalEnvironment(env) ||
163              env->is<DebugEnvironmentProxy>());
164 #endif
165 }
166 
AssertScopeMatchesEnvironment(InterpreterFrame * fp,jsbytecode * pc)167 static inline void AssertScopeMatchesEnvironment(InterpreterFrame* fp,
168                                                  jsbytecode* pc) {
169 #ifdef DEBUG
170   // If we OOMed before fully initializing the environment chain, the scope
171   // and environment will definitely mismatch.
172   if (fp->script()->initialEnvironmentShape() && fp->hasInitialEnvironment()) {
173     AssertScopeMatchesEnvironment(fp->script()->innermostScope(pc),
174                                   fp->environmentChain());
175   }
176 #endif
177 }
178 
initFunctionEnvironmentObjects(JSContext * cx)179 bool InterpreterFrame::initFunctionEnvironmentObjects(JSContext* cx) {
180   return js::InitFunctionEnvironmentObjects(cx, this);
181 }
182 
prologue(JSContext * cx)183 bool InterpreterFrame::prologue(JSContext* cx) {
184   RootedScript script(cx, this->script());
185 
186   MOZ_ASSERT(cx->interpreterRegs().pc == script->code());
187   MOZ_ASSERT(cx->realm() == script->realm());
188 
189   if (!isFunctionFrame()) {
190     return probes::EnterScript(cx, script, nullptr, this);
191   }
192 
193   // At this point, we've yet to push any environments. Check that they
194   // match the enclosing scope.
195   AssertScopeMatchesEnvironment(script->enclosingScope(), environmentChain());
196 
197   if (callee().needsFunctionEnvironmentObjects() &&
198       !initFunctionEnvironmentObjects(cx)) {
199     return false;
200   }
201 
202   MOZ_ASSERT_IF(isConstructing(),
203                 thisArgument().isObject() ||
204                     thisArgument().isMagic(JS_UNINITIALIZED_LEXICAL));
205 
206   return probes::EnterScript(cx, script, script->function(), this);
207 }
208 
epilogue(JSContext * cx,jsbytecode * pc)209 void InterpreterFrame::epilogue(JSContext* cx, jsbytecode* pc) {
210   RootedScript script(cx, this->script());
211   MOZ_ASSERT(cx->realm() == script->realm());
212   probes::ExitScript(cx, script, script->function(),
213                      hasPushedGeckoProfilerFrame());
214 
215   // Check that the scope matches the environment at the point of leaving
216   // the frame.
217   AssertScopeMatchesEnvironment(this, pc);
218 
219   EnvironmentIter ei(cx, this, pc);
220   UnwindAllEnvironmentsInFrame(cx, ei);
221 
222   if (isFunctionFrame()) {
223     if (!callee().isGenerator() && !callee().isAsync() && isConstructing() &&
224         thisArgument().isObject() && returnValue().isPrimitive()) {
225       setReturnValue(thisArgument());
226     }
227 
228     return;
229   }
230 
231   MOZ_ASSERT(isEvalFrame() || isGlobalFrame() || isModuleFrame());
232 }
233 
checkReturn(JSContext * cx,HandleValue thisv)234 bool InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv) {
235   MOZ_ASSERT(script()->isDerivedClassConstructor());
236   MOZ_ASSERT(isFunctionFrame());
237   MOZ_ASSERT(callee().isClassConstructor());
238 
239   HandleValue retVal = returnValue();
240   if (retVal.isObject()) {
241     return true;
242   }
243 
244   if (!retVal.isUndefined()) {
245     ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal,
246                      nullptr);
247     return false;
248   }
249 
250   if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
251     return ThrowUninitializedThis(cx);
252   }
253 
254   setReturnValue(thisv);
255   return true;
256 }
257 
pushVarEnvironment(JSContext * cx,HandleScope scope)258 bool InterpreterFrame::pushVarEnvironment(JSContext* cx, HandleScope scope) {
259   return js::PushVarEnvironmentObject(cx, scope, this);
260 }
261 
pushLexicalEnvironment(JSContext * cx,Handle<LexicalScope * > scope)262 bool InterpreterFrame::pushLexicalEnvironment(JSContext* cx,
263                                               Handle<LexicalScope*> scope) {
264   LexicalEnvironmentObject* env =
265       LexicalEnvironmentObject::createForFrame(cx, scope, this);
266   if (!env) {
267     return false;
268   }
269 
270   pushOnEnvironmentChain(*env);
271   return true;
272 }
273 
freshenLexicalEnvironment(JSContext * cx)274 bool InterpreterFrame::freshenLexicalEnvironment(JSContext* cx) {
275   Rooted<LexicalEnvironmentObject*> env(
276       cx, &envChain_->as<LexicalEnvironmentObject>());
277   LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::clone(cx, env);
278   if (!fresh) {
279     return false;
280   }
281 
282   replaceInnermostEnvironment(*fresh);
283   return true;
284 }
285 
recreateLexicalEnvironment(JSContext * cx)286 bool InterpreterFrame::recreateLexicalEnvironment(JSContext* cx) {
287   Rooted<LexicalEnvironmentObject*> env(
288       cx, &envChain_->as<LexicalEnvironmentObject>());
289   LexicalEnvironmentObject* fresh = LexicalEnvironmentObject::recreate(cx, env);
290   if (!fresh) {
291     return false;
292   }
293 
294   replaceInnermostEnvironment(*fresh);
295   return true;
296 }
297 
trace(JSTracer * trc,Value * sp,jsbytecode * pc)298 void InterpreterFrame::trace(JSTracer* trc, Value* sp, jsbytecode* pc) {
299   TraceRoot(trc, &envChain_, "env chain");
300   TraceRoot(trc, &script_, "script");
301 
302   if (flags_ & HAS_ARGS_OBJ) {
303     TraceRoot(trc, &argsObj_, "arguments");
304   }
305 
306   if (hasReturnValue()) {
307     TraceRoot(trc, &rval_, "rval");
308   }
309 
310   MOZ_ASSERT(sp >= slots());
311 
312   if (hasArgs()) {
313     // Trace the callee and |this|. When we're doing a moving GC, we
314     // need to fix up the callee pointer before we use it below, under
315     // numFormalArgs() and script().
316     TraceRootRange(trc, 2, argv_ - 2, "fp callee and this");
317 
318     // Trace arguments.
319     unsigned argc = std::max(numActualArgs(), numFormalArgs());
320     TraceRootRange(trc, argc + isConstructing(), argv_, "fp argv");
321   } else {
322     // Trace newTarget.
323     TraceRoot(trc, ((Value*)this) - 1, "stack newTarget");
324   }
325 
326   JSScript* script = this->script();
327   size_t nfixed = script->nfixed();
328   size_t nlivefixed = script->calculateLiveFixed(pc);
329 
330   if (nfixed == nlivefixed) {
331     // All locals are live.
332     traceValues(trc, 0, sp - slots());
333   } else {
334     // Trace operand stack.
335     traceValues(trc, nfixed, sp - slots());
336 
337     // Clear dead block-scoped locals.
338     while (nfixed > nlivefixed) {
339       unaliasedLocal(--nfixed).setUndefined();
340     }
341 
342     // Trace live locals.
343     traceValues(trc, 0, nlivefixed);
344   }
345 
346   if (auto* debugEnvs = script->realm()->debugEnvs()) {
347     debugEnvs->traceLiveFrame(trc, this);
348   }
349 }
350 
traceValues(JSTracer * trc,unsigned start,unsigned end)351 void InterpreterFrame::traceValues(JSTracer* trc, unsigned start,
352                                    unsigned end) {
353   if (start < end) {
354     TraceRootRange(trc, end - start, slots() + start, "vm_stack");
355   }
356 }
357 
TraceInterpreterActivation(JSTracer * trc,InterpreterActivation * act)358 static void TraceInterpreterActivation(JSTracer* trc,
359                                        InterpreterActivation* act) {
360   for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
361     InterpreterFrame* fp = frames.frame();
362     fp->trace(trc, frames.sp(), frames.pc());
363   }
364 }
365 
TraceInterpreterActivations(JSContext * cx,JSTracer * trc)366 void js::TraceInterpreterActivations(JSContext* cx, JSTracer* trc) {
367   for (ActivationIterator iter(cx); !iter.done(); ++iter) {
368     Activation* act = iter.activation();
369     if (act->isInterpreter()) {
370       TraceInterpreterActivation(trc, act->asInterpreter());
371     }
372   }
373 }
374 
375 /*****************************************************************************/
376 
377 // Unlike the other methods of this class, this method is defined here so that
378 // we don't have to #include jsautooplen.h in vm/Stack.h.
setToEndOfScript()379 void InterpreterRegs::setToEndOfScript() { sp = fp()->base(); }
380 
381 /*****************************************************************************/
382 
pushInvokeFrame(JSContext * cx,const CallArgs & args,MaybeConstruct constructing)383 InterpreterFrame* InterpreterStack::pushInvokeFrame(
384     JSContext* cx, const CallArgs& args, MaybeConstruct constructing) {
385   LifoAlloc::Mark mark = allocator_.mark();
386 
387   RootedFunction fun(cx, &args.callee().as<JSFunction>());
388   RootedScript script(cx, fun->nonLazyScript());
389 
390   Value* argv;
391   InterpreterFrame* fp = getCallFrame(cx, args, script, constructing, &argv);
392   if (!fp) {
393     return nullptr;
394   }
395 
396   fp->mark_ = mark;
397   fp->initCallFrame(nullptr, nullptr, nullptr, *fun, script, argv,
398                     args.length(), constructing);
399   return fp;
400 }
401 
pushExecuteFrame(JSContext * cx,HandleScript script,HandleValue newTargetValue,HandleObject envChain,AbstractFramePtr evalInFrame)402 InterpreterFrame* InterpreterStack::pushExecuteFrame(
403     JSContext* cx, HandleScript script, HandleValue newTargetValue,
404     HandleObject envChain, AbstractFramePtr evalInFrame) {
405   LifoAlloc::Mark mark = allocator_.mark();
406 
407   unsigned nvars = 1 /* newTarget */ + script->nslots();
408   uint8_t* buffer =
409       allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
410   if (!buffer) {
411     return nullptr;
412   }
413 
414   InterpreterFrame* fp =
415       reinterpret_cast<InterpreterFrame*>(buffer + 1 * sizeof(Value));
416   fp->mark_ = mark;
417   fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, envChain);
418   fp->initLocals();
419 
420   return fp;
421 }
422 
423 /*****************************************************************************/
424 
operator ++()425 InterpreterFrameIterator& InterpreterFrameIterator::operator++() {
426   MOZ_ASSERT(!done());
427   if (fp_ != activation_->entryFrame_) {
428     pc_ = fp_->prevpc();
429     sp_ = fp_->prevsp();
430     fp_ = fp_->prev();
431   } else {
432     pc_ = nullptr;
433     sp_ = nullptr;
434     fp_ = nullptr;
435   }
436   return *this;
437 }
438 
ProfilingFrameIterator(JSContext * cx,const RegisterState & state,const Maybe<uint64_t> & samplePositionInProfilerBuffer)439 JS::ProfilingFrameIterator::ProfilingFrameIterator(
440     JSContext* cx, const RegisterState& state,
441     const Maybe<uint64_t>& samplePositionInProfilerBuffer)
442     : cx_(cx),
443       samplePositionInProfilerBuffer_(samplePositionInProfilerBuffer),
444       activation_(nullptr) {
445   if (!cx->runtime()->geckoProfiler().enabled()) {
446     MOZ_CRASH(
447         "ProfilingFrameIterator called when geckoProfiler not enabled for "
448         "runtime.");
449   }
450 
451   if (!cx->profilingActivation()) {
452     return;
453   }
454 
455   // If profiler sampling is not enabled, skip.
456   if (!cx->isProfilerSamplingEnabled()) {
457     return;
458   }
459 
460   activation_ = cx->profilingActivation();
461 
462   MOZ_ASSERT(activation_->isProfiling());
463 
464   static_assert(sizeof(wasm::ProfilingFrameIterator) <= StorageSpace &&
465                     sizeof(jit::JSJitProfilingFrameIterator) <= StorageSpace,
466                 "ProfilingFrameIterator::storage_ is too small");
467   static_assert(alignof(void*) >= alignof(wasm::ProfilingFrameIterator) &&
468                     alignof(void*) >= alignof(jit::JSJitProfilingFrameIterator),
469                 "ProfilingFrameIterator::storage_ is too weakly aligned");
470 
471   iteratorConstruct(state);
472   settle();
473 }
474 
~ProfilingFrameIterator()475 JS::ProfilingFrameIterator::~ProfilingFrameIterator() {
476   if (!done()) {
477     MOZ_ASSERT(activation_->isProfiling());
478     iteratorDestroy();
479   }
480 }
481 
operator ++()482 void JS::ProfilingFrameIterator::operator++() {
483   MOZ_ASSERT(!done());
484   MOZ_ASSERT(activation_->isJit());
485   if (isWasm()) {
486     ++wasmIter();
487   } else {
488     ++jsJitIter();
489   }
490   settle();
491 }
492 
settleFrames()493 void JS::ProfilingFrameIterator::settleFrames() {
494   // Handle transition frames (see comment in JitFrameIter::operator++).
495   if (isJSJit() && !jsJitIter().done() &&
496       jsJitIter().frameType() == jit::FrameType::WasmToJSJit) {
497     wasm::Frame* fp = (wasm::Frame*)jsJitIter().fp();
498     iteratorDestroy();
499     new (storage()) wasm::ProfilingFrameIterator(fp);
500     kind_ = Kind::Wasm;
501     MOZ_ASSERT(!wasmIter().done());
502     return;
503   }
504 
505   if (isWasm() && wasmIter().done() && wasmIter().unwoundIonCallerFP()) {
506     uint8_t* fp = wasmIter().unwoundIonCallerFP();
507     iteratorDestroy();
508     // Using this ctor will skip the first ion->wasm frame, which is
509     // needed because the profiling iterator doesn't know how to unwind
510     // when the callee has no script.
511     new (storage())
512         jit::JSJitProfilingFrameIterator((jit::CommonFrameLayout*)fp);
513     kind_ = Kind::JSJit;
514     MOZ_ASSERT(!jsJitIter().done());
515     return;
516   }
517 }
518 
settle()519 void JS::ProfilingFrameIterator::settle() {
520   settleFrames();
521   while (iteratorDone()) {
522     iteratorDestroy();
523     activation_ = activation_->prevProfiling();
524     if (!activation_) {
525       return;
526     }
527     iteratorConstruct();
528     settleFrames();
529   }
530 }
531 
iteratorConstruct(const RegisterState & state)532 void JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state) {
533   MOZ_ASSERT(!done());
534   MOZ_ASSERT(activation_->isJit());
535 
536   jit::JitActivation* activation = activation_->asJit();
537 
538   // We want to know if we should start with a wasm profiling frame iterator
539   // or not. To determine this, there are three possibilities:
540   // - we've exited to C++ from wasm, in which case the activation
541   //   exitFP low bit is tagged and we can test hasWasmExitFP().
542   // - we're in wasm code, so we can do a lookup on PC.
543   // - in all the other cases, we're not in wasm or we haven't exited from
544   //   wasm.
545   if (activation->hasWasmExitFP() || wasm::InCompiledCode(state.pc)) {
546     new (storage()) wasm::ProfilingFrameIterator(*activation, state);
547     kind_ = Kind::Wasm;
548     return;
549   }
550 
551   new (storage()) jit::JSJitProfilingFrameIterator(cx_, state.pc);
552   kind_ = Kind::JSJit;
553 }
554 
iteratorConstruct()555 void JS::ProfilingFrameIterator::iteratorConstruct() {
556   MOZ_ASSERT(!done());
557   MOZ_ASSERT(activation_->isJit());
558 
559   jit::JitActivation* activation = activation_->asJit();
560 
561   // The same reasoning as in the above iteratorConstruct variant applies
562   // here, except that it's even simpler: since this activation is higher up
563   // on the stack, it can only have exited to C++, through wasm or ion.
564   if (activation->hasWasmExitFP()) {
565     new (storage()) wasm::ProfilingFrameIterator(*activation);
566     kind_ = Kind::Wasm;
567     return;
568   }
569 
570   auto* fp = (jit::ExitFrameLayout*)activation->jsExitFP();
571   new (storage()) jit::JSJitProfilingFrameIterator(fp);
572   kind_ = Kind::JSJit;
573 }
574 
iteratorDestroy()575 void JS::ProfilingFrameIterator::iteratorDestroy() {
576   MOZ_ASSERT(!done());
577   MOZ_ASSERT(activation_->isJit());
578 
579   if (isWasm()) {
580     wasmIter().~ProfilingFrameIterator();
581     return;
582   }
583 
584   jsJitIter().~JSJitProfilingFrameIterator();
585 }
586 
iteratorDone()587 bool JS::ProfilingFrameIterator::iteratorDone() {
588   MOZ_ASSERT(!done());
589   MOZ_ASSERT(activation_->isJit());
590 
591   if (isWasm()) {
592     return wasmIter().done();
593   }
594 
595   return jsJitIter().done();
596 }
597 
stackAddress() const598 void* JS::ProfilingFrameIterator::stackAddress() const {
599   MOZ_ASSERT(!done());
600   MOZ_ASSERT(activation_->isJit());
601 
602   if (isWasm()) {
603     return wasmIter().stackAddress();
604   }
605 
606   return jsJitIter().stackAddress();
607 }
608 
609 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry * entry) const610 JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(
611     jit::JitcodeGlobalEntry* entry) const {
612   void* stackAddr = stackAddress();
613 
614   if (isWasm()) {
615     Frame frame;
616     frame.kind = Frame_Wasm;
617     frame.stackAddress = stackAddr;
618     frame.returnAddress_ = nullptr;
619     frame.activation = activation_;
620     frame.label = nullptr;
621     frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP();
622     frame.interpreterScript = nullptr;
623     // TODO: get the realm ID of wasm frames. Bug 1596235.
624     frame.realmID = 0;
625     return mozilla::Some(frame);
626   }
627 
628   MOZ_ASSERT(isJSJit());
629 
630   // Look up an entry for the return address.
631   void* returnAddr = jsJitIter().resumePCinCurrentFrame();
632   jit::JitcodeGlobalTable* table =
633       cx_->runtime()->jitRuntime()->getJitcodeGlobalTable();
634 
635   // NB:
636   // The following lookups should be infallible, but the ad-hoc stackwalking
637   // code rots easily and corner cases where frames can't be looked up
638   // occur too often (e.g. once every day).
639   //
640   // The calls to `lookup*` below have been changed from infallible ones to
641   // fallible ones.  The proper solution to this problem is to fix all
642   // the jitcode to use frame-pointers and reliably walk the stack with those.
643   const jit::JitcodeGlobalEntry* lookedUpEntry = nullptr;
644   if (samplePositionInProfilerBuffer_) {
645     lookedUpEntry = table->lookupForSampler(returnAddr, cx_->runtime(),
646                                             *samplePositionInProfilerBuffer_);
647   } else {
648     lookedUpEntry = table->lookup(returnAddr);
649   }
650 
651   // Failed to look up a jitcode entry for the given address, ignore.
652   if (!lookedUpEntry) {
653     return mozilla::Nothing();
654   }
655   *entry = *lookedUpEntry;
656 
657   MOZ_ASSERT(entry->isIon() || entry->isBaseline() ||
658              entry->isBaselineInterpreter() || entry->isDummy());
659 
660   // Dummy frames produce no stack frames.
661   if (entry->isDummy()) {
662     return mozilla::Nothing();
663   }
664 
665   Frame frame;
666   if (entry->isBaselineInterpreter()) {
667     frame.kind = Frame_BaselineInterpreter;
668   } else if (entry->isBaseline()) {
669     frame.kind = Frame_Baseline;
670   } else {
671     frame.kind = Frame_Ion;
672   }
673   frame.stackAddress = stackAddr;
674   if (entry->isBaselineInterpreter()) {
675     frame.label = jsJitIter().baselineInterpreterLabel();
676     jsJitIter().baselineInterpreterScriptPC(
677         &frame.interpreterScript, &frame.interpreterPC_, &frame.realmID);
678     MOZ_ASSERT(frame.interpreterScript);
679     MOZ_ASSERT(frame.interpreterPC_);
680   } else {
681     frame.interpreterScript = nullptr;
682     frame.returnAddress_ = returnAddr;
683     frame.label = nullptr;
684     frame.realmID = 0;
685   }
686   frame.activation = activation_;
687   frame.endStackAddress = activation_->asJit()->jsOrWasmExitFP();
688   return mozilla::Some(frame);
689 }
690 
extractStack(Frame * frames,uint32_t offset,uint32_t end) const691 uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
692                                                   uint32_t offset,
693                                                   uint32_t end) const {
694   if (offset >= end) {
695     return 0;
696   }
697 
698   jit::JitcodeGlobalEntry entry;
699   Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
700 
701   // Dummy frames produce no stack frames.
702   if (physicalFrame.isNothing()) {
703     return 0;
704   }
705 
706   if (isWasm()) {
707     frames[offset] = physicalFrame.value();
708     frames[offset].label = wasmIter().label();
709     return 1;
710   }
711 
712   if (physicalFrame->kind == Frame_BaselineInterpreter) {
713     frames[offset] = physicalFrame.value();
714     return 1;
715   }
716 
717   // Extract the stack for the entry.  Assume maximum inlining depth is <64
718   const char* labels[64];
719   uint32_t depth = entry.callStackAtAddr(cx_->runtime(),
720                                          jsJitIter().resumePCinCurrentFrame(),
721                                          labels, ArrayLength(labels));
722   MOZ_ASSERT(depth < ArrayLength(labels));
723   for (uint32_t i = 0; i < depth; i++) {
724     if (offset + i >= end) {
725       return i;
726     }
727     frames[offset + i] = physicalFrame.value();
728     frames[offset + i].label = labels[i];
729   }
730 
731   return depth;
732 }
733 
734 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameWithoutLabel() const735 JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const {
736   jit::JitcodeGlobalEntry unused;
737   return getPhysicalFrameAndEntry(&unused);
738 }
739 
isWasm() const740 bool JS::ProfilingFrameIterator::isWasm() const {
741   MOZ_ASSERT(!done());
742   return kind_ == Kind::Wasm;
743 }
744 
isJSJit() const745 bool JS::ProfilingFrameIterator::isJSJit() const {
746   return kind_ == Kind::JSJit;
747 }
748