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 "mozilla/PodOperations.h"
10
11 #include "jscntxt.h"
12
13 #include "asmjs/AsmJSFrameIterator.h"
14 #include "asmjs/AsmJSModule.h"
15 #include "gc/Marking.h"
16 #include "jit/BaselineFrame.h"
17 #include "jit/JitcodeMap.h"
18 #include "jit/JitCompartment.h"
19 #include "js/GCAPI.h"
20 #include "vm/Debugger.h"
21 #include "vm/Opcodes.h"
22
23 #include "jit/JitFrameIterator-inl.h"
24 #include "vm/Interpreter-inl.h"
25 #include "vm/Probes-inl.h"
26 #include "vm/ScopeObject-inl.h"
27
28 using namespace js;
29
30 using mozilla::Maybe;
31 using mozilla::PodCopy;
32
33 /*****************************************************************************/
34
35 void
initExecuteFrame(JSContext * cx,HandleScript script,AbstractFramePtr evalInFramePrev,const Value & newTargetValue,HandleObject scopeChain,ExecuteType type)36 InterpreterFrame::initExecuteFrame(JSContext* cx, HandleScript script, AbstractFramePtr evalInFramePrev,
37 const Value& newTargetValue, HandleObject scopeChain,
38 ExecuteType type)
39 {
40 /*
41 * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
42 * script in the context of another frame and the frame type is determined
43 * by the context.
44 */
45 flags_ = type | HAS_SCOPECHAIN;
46
47 JSObject* callee = nullptr;
48
49 // newTarget = NullValue is an initial sentinel for "please fill me in from the stack".
50 // It should never be passed from Ion code.
51 RootedValue newTarget(cx, newTargetValue);
52 if (!(flags_ & (GLOBAL | MODULE))) {
53 if (evalInFramePrev) {
54 MOZ_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame());
55 if (evalInFramePrev.isFunctionFrame()) {
56 callee = evalInFramePrev.callee();
57 if (newTarget.isNull())
58 newTarget = evalInFramePrev.newTarget();
59 flags_ |= FUNCTION;
60 } else {
61 flags_ |= GLOBAL;
62 }
63 } else {
64 FrameIter iter(cx);
65 MOZ_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame());
66 MOZ_ASSERT(!iter.isAsmJS());
67 if (iter.isFunctionFrame()) {
68 if (newTarget.isNull())
69 newTarget = iter.newTarget();
70 callee = iter.callee(cx);
71 flags_ |= FUNCTION;
72 } else {
73 flags_ |= GLOBAL;
74 }
75 }
76 }
77
78 Value* dstvp = (Value*)this - 2;
79
80 if (isFunctionFrame()) {
81 dstvp[1] = ObjectValue(*callee);
82 exec.fun = &callee->as<JSFunction>();
83 u.evalScript = script;
84 } else {
85 MOZ_ASSERT(isGlobalFrame() || isModuleFrame());
86 dstvp[1] = NullValue();
87 exec.script = script;
88 #ifdef DEBUG
89 u.evalScript = (JSScript*)0xbad;
90 #endif
91 }
92 dstvp[0] = newTarget;
93
94 scopeChain_ = scopeChain.get();
95 prev_ = nullptr;
96 prevpc_ = nullptr;
97 prevsp_ = nullptr;
98
99 MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
100 evalInFramePrev_ = evalInFramePrev;
101
102 if (script->isDebuggee())
103 setIsDebuggee();
104
105 #ifdef DEBUG
106 Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
107 #endif
108 }
109
110 bool
isNonGlobalEvalFrame() const111 InterpreterFrame::isNonGlobalEvalFrame() const
112 {
113 return isEvalFrame() &&
114 script()->enclosingStaticScope()->as<StaticEvalObject>().isNonGlobal();
115 }
116
117 bool
copyRawFrameSlots(AutoValueVector * vec)118 InterpreterFrame::copyRawFrameSlots(AutoValueVector* vec)
119 {
120 if (!vec->resize(numFormalArgs() + script()->nfixed()))
121 return false;
122 PodCopy(vec->begin(), argv(), numFormalArgs());
123 PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed());
124 return true;
125 }
126
127 JSObject*
createRestParameter(JSContext * cx)128 InterpreterFrame::createRestParameter(JSContext* cx)
129 {
130 MOZ_ASSERT(fun()->hasRest());
131 unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs();
132 unsigned nrest = (nactual > nformal) ? nactual - nformal : 0;
133 Value* restvp = argv() + nformal;
134 return ObjectGroup::newArrayObject(cx, restvp, nrest, GenericObject,
135 ObjectGroup::NewArrayKind::UnknownIndex);
136 }
137
138 static inline void
AssertDynamicScopeMatchesStaticScope(JSContext * cx,JSScript * script,JSObject * scope)139 AssertDynamicScopeMatchesStaticScope(JSContext* cx, JSScript* script, JSObject* scope)
140 {
141 #ifdef DEBUG
142 RootedObject originalScope(cx, scope);
143 RootedObject enclosingScope(cx, script->enclosingStaticScope());
144 for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) {
145 if (i.type() == StaticScopeIter<NoGC>::NonSyntactic) {
146 while (scope->is<DynamicWithObject>() ||
147 scope->is<NonSyntacticVariablesObject>() ||
148 (scope->is<ClonedBlockObject>() &&
149 !scope->as<ClonedBlockObject>().isSyntactic()))
150 {
151 MOZ_ASSERT(!IsSyntacticScope(scope));
152 scope = &scope->as<ScopeObject>().enclosingScope();
153 }
154 } else if (i.hasSyntacticDynamicScopeObject()) {
155 switch (i.type()) {
156 case StaticScopeIter<NoGC>::Module:
157 MOZ_ASSERT(scope->as<ModuleEnvironmentObject>().module().script() == i.moduleScript());
158 scope = &scope->as<ModuleEnvironmentObject>().enclosingScope();
159 break;
160 case StaticScopeIter<NoGC>::Function:
161 MOZ_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript());
162 scope = &scope->as<CallObject>().enclosingScope();
163 break;
164 case StaticScopeIter<NoGC>::Block:
165 MOZ_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope());
166 scope = &scope->as<ClonedBlockObject>().enclosingScope();
167 break;
168 case StaticScopeIter<NoGC>::With:
169 MOZ_ASSERT(&i.staticWith() == scope->as<DynamicWithObject>().staticScope());
170 scope = &scope->as<DynamicWithObject>().enclosingScope();
171 break;
172 case StaticScopeIter<NoGC>::NamedLambda:
173 scope = &scope->as<DeclEnvObject>().enclosingScope();
174 break;
175 case StaticScopeIter<NoGC>::Eval:
176 scope = &scope->as<CallObject>().enclosingScope();
177 break;
178 case StaticScopeIter<NoGC>::NonSyntactic:
179 MOZ_CRASH("NonSyntactic should not have a syntactic scope");
180 break;
181 }
182 }
183 }
184
185 // In the case of a non-syntactic scope chain, the immediate parent of the
186 // outermost non-syntactic scope may be the global lexical scope, or, if
187 // called from Debugger, a DebugScopeObject.
188 //
189 // In the case of a syntactic scope chain, the outermost scope is always a
190 // GlobalObject.
191 MOZ_ASSERT(scope->is<GlobalObject>() || IsGlobalLexicalScope(scope) ||
192 scope->is<DebugScopeObject>());
193 #endif
194 }
195
196 bool
initFunctionScopeObjects(JSContext * cx)197 InterpreterFrame::initFunctionScopeObjects(JSContext* cx)
198 {
199 CallObject* callobj = CallObject::createForFunction(cx, this);
200 if (!callobj)
201 return false;
202 pushOnScopeChain(*callobj);
203 flags_ |= HAS_CALL_OBJ;
204 return true;
205 }
206
207 bool
prologue(JSContext * cx)208 InterpreterFrame::prologue(JSContext* cx)
209 {
210 RootedScript script(cx, this->script());
211
212 MOZ_ASSERT(isModuleFrame() == !!script->module());
213 MOZ_ASSERT(cx->interpreterRegs().pc == script->code());
214
215 if (isEvalFrame()) {
216 if (script->strict()) {
217 CallObject* callobj = CallObject::createForStrictEval(cx, this);
218 if (!callobj)
219 return false;
220 pushOnScopeChain(*callobj);
221 flags_ |= HAS_CALL_OBJ;
222 } else {
223 // Non-strict eval may introduce var bindings that conflict with
224 // lexical bindings in an enclosing lexical scope.
225 RootedObject varObjRoot(cx, &varObj());
226 if (!CheckEvalDeclarationConflicts(cx, script, scopeChain(), varObjRoot))
227 return false;
228 }
229 return probes::EnterScript(cx, script, nullptr, this);
230 }
231
232 if (isGlobalFrame()) {
233 Rooted<ClonedBlockObject*> lexicalScope(cx);
234 RootedObject varObjRoot(cx);
235 if (script->hasNonSyntacticScope()) {
236 lexicalScope = &extensibleLexicalScope();
237 varObjRoot = &varObj();
238 } else {
239 lexicalScope = &cx->global()->lexicalScope();
240 varObjRoot = cx->global();
241 }
242 if (!CheckGlobalDeclarationConflicts(cx, script, lexicalScope, varObjRoot))
243 return false;
244 return probes::EnterScript(cx, script, nullptr, this);
245 }
246
247 AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain());
248
249 if (isModuleFrame())
250 return probes::EnterScript(cx, script, nullptr, this);
251
252 MOZ_ASSERT(isNonEvalFunctionFrame());
253 if (fun()->needsCallObject() && !initFunctionScopeObjects(cx))
254 return false;
255
256 if (isConstructing()) {
257 if (script->isDerivedClassConstructor()) {
258 MOZ_ASSERT(callee().isClassConstructor());
259 thisArgument() = MagicValue(JS_UNINITIALIZED_LEXICAL);
260 } else if (thisArgument().isPrimitive()) {
261 RootedObject callee(cx, &this->callee());
262 RootedObject newTarget(cx, &this->newTarget().toObject());
263 JSObject* obj = CreateThisForFunction(cx, callee, newTarget,
264 createSingleton() ? SingletonObject : GenericObject);
265 if (!obj)
266 return false;
267 thisArgument() = ObjectValue(*obj);
268 }
269 }
270
271 return probes::EnterScript(cx, script, script->functionNonDelazifying(), this);
272 }
273
274 void
epilogue(JSContext * cx)275 InterpreterFrame::epilogue(JSContext* cx)
276 {
277 RootedScript script(cx, this->script());
278 probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame());
279
280 if (isEvalFrame()) {
281 if (isStrictEvalFrame()) {
282 MOZ_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval());
283 if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
284 DebugScopes::onPopStrictEvalScope(this);
285 } else if (isNonGlobalEvalFrame()) {
286 MOZ_ASSERT_IF(isDebuggerEvalFrame(), !IsSyntacticScope(scopeChain()));
287 }
288 return;
289 }
290
291 if (isGlobalFrame()) {
292 // Confusingly, global frames may run in non-global scopes (that is,
293 // not directly under the GlobalObject and its lexical scope).
294 //
295 // Gecko often runs global scripts under custom scopes. See users of
296 // CreateNonSyntacticScopeChain.
297 MOZ_ASSERT(IsGlobalLexicalScope(scopeChain()) || !IsSyntacticScope(scopeChain()));
298 return;
299 }
300
301 if (isModuleFrame())
302 return;
303
304 MOZ_ASSERT(isNonEvalFunctionFrame());
305
306 if (fun()->needsCallObject()) {
307 MOZ_ASSERT_IF(hasCallObj() && !fun()->isGenerator(),
308 scopeChain()->as<CallObject>().callee().nonLazyScript() == script);
309 } else {
310 AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain());
311 }
312
313 if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
314 DebugScopes::onPopCall(this, cx);
315
316 if (!fun()->isGenerator() &&
317 isConstructing() &&
318 thisArgument().isObject() &&
319 returnValue().isPrimitive())
320 {
321 setReturnValue(thisArgument());
322 }
323 }
324
325 bool
checkReturn(JSContext * cx,HandleValue thisv)326 InterpreterFrame::checkReturn(JSContext* cx, HandleValue thisv)
327 {
328 MOZ_ASSERT(script()->isDerivedClassConstructor());
329 MOZ_ASSERT(isFunctionFrame());
330 MOZ_ASSERT(callee().isClassConstructor());
331
332 HandleValue retVal = returnValue();
333 if (retVal.isObject())
334 return true;
335
336 if (!retVal.isUndefined()) {
337 ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, retVal, nullptr);
338 return false;
339 }
340
341 if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL))
342 return ThrowUninitializedThis(cx, this);
343
344 setReturnValue(thisv);
345 return true;
346 }
347
348 bool
pushBlock(JSContext * cx,StaticBlockObject & block)349 InterpreterFrame::pushBlock(JSContext* cx, StaticBlockObject& block)
350 {
351 MOZ_ASSERT(block.needsClone());
352
353 Rooted<StaticBlockObject*> blockHandle(cx, &block);
354 ClonedBlockObject* clone = ClonedBlockObject::create(cx, blockHandle, this);
355 if (!clone)
356 return false;
357
358 pushOnScopeChain(*clone);
359
360 return true;
361 }
362
363 bool
freshenBlock(JSContext * cx)364 InterpreterFrame::freshenBlock(JSContext* cx)
365 {
366 MOZ_ASSERT(flags_ & HAS_SCOPECHAIN);
367 Rooted<ClonedBlockObject*> block(cx, &scopeChain_->as<ClonedBlockObject>());
368 ClonedBlockObject* fresh = ClonedBlockObject::clone(cx, block);
369 if (!fresh)
370 return false;
371
372 replaceInnermostScope(*fresh);
373 return true;
374 }
375
376 void
popBlock(JSContext * cx)377 InterpreterFrame::popBlock(JSContext* cx)
378 {
379 MOZ_ASSERT(scopeChain_->is<ClonedBlockObject>());
380 popOffScopeChain();
381 }
382
383 void
popWith(JSContext * cx)384 InterpreterFrame::popWith(JSContext* cx)
385 {
386 if (MOZ_UNLIKELY(cx->compartment()->isDebuggee()))
387 DebugScopes::onPopWith(this);
388
389 MOZ_ASSERT(scopeChain()->is<DynamicWithObject>());
390 popOffScopeChain();
391 }
392
393 void
mark(JSTracer * trc)394 InterpreterFrame::mark(JSTracer* trc)
395 {
396 /*
397 * Normally we would use MarkRoot here, except that generators also take
398 * this path. However, generators use a special write barrier when the stack
399 * frame is copied to the floating frame. Therefore, no barrier is needed.
400 */
401 if (flags_ & HAS_SCOPECHAIN)
402 TraceManuallyBarrieredEdge(trc, &scopeChain_, "scope chain");
403 if (flags_ & HAS_ARGS_OBJ)
404 TraceManuallyBarrieredEdge(trc, &argsObj_, "arguments");
405 if (isFunctionFrame()) {
406 TraceManuallyBarrieredEdge(trc, &exec.fun, "fun");
407 if (isEvalFrame())
408 TraceManuallyBarrieredEdge(trc, &u.evalScript, "eval script");
409 } else {
410 TraceManuallyBarrieredEdge(trc, &exec.script, "script");
411 }
412 if (trc->isMarkingTracer())
413 script()->compartment()->zone()->active = true;
414 if (hasReturnValue())
415 TraceManuallyBarrieredEdge(trc, &rval_, "rval");
416 }
417
418 void
markValues(JSTracer * trc,unsigned start,unsigned end)419 InterpreterFrame::markValues(JSTracer* trc, unsigned start, unsigned end)
420 {
421 if (start < end)
422 TraceRootRange(trc, end - start, slots() + start, "vm_stack");
423 }
424
425 void
markValues(JSTracer * trc,Value * sp,jsbytecode * pc)426 InterpreterFrame::markValues(JSTracer* trc, Value* sp, jsbytecode* pc)
427 {
428 MOZ_ASSERT(sp >= slots());
429
430 JSScript* script = this->script();
431 size_t nfixed = script->nfixed();
432 size_t nlivefixed = script->calculateLiveFixed(pc);
433
434 if (nfixed == nlivefixed) {
435 // All locals are live.
436 markValues(trc, 0, sp - slots());
437 } else {
438 // Mark operand stack.
439 markValues(trc, nfixed, sp - slots());
440
441 // Clear dead block-scoped locals.
442 while (nfixed > nlivefixed)
443 unaliasedLocal(--nfixed).setMagic(JS_UNINITIALIZED_LEXICAL);
444
445 // Mark live locals.
446 markValues(trc, 0, nlivefixed);
447 }
448
449 if (hasArgs()) {
450 // Mark callee, |this| and arguments.
451 unsigned argc = Max(numActualArgs(), numFormalArgs());
452 TraceRootRange(trc, argc + 2 + isConstructing(), argv_ - 2, "fp argv");
453 } else {
454 // Mark callee and newTarget
455 TraceRootRange(trc, 2, ((Value*)this) - 2, "stack callee and newTarget");
456 }
457 }
458
459 static void
MarkInterpreterActivation(JSTracer * trc,InterpreterActivation * act)460 MarkInterpreterActivation(JSTracer* trc, InterpreterActivation* act)
461 {
462 for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) {
463 InterpreterFrame* fp = frames.frame();
464 fp->markValues(trc, frames.sp(), frames.pc());
465 fp->mark(trc);
466 }
467 }
468
469 void
MarkInterpreterActivations(JSRuntime * rt,JSTracer * trc)470 js::MarkInterpreterActivations(JSRuntime* rt, JSTracer* trc)
471 {
472 for (ActivationIterator iter(rt); !iter.done(); ++iter) {
473 Activation* act = iter.activation();
474 if (act->isInterpreter())
475 MarkInterpreterActivation(trc, act->asInterpreter());
476 }
477 }
478
479 /*****************************************************************************/
480
481 // Unlike the other methods of this class, this method is defined here so that
482 // we don't have to #include jsautooplen.h in vm/Stack.h.
483 void
setToEndOfScript()484 InterpreterRegs::setToEndOfScript()
485 {
486 sp = fp()->base();
487 pc = fp()->script()->lastPC();
488 }
489
490 /*****************************************************************************/
491
492 InterpreterFrame*
pushInvokeFrame(JSContext * cx,const CallArgs & args,InitialFrameFlags initial)493 InterpreterStack::pushInvokeFrame(JSContext* cx, const CallArgs& args, InitialFrameFlags initial)
494 {
495 LifoAlloc::Mark mark = allocator_.mark();
496
497 RootedFunction fun(cx, &args.callee().as<JSFunction>());
498 RootedScript script(cx, fun->nonLazyScript());
499
500 InterpreterFrame::Flags flags = ToFrameFlags(initial);
501 Value* argv;
502 InterpreterFrame* fp = getCallFrame(cx, args, script, &flags, &argv);
503 if (!fp)
504 return nullptr;
505
506 fp->mark_ = mark;
507 fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), flags);
508 return fp;
509 }
510
511 InterpreterFrame*
pushExecuteFrame(JSContext * cx,HandleScript script,const Value & newTargetValue,HandleObject scopeChain,ExecuteType type,AbstractFramePtr evalInFrame)512 InterpreterStack::pushExecuteFrame(JSContext* cx, HandleScript script, const Value& newTargetValue,
513 HandleObject scopeChain, ExecuteType type,
514 AbstractFramePtr evalInFrame)
515 {
516 LifoAlloc::Mark mark = allocator_.mark();
517
518 unsigned nvars = 2 /* callee, newTarget */ + script->nslots();
519 uint8_t* buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value));
520 if (!buffer)
521 return nullptr;
522
523 InterpreterFrame* fp = reinterpret_cast<InterpreterFrame*>(buffer + 2 * sizeof(Value));
524 fp->mark_ = mark;
525 fp->initExecuteFrame(cx, script, evalInFrame, newTargetValue, scopeChain, type);
526 fp->initLocals();
527
528 return fp;
529 }
530
531 /*****************************************************************************/
532
533 void
popActivation()534 FrameIter::popActivation()
535 {
536 ++data_.activations_;
537 settleOnActivation();
538 }
539
540 void
popInterpreterFrame()541 FrameIter::popInterpreterFrame()
542 {
543 MOZ_ASSERT(data_.state_ == INTERP);
544
545 ++data_.interpFrames_;
546
547 if (data_.interpFrames_.done())
548 popActivation();
549 else
550 data_.pc_ = data_.interpFrames_.pc();
551 }
552
553 void
settleOnActivation()554 FrameIter::settleOnActivation()
555 {
556 while (true) {
557 if (data_.activations_.done()) {
558 data_.state_ = DONE;
559 return;
560 }
561
562 Activation* activation = data_.activations_.activation();
563
564 // If JS_SaveFrameChain was called, stop iterating here (unless
565 // GO_THROUGH_SAVED is set).
566 if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) {
567 data_.state_ = DONE;
568 return;
569 }
570
571 // Skip activations from another context if needed.
572 MOZ_ASSERT(activation->cx());
573 MOZ_ASSERT(data_.cx_);
574 if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) {
575 ++data_.activations_;
576 continue;
577 }
578
579 // If the caller supplied principals, only show activations which are subsumed (of the same
580 // origin or of an origin accessible) by these principals.
581 if (data_.principals_) {
582 JSContext* cx = data_.cx_->asJSContext();
583 if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) {
584 if (!subsumes(data_.principals_, activation->compartment()->principals())) {
585 ++data_.activations_;
586 continue;
587 }
588 }
589 }
590
591 if (activation->isJit()) {
592 data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
593
594 // Stop at the first scripted frame.
595 while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done())
596 ++data_.jitFrames_;
597
598 // It's possible to have an JitActivation with no scripted frames,
599 // for instance if we hit an over-recursion during bailout.
600 if (data_.jitFrames_.done()) {
601 ++data_.activations_;
602 continue;
603 }
604
605 nextJitFrame();
606 data_.state_ = JIT;
607 return;
608 }
609
610 if (activation->isAsmJS()) {
611 data_.asmJSFrames_ = AsmJSFrameIterator(*data_.activations_->asAsmJS());
612
613 if (data_.asmJSFrames_.done()) {
614 ++data_.activations_;
615 continue;
616 }
617
618 data_.state_ = ASMJS;
619 return;
620 }
621
622 MOZ_ASSERT(activation->isInterpreter());
623
624 InterpreterActivation* interpAct = activation->asInterpreter();
625 data_.interpFrames_ = InterpreterFrameIterator(interpAct);
626
627 // If we OSR'ed into JIT code, skip the interpreter frame so that
628 // the same frame is not reported twice.
629 if (data_.interpFrames_.frame()->runningInJit()) {
630 ++data_.interpFrames_;
631 if (data_.interpFrames_.done()) {
632 ++data_.activations_;
633 continue;
634 }
635 }
636
637 MOZ_ASSERT(!data_.interpFrames_.frame()->runningInJit());
638 data_.pc_ = data_.interpFrames_.pc();
639 data_.state_ = INTERP;
640 return;
641 }
642 }
643
Data(JSContext * cx,SavedOption savedOption,ContextOption contextOption,DebuggerEvalOption debuggerEvalOption,JSPrincipals * principals)644 FrameIter::Data::Data(JSContext* cx, SavedOption savedOption,
645 ContextOption contextOption, DebuggerEvalOption debuggerEvalOption,
646 JSPrincipals* principals)
647 : cx_(cx),
648 savedOption_(savedOption),
649 contextOption_(contextOption),
650 debuggerEvalOption_(debuggerEvalOption),
651 principals_(principals),
652 pc_(nullptr),
653 interpFrames_(nullptr),
654 activations_(cx->runtime()),
655 jitFrames_(),
656 ionInlineFrameNo_(0),
657 asmJSFrames_()
658 {
659 }
660
Data(const FrameIter::Data & other)661 FrameIter::Data::Data(const FrameIter::Data& other)
662 : cx_(other.cx_),
663 savedOption_(other.savedOption_),
664 contextOption_(other.contextOption_),
665 debuggerEvalOption_(other.debuggerEvalOption_),
666 principals_(other.principals_),
667 state_(other.state_),
668 pc_(other.pc_),
669 interpFrames_(other.interpFrames_),
670 activations_(other.activations_),
671 jitFrames_(other.jitFrames_),
672 ionInlineFrameNo_(other.ionInlineFrameNo_),
673 asmJSFrames_(other.asmJSFrames_)
674 {
675 }
676
FrameIter(JSContext * cx,SavedOption savedOption)677 FrameIter::FrameIter(JSContext* cx, SavedOption savedOption)
678 : data_(cx, savedOption, CURRENT_CONTEXT, FOLLOW_DEBUGGER_EVAL_PREV_LINK, nullptr),
679 ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
680 {
681 // settleOnActivation can only GC if principals are given.
682 JS::AutoSuppressGCAnalysis nogc;
683 settleOnActivation();
684 }
685
FrameIter(JSContext * cx,ContextOption contextOption,SavedOption savedOption,DebuggerEvalOption debuggerEvalOption)686 FrameIter::FrameIter(JSContext* cx, ContextOption contextOption,
687 SavedOption savedOption, DebuggerEvalOption debuggerEvalOption)
688 : data_(cx, savedOption, contextOption, debuggerEvalOption, nullptr),
689 ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
690 {
691 // settleOnActivation can only GC if principals are given.
692 JS::AutoSuppressGCAnalysis nogc;
693 settleOnActivation();
694 }
695
FrameIter(JSContext * cx,ContextOption contextOption,SavedOption savedOption,DebuggerEvalOption debuggerEvalOption,JSPrincipals * principals)696 FrameIter::FrameIter(JSContext* cx, ContextOption contextOption,
697 SavedOption savedOption, DebuggerEvalOption debuggerEvalOption,
698 JSPrincipals* principals)
699 : data_(cx, savedOption, contextOption, debuggerEvalOption, principals),
700 ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
701 {
702 settleOnActivation();
703 }
704
FrameIter(const FrameIter & other)705 FrameIter::FrameIter(const FrameIter& other)
706 : data_(other.data_),
707 ionInlineFrames_(other.data_.cx_,
708 data_.jitFrames_.isIonScripted() ? &other.ionInlineFrames_ : nullptr)
709 {
710 }
711
FrameIter(const Data & data)712 FrameIter::FrameIter(const Data& data)
713 : data_(data),
714 ionInlineFrames_(data.cx_, data_.jitFrames_.isIonScripted() ? &data_.jitFrames_ : nullptr)
715 {
716 MOZ_ASSERT(data.cx_);
717
718 if (data_.jitFrames_.isIonScripted()) {
719 while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_)
720 ++ionInlineFrames_;
721 }
722 }
723
724 void
nextJitFrame()725 FrameIter::nextJitFrame()
726 {
727 if (data_.jitFrames_.isIonScripted()) {
728 ionInlineFrames_.resetOn(&data_.jitFrames_);
729 data_.pc_ = ionInlineFrames_.pc();
730 } else {
731 MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
732 data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
733 }
734 }
735
736 void
popJitFrame()737 FrameIter::popJitFrame()
738 {
739 MOZ_ASSERT(data_.state_ == JIT);
740
741 if (data_.jitFrames_.isIonScripted() && ionInlineFrames_.more()) {
742 ++ionInlineFrames_;
743 data_.pc_ = ionInlineFrames_.pc();
744 return;
745 }
746
747 ++data_.jitFrames_;
748 while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted())
749 ++data_.jitFrames_;
750
751 if (!data_.jitFrames_.done()) {
752 nextJitFrame();
753 return;
754 }
755
756 popActivation();
757 }
758
759 void
popAsmJSFrame()760 FrameIter::popAsmJSFrame()
761 {
762 MOZ_ASSERT(data_.state_ == ASMJS);
763
764 ++data_.asmJSFrames_;
765 if (data_.asmJSFrames_.done())
766 popActivation();
767 }
768
769 FrameIter&
operator ++()770 FrameIter::operator++()
771 {
772 switch (data_.state_) {
773 case DONE:
774 MOZ_CRASH("Unexpected state");
775 case INTERP:
776 if (interpFrame()->isDebuggerEvalFrame() &&
777 interpFrame()->evalInFramePrev() &&
778 data_.debuggerEvalOption_ == FOLLOW_DEBUGGER_EVAL_PREV_LINK)
779 {
780 AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev();
781
782 // Eval-in-frame can cross contexts and works across saved frame
783 // chains.
784 ContextOption prevContextOption = data_.contextOption_;
785 SavedOption prevSavedOption = data_.savedOption_;
786 data_.contextOption_ = ALL_CONTEXTS;
787 data_.savedOption_ = GO_THROUGH_SAVED;
788
789 popInterpreterFrame();
790
791 while (!hasUsableAbstractFramePtr() || abstractFramePtr() != eifPrev) {
792 if (data_.state_ == JIT)
793 popJitFrame();
794 else
795 popInterpreterFrame();
796 }
797
798 data_.contextOption_ = prevContextOption;
799 data_.savedOption_ = prevSavedOption;
800 data_.cx_ = data_.activations_->cx();
801 break;
802 }
803 popInterpreterFrame();
804 break;
805 case JIT:
806 popJitFrame();
807 break;
808 case ASMJS:
809 popAsmJSFrame();
810 break;
811 }
812 return *this;
813 }
814
815 FrameIter::Data*
copyData() const816 FrameIter::copyData() const
817 {
818 Data* data = data_.cx_->new_<Data>(data_);
819 if (!data)
820 return nullptr;
821
822 MOZ_ASSERT(data_.state_ != ASMJS);
823 if (data && data_.jitFrames_.isIonScripted())
824 data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
825 // Give the copied Data the cx of the current activation, which may be
826 // different than the cx that the current FrameIter was constructed
827 // with. This ensures that when we instantiate another FrameIter with the
828 // copied data, its cx is still alive.
829 data->cx_ = activation()->cx();
830 return data;
831 }
832
833 AbstractFramePtr
copyDataAsAbstractFramePtr() const834 FrameIter::copyDataAsAbstractFramePtr() const
835 {
836 AbstractFramePtr frame;
837 if (Data* data = copyData())
838 frame.ptr_ = uintptr_t(data);
839 return frame;
840 }
841
842 void*
rawFramePtr() const843 FrameIter::rawFramePtr() const
844 {
845 switch (data_.state_) {
846 case DONE:
847 case ASMJS:
848 return nullptr;
849 case JIT:
850 return data_.jitFrames_.fp();
851 case INTERP:
852 return interpFrame();
853 }
854 MOZ_CRASH("Unexpected state");
855 }
856
857 JSCompartment*
compartment() const858 FrameIter::compartment() const
859 {
860 switch (data_.state_) {
861 case DONE:
862 break;
863 case INTERP:
864 case JIT:
865 case ASMJS:
866 return data_.activations_->compartment();
867 }
868 MOZ_CRASH("Unexpected state");
869 }
870
871 bool
isFunctionFrame() const872 FrameIter::isFunctionFrame() const
873 {
874 switch (data_.state_) {
875 case DONE:
876 break;
877 case INTERP:
878 return interpFrame()->isFunctionFrame();
879 case JIT:
880 MOZ_ASSERT(data_.jitFrames_.isScripted());
881 if (data_.jitFrames_.isBaselineJS())
882 return data_.jitFrames_.isFunctionFrame();
883 return ionInlineFrames_.isFunctionFrame();
884 case ASMJS:
885 return true;
886 }
887 MOZ_CRASH("Unexpected state");
888 }
889
890 bool
isGlobalFrame() const891 FrameIter::isGlobalFrame() const
892 {
893 switch (data_.state_) {
894 case DONE:
895 break;
896 case INTERP:
897 return interpFrame()->isGlobalFrame();
898 case JIT:
899 if (data_.jitFrames_.isBaselineJS())
900 return data_.jitFrames_.baselineFrame()->isGlobalFrame();
901 MOZ_ASSERT(!script()->isForEval());
902 return !script()->functionNonDelazifying();
903 case ASMJS:
904 return false;
905 }
906 MOZ_CRASH("Unexpected state");
907 }
908
909 bool
isEvalFrame() const910 FrameIter::isEvalFrame() const
911 {
912 switch (data_.state_) {
913 case DONE:
914 break;
915 case INTERP:
916 return interpFrame()->isEvalFrame();
917 case JIT:
918 if (data_.jitFrames_.isBaselineJS())
919 return data_.jitFrames_.baselineFrame()->isEvalFrame();
920 MOZ_ASSERT(!script()->isForEval());
921 return false;
922 case ASMJS:
923 return false;
924 }
925 MOZ_CRASH("Unexpected state");
926 }
927
928 bool
isNonEvalFunctionFrame() const929 FrameIter::isNonEvalFunctionFrame() const
930 {
931 MOZ_ASSERT(!done());
932 switch (data_.state_) {
933 case DONE:
934 break;
935 case INTERP:
936 return interpFrame()->isNonEvalFunctionFrame();
937 case JIT:
938 return !isEvalFrame() && isFunctionFrame();
939 case ASMJS:
940 return true;
941 }
942 MOZ_CRASH("Unexpected state");
943 }
944
945 JSAtom*
functionDisplayAtom() const946 FrameIter::functionDisplayAtom() const
947 {
948 MOZ_ASSERT(isNonEvalFunctionFrame());
949
950 switch (data_.state_) {
951 case DONE:
952 break;
953 case INTERP:
954 case JIT:
955 return calleeTemplate()->displayAtom();
956 case ASMJS:
957 return data_.asmJSFrames_.functionDisplayAtom();
958 }
959
960 MOZ_CRASH("Unexpected state");
961 }
962
963 ScriptSource*
scriptSource() const964 FrameIter::scriptSource() const
965 {
966 switch (data_.state_) {
967 case DONE:
968 break;
969 case INTERP:
970 case JIT:
971 return script()->scriptSource();
972 case ASMJS:
973 return data_.activations_->asAsmJS()->module().scriptSource();
974 }
975
976 MOZ_CRASH("Unexpected state");
977 }
978
979 const char*
scriptFilename() const980 FrameIter::scriptFilename() const
981 {
982 switch (data_.state_) {
983 case DONE:
984 break;
985 case INTERP:
986 case JIT:
987 return script()->filename();
988 case ASMJS:
989 return data_.activations_->asAsmJS()->module().scriptSource()->filename();
990 }
991
992 MOZ_CRASH("Unexpected state");
993 }
994
995 const char16_t*
scriptDisplayURL() const996 FrameIter::scriptDisplayURL() const
997 {
998 ScriptSource* ss = scriptSource();
999 return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
1000 }
1001
1002 unsigned
computeLine(uint32_t * column) const1003 FrameIter::computeLine(uint32_t* column) const
1004 {
1005 switch (data_.state_) {
1006 case DONE:
1007 break;
1008 case INTERP:
1009 case JIT:
1010 return PCToLineNumber(script(), pc(), column);
1011 case ASMJS:
1012 return data_.asmJSFrames_.computeLine(column);
1013 }
1014
1015 MOZ_CRASH("Unexpected state");
1016 }
1017
1018 bool
mutedErrors() const1019 FrameIter::mutedErrors() const
1020 {
1021 switch (data_.state_) {
1022 case DONE:
1023 break;
1024 case INTERP:
1025 case JIT:
1026 return script()->mutedErrors();
1027 case ASMJS:
1028 return data_.activations_->asAsmJS()->module().scriptSource()->mutedErrors();
1029 }
1030
1031 MOZ_CRASH("Unexpected state");
1032 }
1033
1034 bool
isConstructing() const1035 FrameIter::isConstructing() const
1036 {
1037 switch (data_.state_) {
1038 case DONE:
1039 case ASMJS:
1040 break;
1041 case JIT:
1042 if (data_.jitFrames_.isIonScripted())
1043 return ionInlineFrames_.isConstructing();
1044 MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
1045 return data_.jitFrames_.isConstructing();
1046 case INTERP:
1047 return interpFrame()->isConstructing();
1048 }
1049
1050 MOZ_CRASH("Unexpected state");
1051 }
1052
1053 bool
ensureHasRematerializedFrame(JSContext * cx)1054 FrameIter::ensureHasRematerializedFrame(JSContext* cx)
1055 {
1056 MOZ_ASSERT(isIon());
1057 return !!activation()->asJit()->getRematerializedFrame(cx, data_.jitFrames_);
1058 }
1059
1060 bool
hasUsableAbstractFramePtr() const1061 FrameIter::hasUsableAbstractFramePtr() const
1062 {
1063 switch (data_.state_) {
1064 case DONE:
1065 case ASMJS:
1066 return false;
1067 case JIT:
1068 if (data_.jitFrames_.isBaselineJS())
1069 return true;
1070
1071 MOZ_ASSERT(data_.jitFrames_.isIonScripted());
1072 return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
1073 ionInlineFrames_.frameNo());
1074 break;
1075 case INTERP:
1076 return true;
1077 }
1078 MOZ_CRASH("Unexpected state");
1079 }
1080
1081 AbstractFramePtr
abstractFramePtr() const1082 FrameIter::abstractFramePtr() const
1083 {
1084 MOZ_ASSERT(hasUsableAbstractFramePtr());
1085 switch (data_.state_) {
1086 case DONE:
1087 case ASMJS:
1088 break;
1089 case JIT: {
1090 if (data_.jitFrames_.isBaselineJS())
1091 return data_.jitFrames_.baselineFrame();
1092
1093 MOZ_ASSERT(data_.jitFrames_.isIonScripted());
1094 return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
1095 ionInlineFrames_.frameNo());
1096 break;
1097 }
1098 case INTERP:
1099 MOZ_ASSERT(interpFrame());
1100 return AbstractFramePtr(interpFrame());
1101 }
1102 MOZ_CRASH("Unexpected state");
1103 }
1104
1105 void
updatePcQuadratic()1106 FrameIter::updatePcQuadratic()
1107 {
1108 switch (data_.state_) {
1109 case DONE:
1110 case ASMJS:
1111 break;
1112 case INTERP: {
1113 InterpreterFrame* frame = interpFrame();
1114 InterpreterActivation* activation = data_.activations_->asInterpreter();
1115
1116 // Look for the current frame.
1117 data_.interpFrames_ = InterpreterFrameIterator(activation);
1118 while (data_.interpFrames_.frame() != frame)
1119 ++data_.interpFrames_;
1120
1121 // Update the pc.
1122 MOZ_ASSERT(data_.interpFrames_.frame() == frame);
1123 data_.pc_ = data_.interpFrames_.pc();
1124 return;
1125 }
1126 case JIT:
1127 if (data_.jitFrames_.isBaselineJS()) {
1128 jit::BaselineFrame* frame = data_.jitFrames_.baselineFrame();
1129 jit::JitActivation* activation = data_.activations_->asJit();
1130
1131 // ActivationIterator::jitTop_ may be invalid, so create a new
1132 // activation iterator.
1133 data_.activations_ = ActivationIterator(data_.cx_->runtime());
1134 while (data_.activations_.activation() != activation)
1135 ++data_.activations_;
1136
1137 // Look for the current frame.
1138 data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
1139 while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame)
1140 ++data_.jitFrames_;
1141
1142 // Update the pc.
1143 MOZ_ASSERT(data_.jitFrames_.baselineFrame() == frame);
1144 data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
1145 return;
1146 }
1147 break;
1148 }
1149 MOZ_CRASH("Unexpected state");
1150 }
1151
1152 JSFunction*
calleeTemplate() const1153 FrameIter::calleeTemplate() const
1154 {
1155 switch (data_.state_) {
1156 case DONE:
1157 case ASMJS:
1158 break;
1159 case INTERP:
1160 MOZ_ASSERT(isFunctionFrame());
1161 return &interpFrame()->callee();
1162 case JIT:
1163 if (data_.jitFrames_.isBaselineJS())
1164 return data_.jitFrames_.callee();
1165 MOZ_ASSERT(data_.jitFrames_.isIonScripted());
1166 return ionInlineFrames_.calleeTemplate();
1167 }
1168 MOZ_CRASH("Unexpected state");
1169 }
1170
1171 JSFunction*
callee(JSContext * cx) const1172 FrameIter::callee(JSContext* cx) const
1173 {
1174 switch (data_.state_) {
1175 case DONE:
1176 case ASMJS:
1177 break;
1178 case INTERP:
1179 return calleeTemplate();
1180 case JIT:
1181 if (data_.jitFrames_.isIonScripted()) {
1182 jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
1183 return ionInlineFrames_.callee(recover);
1184 }
1185 MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
1186 return calleeTemplate();
1187 }
1188 MOZ_CRASH("Unexpected state");
1189 }
1190
1191 bool
matchCallee(JSContext * cx,HandleFunction fun) const1192 FrameIter::matchCallee(JSContext* cx, HandleFunction fun) const
1193 {
1194 RootedFunction currentCallee(cx, calleeTemplate());
1195
1196 // As we do not know if the calleeTemplate is the real function, or the
1197 // template from which it would be cloned, we compare properties which are
1198 // stable across the cloning of JSFunctions.
1199 if (((currentCallee->flags() ^ fun->flags()) & JSFunction::STABLE_ACROSS_CLONES) != 0 ||
1200 currentCallee->nargs() != fun->nargs())
1201 {
1202 return false;
1203 }
1204
1205 // Use the same condition as |js::CloneFunctionObject|, to know if we should
1206 // expect both functions to have the same JSScript. If so, and if they are
1207 // different, then they cannot be equal.
1208 RootedObject global(cx, &fun->global());
1209 bool useSameScript = CanReuseScriptForClone(fun->compartment(), currentCallee, global);
1210 if (useSameScript &&
1211 (currentCallee->hasScript() != fun->hasScript() ||
1212 currentCallee->nonLazyScript() != fun->nonLazyScript()))
1213 {
1214 return false;
1215 }
1216
1217 // If none of the previous filters worked, then take the risk of
1218 // invalidating the frame to identify the JSFunction.
1219 return callee(cx) == fun;
1220 }
1221
1222 unsigned
numActualArgs() const1223 FrameIter::numActualArgs() const
1224 {
1225 switch (data_.state_) {
1226 case DONE:
1227 case ASMJS:
1228 break;
1229 case INTERP:
1230 MOZ_ASSERT(isFunctionFrame());
1231 return interpFrame()->numActualArgs();
1232 case JIT:
1233 if (data_.jitFrames_.isIonScripted())
1234 return ionInlineFrames_.numActualArgs();
1235
1236 MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
1237 return data_.jitFrames_.numActualArgs();
1238 }
1239 MOZ_CRASH("Unexpected state");
1240 }
1241
1242 unsigned
numFormalArgs() const1243 FrameIter::numFormalArgs() const
1244 {
1245 return script()->functionNonDelazifying()->nargs();
1246 }
1247
1248 Value
unaliasedActual(unsigned i,MaybeCheckAliasing checkAliasing) const1249 FrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
1250 {
1251 return abstractFramePtr().unaliasedActual(i, checkAliasing);
1252 }
1253
1254 JSObject*
scopeChain(JSContext * cx) const1255 FrameIter::scopeChain(JSContext* cx) const
1256 {
1257 switch (data_.state_) {
1258 case DONE:
1259 case ASMJS:
1260 break;
1261 case JIT:
1262 if (data_.jitFrames_.isIonScripted()) {
1263 jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
1264 return ionInlineFrames_.scopeChain(recover);
1265 }
1266 return data_.jitFrames_.baselineFrame()->scopeChain();
1267 case INTERP:
1268 return interpFrame()->scopeChain();
1269 }
1270 MOZ_CRASH("Unexpected state");
1271 }
1272
1273 CallObject&
callObj(JSContext * cx) const1274 FrameIter::callObj(JSContext* cx) const
1275 {
1276 MOZ_ASSERT(calleeTemplate()->needsCallObject());
1277
1278 JSObject* pobj = scopeChain(cx);
1279 while (!pobj->is<CallObject>())
1280 pobj = pobj->enclosingScope();
1281 return pobj->as<CallObject>();
1282 }
1283
1284 bool
hasArgsObj() const1285 FrameIter::hasArgsObj() const
1286 {
1287 return abstractFramePtr().hasArgsObj();
1288 }
1289
1290 ArgumentsObject&
argsObj() const1291 FrameIter::argsObj() const
1292 {
1293 MOZ_ASSERT(hasArgsObj());
1294 return abstractFramePtr().argsObj();
1295 }
1296
1297 Value
thisArgument(JSContext * cx) const1298 FrameIter::thisArgument(JSContext* cx) const
1299 {
1300 MOZ_ASSERT(isNonEvalFunctionFrame());
1301
1302 switch (data_.state_) {
1303 case DONE:
1304 case ASMJS:
1305 break;
1306 case JIT:
1307 if (data_.jitFrames_.isIonScripted()) {
1308 jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
1309 return ionInlineFrames_.thisArgument(recover);
1310 }
1311 return data_.jitFrames_.baselineFrame()->thisArgument();
1312 case INTERP:
1313 return interpFrame()->thisArgument();
1314 }
1315 MOZ_CRASH("Unexpected state");
1316 }
1317
1318 Value
newTarget() const1319 FrameIter::newTarget() const
1320 {
1321 switch (data_.state_) {
1322 case DONE:
1323 case ASMJS:
1324 break;
1325 case INTERP:
1326 return interpFrame()->newTarget();
1327 case JIT:
1328 MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
1329 return data_.jitFrames_.baselineFrame()->newTarget();
1330 }
1331 MOZ_CRASH("Unexpected state");
1332 }
1333
1334 Value
returnValue() const1335 FrameIter::returnValue() const
1336 {
1337 switch (data_.state_) {
1338 case DONE:
1339 case ASMJS:
1340 break;
1341 case JIT:
1342 if (data_.jitFrames_.isBaselineJS())
1343 return data_.jitFrames_.baselineFrame()->returnValue();
1344 break;
1345 case INTERP:
1346 return interpFrame()->returnValue();
1347 }
1348 MOZ_CRASH("Unexpected state");
1349 }
1350
1351 void
setReturnValue(const Value & v)1352 FrameIter::setReturnValue(const Value& v)
1353 {
1354 switch (data_.state_) {
1355 case DONE:
1356 case ASMJS:
1357 break;
1358 case JIT:
1359 if (data_.jitFrames_.isBaselineJS()) {
1360 data_.jitFrames_.baselineFrame()->setReturnValue(v);
1361 return;
1362 }
1363 break;
1364 case INTERP:
1365 interpFrame()->setReturnValue(v);
1366 return;
1367 }
1368 MOZ_CRASH("Unexpected state");
1369 }
1370
1371 size_t
numFrameSlots() const1372 FrameIter::numFrameSlots() const
1373 {
1374 switch (data_.state_) {
1375 case DONE:
1376 case ASMJS:
1377 break;
1378 case JIT: {
1379 if (data_.jitFrames_.isIonScripted()) {
1380 return ionInlineFrames_.snapshotIterator().numAllocations() -
1381 ionInlineFrames_.script()->nfixed();
1382 }
1383 jit::BaselineFrame* frame = data_.jitFrames_.baselineFrame();
1384 return frame->numValueSlots() - data_.jitFrames_.script()->nfixed();
1385 }
1386 case INTERP:
1387 MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
1388 return data_.interpFrames_.sp() - interpFrame()->base();
1389 }
1390 MOZ_CRASH("Unexpected state");
1391 }
1392
1393 Value
frameSlotValue(size_t index) const1394 FrameIter::frameSlotValue(size_t index) const
1395 {
1396 switch (data_.state_) {
1397 case DONE:
1398 case ASMJS:
1399 break;
1400 case JIT:
1401 if (data_.jitFrames_.isIonScripted()) {
1402 jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
1403 index += ionInlineFrames_.script()->nfixed();
1404 return si.maybeReadAllocByIndex(index);
1405 }
1406
1407 index += data_.jitFrames_.script()->nfixed();
1408 return *data_.jitFrames_.baselineFrame()->valueSlot(index);
1409 case INTERP:
1410 return interpFrame()->base()[index];
1411 }
1412 MOZ_CRASH("Unexpected state");
1413 }
1414
1415 #ifdef DEBUG
1416 bool
SelfHostedFramesVisible()1417 js::SelfHostedFramesVisible()
1418 {
1419 static bool checked = false;
1420 static bool visible = false;
1421 if (!checked) {
1422 checked = true;
1423 char* env = getenv("MOZ_SHOW_ALL_JS_FRAMES");
1424 visible = !!env;
1425 }
1426 return visible;
1427 }
1428 #endif
1429
1430 void
settle()1431 NonBuiltinFrameIter::settle()
1432 {
1433 if (!SelfHostedFramesVisible()) {
1434 while (!done() && hasScript() && script()->selfHosted())
1435 FrameIter::operator++();
1436 }
1437 }
1438
1439 void
settle()1440 NonBuiltinScriptFrameIter::settle()
1441 {
1442 if (!SelfHostedFramesVisible()) {
1443 while (!done() && script()->selfHosted())
1444 ScriptFrameIter::operator++();
1445 }
1446 }
1447
ActivationEntryMonitor(JSContext * cx)1448 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx)
1449 : cx_(cx), entryMonitor_(cx->runtime()->entryMonitor)
1450 {
1451 cx->runtime()->entryMonitor = nullptr;
1452 }
1453
1454 Value
asyncStack(JSContext * cx)1455 ActivationEntryMonitor::asyncStack(JSContext* cx)
1456 {
1457 RootedValue stack(cx, ObjectOrNullValue(cx->runtime()->asyncStackForNewActivations));
1458 if (!cx->compartment()->wrap(cx, &stack)) {
1459 cx->clearPendingException();
1460 return UndefinedValue();
1461 }
1462 return stack;
1463 }
1464
ActivationEntryMonitor(JSContext * cx,InterpreterFrame * entryFrame)1465 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, InterpreterFrame* entryFrame)
1466 : ActivationEntryMonitor(cx)
1467 {
1468 if (entryMonitor_) {
1469 // The InterpreterFrame is not yet part of an Activation, so it won't
1470 // be traced if we trigger GC here. Suppress GC to avoid this.
1471 gc::AutoSuppressGC suppressGC(cx);
1472 RootedValue stack(cx, asyncStack(cx));
1473 RootedString asyncCause(cx, cx->runtime()->asyncCauseForNewActivations);
1474 if (entryFrame->isFunctionFrame())
1475 entryMonitor_->Entry(cx, entryFrame->fun(), stack, asyncCause);
1476 else
1477 entryMonitor_->Entry(cx, entryFrame->script(), stack, asyncCause);
1478 }
1479 }
1480
ActivationEntryMonitor(JSContext * cx,jit::CalleeToken entryToken)1481 ActivationEntryMonitor::ActivationEntryMonitor(JSContext* cx, jit::CalleeToken entryToken)
1482 : ActivationEntryMonitor(cx)
1483 {
1484 if (entryMonitor_) {
1485 // The CalleeToken is not traced at this point and we also don't want
1486 // a GC to discard the code we're about to enter, so we suppress GC.
1487 gc::AutoSuppressGC suppressGC(cx);
1488 RootedValue stack(cx, asyncStack(cx));
1489 RootedString asyncCause(cx, cx->runtime()->asyncCauseForNewActivations);
1490 if (jit::CalleeTokenIsFunction(entryToken))
1491 entryMonitor_->Entry(cx_, jit::CalleeTokenToFunction(entryToken), stack, asyncCause);
1492 else
1493 entryMonitor_->Entry(cx_, jit::CalleeTokenToScript(entryToken), stack, asyncCause);
1494 }
1495 }
1496
1497 /*****************************************************************************/
1498
JitActivation(JSContext * cx,bool active)1499 jit::JitActivation::JitActivation(JSContext* cx, bool active)
1500 : Activation(cx, Jit),
1501 active_(active),
1502 isLazyLinkExitFrame_(false),
1503 rematerializedFrames_(nullptr),
1504 ionRecovery_(cx),
1505 bailoutData_(nullptr),
1506 lastProfilingFrame_(nullptr),
1507 lastProfilingCallSite_(nullptr)
1508 {
1509 if (active) {
1510 prevJitTop_ = cx->runtime()->jitTop;
1511 prevJitJSContext_ = cx->runtime()->jitJSContext;
1512 prevJitActivation_ = cx->runtime()->jitActivation;
1513 cx->runtime()->jitJSContext = cx;
1514 cx->runtime()->jitActivation = this;
1515
1516 registerProfiling();
1517 } else {
1518 prevJitTop_ = nullptr;
1519 prevJitJSContext_ = nullptr;
1520 prevJitActivation_ = nullptr;
1521 }
1522 }
1523
~JitActivation()1524 jit::JitActivation::~JitActivation()
1525 {
1526 if (active_) {
1527 if (isProfiling())
1528 unregisterProfiling();
1529
1530 cx_->runtime()->jitTop = prevJitTop_;
1531 cx_->runtime()->jitJSContext = prevJitJSContext_;
1532 cx_->runtime()->jitActivation = prevJitActivation_;
1533 }
1534
1535 // All reocvered value are taken from activation during the bailout.
1536 MOZ_ASSERT(ionRecovery_.empty());
1537
1538 // The BailoutFrameInfo should have unregistered itself from the
1539 // JitActivations.
1540 MOZ_ASSERT(!bailoutData_);
1541
1542 clearRematerializedFrames();
1543 js_delete(rematerializedFrames_);
1544 }
1545
1546 bool
isProfiling() const1547 jit::JitActivation::isProfiling() const
1548 {
1549 // All JitActivations can be profiled.
1550 return true;
1551 }
1552
1553 void
setBailoutData(jit::BailoutFrameInfo * bailoutData)1554 jit::JitActivation::setBailoutData(jit::BailoutFrameInfo* bailoutData)
1555 {
1556 MOZ_ASSERT(!bailoutData_);
1557 bailoutData_ = bailoutData;
1558 }
1559
1560 void
cleanBailoutData()1561 jit::JitActivation::cleanBailoutData()
1562 {
1563 MOZ_ASSERT(bailoutData_);
1564 bailoutData_ = nullptr;
1565 }
1566
1567 // setActive() is inlined in GenerateFFIIonExit() with explicit masm instructions so
1568 // changes to the logic here need to be reflected in GenerateFFIIonExit() in the enable
1569 // and disable activation instruction sequences.
1570 void
setActive(JSContext * cx,bool active)1571 jit::JitActivation::setActive(JSContext* cx, bool active)
1572 {
1573 // Only allowed to deactivate/activate if activation is top.
1574 // (Not tested and will probably fail in other situations.)
1575 MOZ_ASSERT(cx->runtime()->activation_ == this);
1576 MOZ_ASSERT(active != active_);
1577
1578 if (active) {
1579 *((volatile bool*) active_) = true;
1580 prevJitTop_ = cx->runtime()->jitTop;
1581 prevJitJSContext_ = cx->runtime()->jitJSContext;
1582 prevJitActivation_ = cx->runtime()->jitActivation;
1583 cx->runtime()->jitJSContext = cx;
1584 cx->runtime()->jitActivation = this;
1585
1586 registerProfiling();
1587
1588 } else {
1589 unregisterProfiling();
1590
1591 cx->runtime()->jitTop = prevJitTop_;
1592 cx->runtime()->jitJSContext = prevJitJSContext_;
1593 cx->runtime()->jitActivation = prevJitActivation_;
1594
1595 *((volatile bool*) active_) = false;
1596 }
1597 }
1598
1599 void
removeRematerializedFrame(uint8_t * top)1600 jit::JitActivation::removeRematerializedFrame(uint8_t* top)
1601 {
1602 if (!rematerializedFrames_)
1603 return;
1604
1605 if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
1606 RematerializedFrame::FreeInVector(p->value());
1607 rematerializedFrames_->remove(p);
1608 }
1609 }
1610
1611 void
clearRematerializedFrames()1612 jit::JitActivation::clearRematerializedFrames()
1613 {
1614 if (!rematerializedFrames_)
1615 return;
1616
1617 for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront()) {
1618 RematerializedFrame::FreeInVector(e.front().value());
1619 e.removeFront();
1620 }
1621 }
1622
1623 jit::RematerializedFrame*
getRematerializedFrame(JSContext * cx,const JitFrameIterator & iter,size_t inlineDepth)1624 jit::JitActivation::getRematerializedFrame(JSContext* cx, const JitFrameIterator& iter, size_t inlineDepth)
1625 {
1626 MOZ_ASSERT(iter.activation() == this);
1627 MOZ_ASSERT(iter.isIonScripted());
1628
1629 if (!rematerializedFrames_) {
1630 rematerializedFrames_ = cx->new_<RematerializedFrameTable>(cx);
1631 if (!rematerializedFrames_)
1632 return nullptr;
1633 if (!rematerializedFrames_->init()) {
1634 rematerializedFrames_ = nullptr;
1635 ReportOutOfMemory(cx);
1636 return nullptr;
1637 }
1638 }
1639
1640 uint8_t* top = iter.fp();
1641 RematerializedFrameTable::AddPtr p = rematerializedFrames_->lookupForAdd(top);
1642 if (!p) {
1643 RematerializedFrameVector empty(cx);
1644 if (!rematerializedFrames_->add(p, top, Move(empty))) {
1645 ReportOutOfMemory(cx);
1646 return nullptr;
1647 }
1648
1649 // The unit of rematerialization is an uninlined frame and its inlined
1650 // frames. Since inlined frames do not exist outside of snapshots, it
1651 // is impossible to synchronize their rematerialized copies to
1652 // preserve identity. Therefore, we always rematerialize an uninlined
1653 // frame and all its inlined frames at once.
1654 InlineFrameIterator inlineIter(cx, &iter);
1655 MaybeReadFallback recover(cx, this, &iter);
1656
1657 // Frames are often rematerialized with the cx inside a Debugger's
1658 // compartment. To recover slots and to create CallObjects, we need to
1659 // be in the activation's compartment.
1660 AutoCompartment ac(cx, compartment_);
1661
1662 if (!RematerializedFrame::RematerializeInlineFrames(cx, top, inlineIter, recover,
1663 p->value()))
1664 {
1665 return nullptr;
1666 }
1667
1668 // See comment in unsetPrevUpToDateUntil.
1669 DebugScopes::unsetPrevUpToDateUntil(cx, p->value()[inlineDepth]);
1670 }
1671
1672 return p->value()[inlineDepth];
1673 }
1674
1675 jit::RematerializedFrame*
lookupRematerializedFrame(uint8_t * top,size_t inlineDepth)1676 jit::JitActivation::lookupRematerializedFrame(uint8_t* top, size_t inlineDepth)
1677 {
1678 if (!rematerializedFrames_)
1679 return nullptr;
1680 if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top))
1681 return inlineDepth < p->value().length() ? p->value()[inlineDepth] : nullptr;
1682 return nullptr;
1683 }
1684
1685 void
removeRematerializedFramesFromDebugger(JSContext * cx,uint8_t * top)1686 jit::JitActivation::removeRematerializedFramesFromDebugger(JSContext* cx, uint8_t* top)
1687 {
1688 // Ion bailout can fail due to overrecursion and OOM. In such cases we
1689 // cannot honor any further Debugger hooks on the frame, and need to
1690 // ensure that its Debugger.Frame entry is cleaned up.
1691 if (!cx->compartment()->isDebuggee() || !rematerializedFrames_)
1692 return;
1693 if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
1694 for (uint32_t i = 0; i < p->value().length(); i++)
1695 Debugger::handleUnrecoverableIonBailoutError(cx, p->value()[i]);
1696 }
1697 }
1698
1699 void
markRematerializedFrames(JSTracer * trc)1700 jit::JitActivation::markRematerializedFrames(JSTracer* trc)
1701 {
1702 if (!rematerializedFrames_)
1703 return;
1704 for (RematerializedFrameTable::Enum e(*rematerializedFrames_); !e.empty(); e.popFront())
1705 RematerializedFrame::MarkInVector(trc, e.front().value());
1706 }
1707
1708 bool
registerIonFrameRecovery(RInstructionResults && results)1709 jit::JitActivation::registerIonFrameRecovery(RInstructionResults&& results)
1710 {
1711 // Check that there is no entry in the vector yet.
1712 MOZ_ASSERT(!maybeIonFrameRecovery(results.frame()));
1713 if (!ionRecovery_.append(mozilla::Move(results)))
1714 return false;
1715
1716 return true;
1717 }
1718
1719 jit::RInstructionResults*
maybeIonFrameRecovery(JitFrameLayout * fp)1720 jit::JitActivation::maybeIonFrameRecovery(JitFrameLayout* fp)
1721 {
1722 for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); ) {
1723 if (it->frame() == fp)
1724 return it;
1725 }
1726
1727 return nullptr;
1728 }
1729
1730 void
removeIonFrameRecovery(JitFrameLayout * fp)1731 jit::JitActivation::removeIonFrameRecovery(JitFrameLayout* fp)
1732 {
1733 RInstructionResults* elem = maybeIonFrameRecovery(fp);
1734 if (!elem)
1735 return;
1736
1737 ionRecovery_.erase(elem);
1738 }
1739
1740 void
markIonRecovery(JSTracer * trc)1741 jit::JitActivation::markIonRecovery(JSTracer* trc)
1742 {
1743 for (RInstructionResults* it = ionRecovery_.begin(); it != ionRecovery_.end(); it++)
1744 it->trace(trc);
1745 }
1746
AsmJSActivation(JSContext * cx,AsmJSModule & module)1747 AsmJSActivation::AsmJSActivation(JSContext* cx, AsmJSModule& module)
1748 : Activation(cx, AsmJS),
1749 module_(module),
1750 entrySP_(nullptr),
1751 resumePC_(nullptr),
1752 fp_(nullptr),
1753 packedExitReason_(wasm::ExitReason(wasm::ExitReason::None).pack())
1754 {
1755 (void) entrySP_; // squelch GCC warning
1756
1757 prevAsmJSForModule_ = module.activation();
1758 module.activation() = this;
1759
1760 prevAsmJS_ = cx->runtime()->asmJSActivationStack_;
1761 cx->runtime()->asmJSActivationStack_ = this;
1762
1763 // Now that the AsmJSActivation is fully initialized, make it visible to
1764 // asynchronous profiling.
1765 registerProfiling();
1766 }
1767
~AsmJSActivation()1768 AsmJSActivation::~AsmJSActivation()
1769 {
1770 // Hide this activation from the profiler before is is destroyed.
1771 unregisterProfiling();
1772
1773 MOZ_ASSERT(fp_ == nullptr);
1774
1775 MOZ_ASSERT(module_.activation() == this);
1776 module_.activation() = prevAsmJSForModule_;
1777
1778 JSContext* cx = cx_->asJSContext();
1779 MOZ_ASSERT(cx->runtime()->asmJSActivationStack_ == this);
1780
1781 cx->runtime()->asmJSActivationStack_ = prevAsmJS_;
1782 }
1783
1784 InterpreterFrameIterator&
operator ++()1785 InterpreterFrameIterator::operator++()
1786 {
1787 MOZ_ASSERT(!done());
1788 if (fp_ != activation_->entryFrame_) {
1789 pc_ = fp_->prevpc();
1790 sp_ = fp_->prevsp();
1791 fp_ = fp_->prev();
1792 } else {
1793 pc_ = nullptr;
1794 sp_ = nullptr;
1795 fp_ = nullptr;
1796 }
1797 return *this;
1798 }
1799
1800 void
registerProfiling()1801 Activation::registerProfiling()
1802 {
1803 MOZ_ASSERT(isProfiling());
1804 cx_->runtime()->profilingActivation_ = this;
1805 }
1806
1807 void
unregisterProfiling()1808 Activation::unregisterProfiling()
1809 {
1810 MOZ_ASSERT(isProfiling());
1811 MOZ_ASSERT(cx_->runtime()->profilingActivation_ == this);
1812
1813 // There may be a non-active jit activation in the linked list. Skip past it.
1814 Activation* prevProfiling = prevProfiling_;
1815 while (prevProfiling && prevProfiling->isJit() && !prevProfiling->asJit()->isActive())
1816 prevProfiling = prevProfiling->prevProfiling_;
1817
1818 cx_->runtime()->profilingActivation_ = prevProfiling;
1819 }
1820
ActivationIterator(JSRuntime * rt)1821 ActivationIterator::ActivationIterator(JSRuntime* rt)
1822 : jitTop_(rt->jitTop),
1823 activation_(rt->activation_)
1824 {
1825 settle();
1826 }
1827
1828 ActivationIterator&
operator ++()1829 ActivationIterator::operator++()
1830 {
1831 MOZ_ASSERT(activation_);
1832 if (activation_->isJit() && activation_->asJit()->isActive())
1833 jitTop_ = activation_->asJit()->prevJitTop();
1834 activation_ = activation_->prev();
1835 settle();
1836 return *this;
1837 }
1838
1839 void
settle()1840 ActivationIterator::settle()
1841 {
1842 // Stop at the next active activation. No need to update jitTop_, since
1843 // we don't iterate over an active jit activation.
1844 while (!done() && activation_->isJit() && !activation_->asJit()->isActive())
1845 activation_ = activation_->prev();
1846 }
1847
ProfilingFrameIterator(JSRuntime * rt,const RegisterState & state,uint32_t sampleBufferGen)1848 JS::ProfilingFrameIterator::ProfilingFrameIterator(JSRuntime* rt, const RegisterState& state,
1849 uint32_t sampleBufferGen)
1850 : rt_(rt),
1851 sampleBufferGen_(sampleBufferGen),
1852 activation_(nullptr),
1853 savedPrevJitTop_(nullptr)
1854 {
1855 if (!rt->spsProfiler.enabled())
1856 MOZ_CRASH("ProfilingFrameIterator called when spsProfiler not enabled for runtime.");
1857
1858 if (!rt->profilingActivation())
1859 return;
1860
1861 // If profiler sampling is not enabled, skip.
1862 if (!rt_->isProfilerSamplingEnabled())
1863 return;
1864
1865 activation_ = rt->profilingActivation();
1866
1867 MOZ_ASSERT(activation_->isProfiling());
1868
1869 static_assert(sizeof(AsmJSProfilingFrameIterator) <= StorageSpace &&
1870 sizeof(jit::JitProfilingFrameIterator) <= StorageSpace,
1871 "Need to increase storage");
1872
1873 iteratorConstruct(state);
1874 settle();
1875 }
1876
~ProfilingFrameIterator()1877 JS::ProfilingFrameIterator::~ProfilingFrameIterator()
1878 {
1879 if (!done()) {
1880 MOZ_ASSERT(activation_->isProfiling());
1881 iteratorDestroy();
1882 }
1883 }
1884
1885 void
operator ++()1886 JS::ProfilingFrameIterator::operator++()
1887 {
1888 MOZ_ASSERT(!done());
1889 MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit());
1890
1891 if (activation_->isAsmJS()) {
1892 ++asmJSIter();
1893 settle();
1894 return;
1895 }
1896
1897 ++jitIter();
1898 settle();
1899 }
1900
1901 void
settle()1902 JS::ProfilingFrameIterator::settle()
1903 {
1904 while (iteratorDone()) {
1905 iteratorDestroy();
1906 activation_ = activation_->prevProfiling();
1907
1908 // Skip past any non-active jit activations in the list.
1909 while (activation_ && activation_->isJit() && !activation_->asJit()->isActive())
1910 activation_ = activation_->prevProfiling();
1911
1912 if (!activation_)
1913 return;
1914 iteratorConstruct();
1915 }
1916 }
1917
1918 void
iteratorConstruct(const RegisterState & state)1919 JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
1920 {
1921 MOZ_ASSERT(!done());
1922 MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit());
1923
1924 if (activation_->isAsmJS()) {
1925 new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS(), state);
1926 // Set savedPrevJitTop_ to the actual jitTop_ from the runtime.
1927 savedPrevJitTop_ = activation_->cx()->runtime()->jitTop;
1928 return;
1929 }
1930
1931 MOZ_ASSERT(activation_->asJit()->isActive());
1932 new (storage_.addr()) jit::JitProfilingFrameIterator(rt_, state);
1933 }
1934
1935 void
iteratorConstruct()1936 JS::ProfilingFrameIterator::iteratorConstruct()
1937 {
1938 MOZ_ASSERT(!done());
1939 MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit());
1940
1941 if (activation_->isAsmJS()) {
1942 new (storage_.addr()) AsmJSProfilingFrameIterator(*activation_->asAsmJS());
1943 return;
1944 }
1945
1946 MOZ_ASSERT(activation_->asJit()->isActive());
1947 MOZ_ASSERT(savedPrevJitTop_ != nullptr);
1948 new (storage_.addr()) jit::JitProfilingFrameIterator(savedPrevJitTop_);
1949 }
1950
1951 void
iteratorDestroy()1952 JS::ProfilingFrameIterator::iteratorDestroy()
1953 {
1954 MOZ_ASSERT(!done());
1955 MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit());
1956
1957 if (activation_->isAsmJS()) {
1958 asmJSIter().~AsmJSProfilingFrameIterator();
1959 return;
1960 }
1961
1962 // Save prevjitTop for later use
1963 savedPrevJitTop_ = activation_->asJit()->prevJitTop();
1964 jitIter().~JitProfilingFrameIterator();
1965 }
1966
1967 bool
iteratorDone()1968 JS::ProfilingFrameIterator::iteratorDone()
1969 {
1970 MOZ_ASSERT(!done());
1971 MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit());
1972
1973 if (activation_->isAsmJS())
1974 return asmJSIter().done();
1975
1976 return jitIter().done();
1977 }
1978
1979 void*
stackAddress() const1980 JS::ProfilingFrameIterator::stackAddress() const
1981 {
1982 MOZ_ASSERT(!done());
1983 MOZ_ASSERT(activation_->isAsmJS() || activation_->isJit());
1984
1985 if (activation_->isAsmJS())
1986 return asmJSIter().stackAddress();
1987
1988 return jitIter().stackAddress();
1989 }
1990
1991 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry * entry) const1992 JS::ProfilingFrameIterator::getPhysicalFrameAndEntry(jit::JitcodeGlobalEntry* entry) const
1993 {
1994 void* stackAddr = stackAddress();
1995
1996 if (isAsmJS()) {
1997 Frame frame;
1998 frame.kind = Frame_AsmJS;
1999 frame.stackAddress = stackAddr;
2000 frame.returnAddress = nullptr;
2001 frame.activation = activation_;
2002 frame.label = nullptr;
2003 return mozilla::Some(frame);
2004 }
2005
2006 MOZ_ASSERT(isJit());
2007
2008 // Look up an entry for the return address.
2009 void* returnAddr = jitIter().returnAddressToFp();
2010 jit::JitcodeGlobalTable* table = rt_->jitRuntime()->getJitcodeGlobalTable();
2011 if (hasSampleBufferGen())
2012 table->lookupForSampler(returnAddr, entry, rt_, sampleBufferGen_);
2013 else
2014 table->lookupInfallible(returnAddr, entry, rt_);
2015
2016 MOZ_ASSERT(entry->isIon() || entry->isIonCache() || entry->isBaseline() || entry->isDummy());
2017
2018 // Dummy frames produce no stack frames.
2019 if (entry->isDummy())
2020 return mozilla::Nothing();
2021
2022 Frame frame;
2023 frame.kind = entry->isBaseline() ? Frame_Baseline : Frame_Ion;
2024 frame.stackAddress = stackAddr;
2025 frame.returnAddress = returnAddr;
2026 frame.activation = activation_;
2027 frame.label = nullptr;
2028 return mozilla::Some(frame);
2029 }
2030
2031 uint32_t
extractStack(Frame * frames,uint32_t offset,uint32_t end) const2032 JS::ProfilingFrameIterator::extractStack(Frame* frames, uint32_t offset, uint32_t end) const
2033 {
2034 if (offset >= end)
2035 return 0;
2036
2037 jit::JitcodeGlobalEntry entry;
2038 Maybe<Frame> physicalFrame = getPhysicalFrameAndEntry(&entry);
2039
2040 // Dummy frames produce no stack frames.
2041 if (physicalFrame.isNothing())
2042 return 0;
2043
2044 if (isAsmJS()) {
2045 frames[offset] = physicalFrame.value();
2046 frames[offset].label = asmJSIter().label();
2047 return 1;
2048 }
2049
2050 // Extract the stack for the entry. Assume maximum inlining depth is <64
2051 const char* labels[64];
2052 uint32_t depth = entry.callStackAtAddr(rt_, jitIter().returnAddressToFp(), labels, 64);
2053 MOZ_ASSERT(depth < 64);
2054 for (uint32_t i = 0; i < depth; i++) {
2055 if (offset + i >= end)
2056 return i;
2057 frames[offset + i] = physicalFrame.value();
2058 frames[offset + i].label = labels[i];
2059 }
2060
2061 return depth;
2062 }
2063
2064 Maybe<JS::ProfilingFrameIterator::Frame>
getPhysicalFrameWithoutLabel() const2065 JS::ProfilingFrameIterator::getPhysicalFrameWithoutLabel() const
2066 {
2067 jit::JitcodeGlobalEntry unused;
2068 return getPhysicalFrameAndEntry(&unused);
2069 }
2070
2071 bool
isAsmJS() const2072 JS::ProfilingFrameIterator::isAsmJS() const
2073 {
2074 MOZ_ASSERT(!done());
2075 return activation_->isAsmJS();
2076 }
2077
2078 bool
isJit() const2079 JS::ProfilingFrameIterator::isJit() const
2080 {
2081 return activation_->isJit();
2082 }
2083