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