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