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