|
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 "gc/Marking.h" |
|
14 #ifdef JS_ION |
|
15 #include "jit/AsmJSModule.h" |
|
16 #include "jit/BaselineFrame.h" |
|
17 #include "jit/JitCompartment.h" |
|
18 #endif |
|
19 #include "js/GCAPI.h" |
|
20 #include "vm/Opcodes.h" |
|
21 |
|
22 #include "jit/JitFrameIterator-inl.h" |
|
23 #include "vm/Interpreter-inl.h" |
|
24 #include "vm/Probes-inl.h" |
|
25 #include "vm/ScopeObject-inl.h" |
|
26 |
|
27 using namespace js; |
|
28 |
|
29 using mozilla::PodCopy; |
|
30 |
|
31 /*****************************************************************************/ |
|
32 |
|
33 void |
|
34 InterpreterFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr evalInFramePrev, |
|
35 const Value &thisv, JSObject &scopeChain, ExecuteType type) |
|
36 { |
|
37 /* |
|
38 * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a |
|
39 * script in the context of another frame and the frame type is determined |
|
40 * by the context. |
|
41 */ |
|
42 flags_ = type | HAS_SCOPECHAIN; |
|
43 |
|
44 JSObject *callee = nullptr; |
|
45 if (!(flags_ & (GLOBAL))) { |
|
46 if (evalInFramePrev) { |
|
47 JS_ASSERT(evalInFramePrev.isFunctionFrame() || evalInFramePrev.isGlobalFrame()); |
|
48 if (evalInFramePrev.isFunctionFrame()) { |
|
49 callee = evalInFramePrev.callee(); |
|
50 flags_ |= FUNCTION; |
|
51 } else { |
|
52 flags_ |= GLOBAL; |
|
53 } |
|
54 } else { |
|
55 FrameIter iter(cx); |
|
56 JS_ASSERT(iter.isFunctionFrame() || iter.isGlobalFrame()); |
|
57 JS_ASSERT(!iter.isAsmJS()); |
|
58 if (iter.isFunctionFrame()) { |
|
59 callee = iter.callee(); |
|
60 flags_ |= FUNCTION; |
|
61 } else { |
|
62 flags_ |= GLOBAL; |
|
63 } |
|
64 } |
|
65 } |
|
66 |
|
67 Value *dstvp = (Value *)this - 2; |
|
68 dstvp[1] = thisv; |
|
69 |
|
70 if (isFunctionFrame()) { |
|
71 dstvp[0] = ObjectValue(*callee); |
|
72 exec.fun = &callee->as<JSFunction>(); |
|
73 u.evalScript = script; |
|
74 } else { |
|
75 JS_ASSERT(isGlobalFrame()); |
|
76 dstvp[0] = NullValue(); |
|
77 exec.script = script; |
|
78 #ifdef DEBUG |
|
79 u.evalScript = (JSScript *)0xbad; |
|
80 #endif |
|
81 } |
|
82 |
|
83 scopeChain_ = &scopeChain; |
|
84 prev_ = nullptr; |
|
85 prevpc_ = nullptr; |
|
86 prevsp_ = nullptr; |
|
87 |
|
88 JS_ASSERT_IF(evalInFramePrev, isDebuggerFrame()); |
|
89 evalInFramePrev_ = evalInFramePrev; |
|
90 |
|
91 #ifdef DEBUG |
|
92 Debug_SetValueRangeToCrashOnTouch(&rval_, 1); |
|
93 hookData_ = (void *)0xbad; |
|
94 #endif |
|
95 } |
|
96 |
|
97 template <InterpreterFrame::TriggerPostBarriers doPostBarrier> |
|
98 void |
|
99 InterpreterFrame::copyFrameAndValues(JSContext *cx, Value *vp, InterpreterFrame *otherfp, |
|
100 const Value *othervp, Value *othersp) |
|
101 { |
|
102 JS_ASSERT(othervp == otherfp->generatorArgsSnapshotBegin()); |
|
103 JS_ASSERT(othersp >= otherfp->slots()); |
|
104 JS_ASSERT(othersp <= otherfp->generatorSlotsSnapshotBegin() + otherfp->script()->nslots()); |
|
105 |
|
106 /* Copy args, InterpreterFrame, and slots. */ |
|
107 const Value *srcend = otherfp->generatorArgsSnapshotEnd(); |
|
108 Value *dst = vp; |
|
109 for (const Value *src = othervp; src < srcend; src++, dst++) { |
|
110 *dst = *src; |
|
111 if (doPostBarrier) |
|
112 HeapValue::writeBarrierPost(*dst, dst); |
|
113 } |
|
114 |
|
115 *this = *otherfp; |
|
116 argv_ = vp + 2; |
|
117 unsetPushedSPSFrame(); |
|
118 if (doPostBarrier) |
|
119 writeBarrierPost(); |
|
120 |
|
121 srcend = othersp; |
|
122 dst = slots(); |
|
123 for (const Value *src = otherfp->slots(); src < srcend; src++, dst++) { |
|
124 *dst = *src; |
|
125 if (doPostBarrier) |
|
126 HeapValue::writeBarrierPost(*dst, dst); |
|
127 } |
|
128 } |
|
129 |
|
130 /* Note: explicit instantiation for js_NewGenerator located in jsiter.cpp. */ |
|
131 template |
|
132 void InterpreterFrame::copyFrameAndValues<InterpreterFrame::NoPostBarrier>( |
|
133 JSContext *, Value *, InterpreterFrame *, const Value *, Value *); |
|
134 template |
|
135 void InterpreterFrame::copyFrameAndValues<InterpreterFrame::DoPostBarrier>( |
|
136 JSContext *, Value *, InterpreterFrame *, const Value *, Value *); |
|
137 |
|
138 void |
|
139 InterpreterFrame::writeBarrierPost() |
|
140 { |
|
141 /* This needs to follow the same rules as in InterpreterFrame::mark. */ |
|
142 if (scopeChain_) |
|
143 JSObject::writeBarrierPost(scopeChain_, (void *)&scopeChain_); |
|
144 if (flags_ & HAS_ARGS_OBJ) |
|
145 JSObject::writeBarrierPost(argsObj_, (void *)&argsObj_); |
|
146 if (isFunctionFrame()) { |
|
147 JSFunction::writeBarrierPost(exec.fun, (void *)&exec.fun); |
|
148 if (isEvalFrame()) |
|
149 JSScript::writeBarrierPost(u.evalScript, (void *)&u.evalScript); |
|
150 } else { |
|
151 JSScript::writeBarrierPost(exec.script, (void *)&exec.script); |
|
152 } |
|
153 if (hasReturnValue()) |
|
154 HeapValue::writeBarrierPost(rval_, &rval_); |
|
155 } |
|
156 |
|
157 bool |
|
158 InterpreterFrame::copyRawFrameSlots(AutoValueVector *vec) |
|
159 { |
|
160 if (!vec->resize(numFormalArgs() + script()->nfixed())) |
|
161 return false; |
|
162 PodCopy(vec->begin(), argv(), numFormalArgs()); |
|
163 PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed()); |
|
164 return true; |
|
165 } |
|
166 |
|
167 JSObject * |
|
168 InterpreterFrame::createRestParameter(JSContext *cx) |
|
169 { |
|
170 JS_ASSERT(fun()->hasRest()); |
|
171 unsigned nformal = fun()->nargs() - 1, nactual = numActualArgs(); |
|
172 unsigned nrest = (nactual > nformal) ? nactual - nformal : 0; |
|
173 Value *restvp = argv() + nformal; |
|
174 JSObject *obj = NewDenseCopiedArray(cx, nrest, restvp, nullptr); |
|
175 if (!obj) |
|
176 return nullptr; |
|
177 types::FixRestArgumentsType(cx, obj); |
|
178 return obj; |
|
179 } |
|
180 |
|
181 static inline void |
|
182 AssertDynamicScopeMatchesStaticScope(JSContext *cx, JSScript *script, JSObject *scope) |
|
183 { |
|
184 #ifdef DEBUG |
|
185 RootedObject enclosingScope(cx, script->enclosingStaticScope()); |
|
186 for (StaticScopeIter<NoGC> i(enclosingScope); !i.done(); i++) { |
|
187 if (i.hasDynamicScopeObject()) { |
|
188 switch (i.type()) { |
|
189 case StaticScopeIter<NoGC>::BLOCK: |
|
190 JS_ASSERT(&i.block() == scope->as<ClonedBlockObject>().staticScope()); |
|
191 scope = &scope->as<ClonedBlockObject>().enclosingScope(); |
|
192 break; |
|
193 case StaticScopeIter<NoGC>::WITH: |
|
194 JS_ASSERT(&i.staticWith() == scope->as<DynamicWithObject>().staticScope()); |
|
195 scope = &scope->as<DynamicWithObject>().enclosingScope(); |
|
196 break; |
|
197 case StaticScopeIter<NoGC>::FUNCTION: |
|
198 JS_ASSERT(scope->as<CallObject>().callee().nonLazyScript() == i.funScript()); |
|
199 scope = &scope->as<CallObject>().enclosingScope(); |
|
200 break; |
|
201 case StaticScopeIter<NoGC>::NAMED_LAMBDA: |
|
202 scope = &scope->as<DeclEnvObject>().enclosingScope(); |
|
203 break; |
|
204 } |
|
205 } |
|
206 } |
|
207 |
|
208 /* |
|
209 * Ideally, we'd JS_ASSERT(!scope->is<ScopeObject>()) but the enclosing |
|
210 * lexical scope chain stops at eval() boundaries. See StaticScopeIter |
|
211 * comment. |
|
212 */ |
|
213 #endif |
|
214 } |
|
215 |
|
216 bool |
|
217 InterpreterFrame::initFunctionScopeObjects(JSContext *cx) |
|
218 { |
|
219 CallObject *callobj = CallObject::createForFunction(cx, this); |
|
220 if (!callobj) |
|
221 return false; |
|
222 pushOnScopeChain(*callobj); |
|
223 flags_ |= HAS_CALL_OBJ; |
|
224 return true; |
|
225 } |
|
226 |
|
227 bool |
|
228 InterpreterFrame::prologue(JSContext *cx) |
|
229 { |
|
230 RootedScript script(cx, this->script()); |
|
231 |
|
232 JS_ASSERT(!isGeneratorFrame()); |
|
233 JS_ASSERT(cx->interpreterRegs().pc == script->code()); |
|
234 |
|
235 if (isEvalFrame()) { |
|
236 if (script->strict()) { |
|
237 CallObject *callobj = CallObject::createForStrictEval(cx, this); |
|
238 if (!callobj) |
|
239 return false; |
|
240 pushOnScopeChain(*callobj); |
|
241 flags_ |= HAS_CALL_OBJ; |
|
242 } |
|
243 return probes::EnterScript(cx, script, nullptr, this); |
|
244 } |
|
245 |
|
246 if (isGlobalFrame()) |
|
247 return probes::EnterScript(cx, script, nullptr, this); |
|
248 |
|
249 JS_ASSERT(isNonEvalFunctionFrame()); |
|
250 AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain()); |
|
251 |
|
252 if (fun()->isHeavyweight() && !initFunctionScopeObjects(cx)) |
|
253 return false; |
|
254 |
|
255 if (isConstructing()) { |
|
256 RootedObject callee(cx, &this->callee()); |
|
257 JSObject *obj = CreateThisForFunction(cx, callee, |
|
258 useNewType() ? SingletonObject : GenericObject); |
|
259 if (!obj) |
|
260 return false; |
|
261 functionThis() = ObjectValue(*obj); |
|
262 } |
|
263 |
|
264 return probes::EnterScript(cx, script, script->functionNonDelazifying(), this); |
|
265 } |
|
266 |
|
267 void |
|
268 InterpreterFrame::epilogue(JSContext *cx) |
|
269 { |
|
270 JS_ASSERT(!isYielding()); |
|
271 |
|
272 RootedScript script(cx, this->script()); |
|
273 probes::ExitScript(cx, script, script->functionNonDelazifying(), hasPushedSPSFrame()); |
|
274 |
|
275 if (isEvalFrame()) { |
|
276 if (isStrictEvalFrame()) { |
|
277 JS_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval()); |
|
278 if (MOZ_UNLIKELY(cx->compartment()->debugMode())) |
|
279 DebugScopes::onPopStrictEvalScope(this); |
|
280 } else if (isDirectEvalFrame()) { |
|
281 if (isDebuggerFrame()) |
|
282 JS_ASSERT(!scopeChain()->is<ScopeObject>()); |
|
283 } else { |
|
284 /* |
|
285 * Debugger.Object.prototype.evalInGlobal creates indirect eval |
|
286 * frames scoped to the given global; |
|
287 * Debugger.Object.prototype.evalInGlobalWithBindings creates |
|
288 * indirect eval frames scoped to an object carrying the introduced |
|
289 * bindings. |
|
290 */ |
|
291 if (isDebuggerFrame()) { |
|
292 JS_ASSERT(scopeChain()->is<GlobalObject>() || |
|
293 scopeChain()->enclosingScope()->is<GlobalObject>()); |
|
294 } else { |
|
295 JS_ASSERT(scopeChain()->is<GlobalObject>()); |
|
296 } |
|
297 } |
|
298 return; |
|
299 } |
|
300 |
|
301 if (isGlobalFrame()) { |
|
302 JS_ASSERT(!scopeChain()->is<ScopeObject>()); |
|
303 return; |
|
304 } |
|
305 |
|
306 JS_ASSERT(isNonEvalFunctionFrame()); |
|
307 |
|
308 if (fun()->isHeavyweight()) |
|
309 JS_ASSERT_IF(hasCallObj(), |
|
310 scopeChain()->as<CallObject>().callee().nonLazyScript() == script); |
|
311 else |
|
312 AssertDynamicScopeMatchesStaticScope(cx, script, scopeChain()); |
|
313 |
|
314 if (MOZ_UNLIKELY(cx->compartment()->debugMode())) |
|
315 DebugScopes::onPopCall(this, cx); |
|
316 |
|
317 if (isConstructing() && thisValue().isObject() && returnValue().isPrimitive()) |
|
318 setReturnValue(ObjectValue(constructorThis())); |
|
319 } |
|
320 |
|
321 bool |
|
322 InterpreterFrame::pushBlock(JSContext *cx, StaticBlockObject &block) |
|
323 { |
|
324 JS_ASSERT (block.needsClone()); |
|
325 |
|
326 Rooted<StaticBlockObject *> blockHandle(cx, &block); |
|
327 ClonedBlockObject *clone = ClonedBlockObject::create(cx, blockHandle, this); |
|
328 if (!clone) |
|
329 return false; |
|
330 |
|
331 pushOnScopeChain(*clone); |
|
332 |
|
333 return true; |
|
334 } |
|
335 |
|
336 void |
|
337 InterpreterFrame::popBlock(JSContext *cx) |
|
338 { |
|
339 JS_ASSERT(scopeChain_->is<ClonedBlockObject>()); |
|
340 popOffScopeChain(); |
|
341 } |
|
342 |
|
343 void |
|
344 InterpreterFrame::popWith(JSContext *cx) |
|
345 { |
|
346 if (MOZ_UNLIKELY(cx->compartment()->debugMode())) |
|
347 DebugScopes::onPopWith(this); |
|
348 |
|
349 JS_ASSERT(scopeChain()->is<DynamicWithObject>()); |
|
350 popOffScopeChain(); |
|
351 } |
|
352 |
|
353 void |
|
354 InterpreterFrame::mark(JSTracer *trc) |
|
355 { |
|
356 /* |
|
357 * Normally we would use MarkRoot here, except that generators also take |
|
358 * this path. However, generators use a special write barrier when the stack |
|
359 * frame is copied to the floating frame. Therefore, no barrier is needed. |
|
360 */ |
|
361 if (flags_ & HAS_SCOPECHAIN) |
|
362 gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain"); |
|
363 if (flags_ & HAS_ARGS_OBJ) |
|
364 gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments"); |
|
365 if (isFunctionFrame()) { |
|
366 gc::MarkObjectUnbarriered(trc, &exec.fun, "fun"); |
|
367 if (isEvalFrame()) |
|
368 gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script"); |
|
369 } else { |
|
370 gc::MarkScriptUnbarriered(trc, &exec.script, "script"); |
|
371 } |
|
372 if (IS_GC_MARKING_TRACER(trc)) |
|
373 script()->compartment()->zone()->active = true; |
|
374 gc::MarkValueUnbarriered(trc, returnValue().address(), "rval"); |
|
375 } |
|
376 |
|
377 void |
|
378 InterpreterFrame::markValues(JSTracer *trc, unsigned start, unsigned end) |
|
379 { |
|
380 if (start < end) |
|
381 gc::MarkValueRootRange(trc, end - start, slots() + start, "vm_stack"); |
|
382 } |
|
383 |
|
384 void |
|
385 InterpreterFrame::markValues(JSTracer *trc, Value *sp, jsbytecode *pc) |
|
386 { |
|
387 JS_ASSERT(sp >= slots()); |
|
388 |
|
389 NestedScopeObject *staticScope; |
|
390 |
|
391 staticScope = script()->getStaticScope(pc); |
|
392 while (staticScope && !staticScope->is<StaticBlockObject>()) |
|
393 staticScope = staticScope->enclosingNestedScope(); |
|
394 |
|
395 size_t nfixed = script()->nfixed(); |
|
396 size_t nlivefixed; |
|
397 |
|
398 if (staticScope) { |
|
399 StaticBlockObject &blockObj = staticScope->as<StaticBlockObject>(); |
|
400 nlivefixed = blockObj.localOffset() + blockObj.numVariables(); |
|
401 } else { |
|
402 nlivefixed = script()->nfixedvars(); |
|
403 } |
|
404 |
|
405 if (nfixed == nlivefixed) { |
|
406 // All locals are live. |
|
407 markValues(trc, 0, sp - slots()); |
|
408 } else { |
|
409 // Mark operand stack. |
|
410 markValues(trc, nfixed, sp - slots()); |
|
411 |
|
412 // Clear dead locals. |
|
413 while (nfixed > nlivefixed) |
|
414 unaliasedLocal(--nfixed, DONT_CHECK_ALIASING).setUndefined(); |
|
415 |
|
416 // Mark live locals. |
|
417 markValues(trc, 0, nlivefixed); |
|
418 } |
|
419 |
|
420 if (hasArgs()) { |
|
421 // Mark callee, |this| and arguments. |
|
422 unsigned argc = Max(numActualArgs(), numFormalArgs()); |
|
423 gc::MarkValueRootRange(trc, argc + 2, argv_ - 2, "fp argv"); |
|
424 } else { |
|
425 // Mark callee and |this| |
|
426 gc::MarkValueRootRange(trc, 2, ((Value *)this) - 2, "stack callee and this"); |
|
427 } |
|
428 } |
|
429 |
|
430 static void |
|
431 MarkInterpreterActivation(JSTracer *trc, InterpreterActivation *act) |
|
432 { |
|
433 for (InterpreterFrameIterator frames(act); !frames.done(); ++frames) { |
|
434 InterpreterFrame *fp = frames.frame(); |
|
435 fp->markValues(trc, frames.sp(), frames.pc()); |
|
436 fp->mark(trc); |
|
437 } |
|
438 } |
|
439 |
|
440 void |
|
441 js::MarkInterpreterActivations(JSRuntime *rt, JSTracer *trc) |
|
442 { |
|
443 for (ActivationIterator iter(rt); !iter.done(); ++iter) { |
|
444 Activation *act = iter.activation(); |
|
445 if (act->isInterpreter()) |
|
446 MarkInterpreterActivation(trc, act->asInterpreter()); |
|
447 } |
|
448 |
|
449 } |
|
450 |
|
451 /*****************************************************************************/ |
|
452 |
|
453 // Unlike the other methods of this calss, this method is defined here so that |
|
454 // we don't have to #include jsautooplen.h in vm/Stack.h. |
|
455 void |
|
456 InterpreterRegs::setToEndOfScript() |
|
457 { |
|
458 JSScript *script = fp()->script(); |
|
459 sp = fp()->base(); |
|
460 pc = script->codeEnd() - JSOP_RETRVAL_LENGTH; |
|
461 JS_ASSERT(*pc == JSOP_RETRVAL); |
|
462 } |
|
463 |
|
464 /*****************************************************************************/ |
|
465 |
|
466 InterpreterFrame * |
|
467 InterpreterStack::pushInvokeFrame(JSContext *cx, const CallArgs &args, InitialFrameFlags initial) |
|
468 { |
|
469 LifoAlloc::Mark mark = allocator_.mark(); |
|
470 |
|
471 RootedFunction fun(cx, &args.callee().as<JSFunction>()); |
|
472 RootedScript script(cx, fun->nonLazyScript()); |
|
473 |
|
474 InterpreterFrame::Flags flags = ToFrameFlags(initial); |
|
475 Value *argv; |
|
476 InterpreterFrame *fp = getCallFrame(cx, args, script, &flags, &argv); |
|
477 if (!fp) |
|
478 return nullptr; |
|
479 |
|
480 fp->mark_ = mark; |
|
481 fp->initCallFrame(cx, nullptr, nullptr, nullptr, *fun, script, argv, args.length(), flags); |
|
482 return fp; |
|
483 } |
|
484 |
|
485 InterpreterFrame * |
|
486 InterpreterStack::pushExecuteFrame(JSContext *cx, HandleScript script, const Value &thisv, |
|
487 HandleObject scopeChain, ExecuteType type, |
|
488 AbstractFramePtr evalInFrame) |
|
489 { |
|
490 LifoAlloc::Mark mark = allocator_.mark(); |
|
491 |
|
492 unsigned nvars = 2 /* callee, this */ + script->nslots(); |
|
493 uint8_t *buffer = allocateFrame(cx, sizeof(InterpreterFrame) + nvars * sizeof(Value)); |
|
494 if (!buffer) |
|
495 return nullptr; |
|
496 |
|
497 InterpreterFrame *fp = reinterpret_cast<InterpreterFrame *>(buffer + 2 * sizeof(Value)); |
|
498 fp->mark_ = mark; |
|
499 fp->initExecuteFrame(cx, script, evalInFrame, thisv, *scopeChain, type); |
|
500 fp->initVarsToUndefined(); |
|
501 |
|
502 return fp; |
|
503 } |
|
504 |
|
505 /*****************************************************************************/ |
|
506 |
|
507 /* MSVC PGO causes xpcshell startup crashes. */ |
|
508 #if defined(_MSC_VER) |
|
509 # pragma optimize("g", off) |
|
510 #endif |
|
511 |
|
512 void |
|
513 FrameIter::popActivation() |
|
514 { |
|
515 ++data_.activations_; |
|
516 settleOnActivation(); |
|
517 } |
|
518 |
|
519 void |
|
520 FrameIter::popInterpreterFrame() |
|
521 { |
|
522 JS_ASSERT(data_.state_ == INTERP); |
|
523 |
|
524 ++data_.interpFrames_; |
|
525 |
|
526 if (data_.interpFrames_.done()) |
|
527 popActivation(); |
|
528 else |
|
529 data_.pc_ = data_.interpFrames_.pc(); |
|
530 } |
|
531 |
|
532 void |
|
533 FrameIter::settleOnActivation() |
|
534 { |
|
535 while (true) { |
|
536 if (data_.activations_.done()) { |
|
537 data_.state_ = DONE; |
|
538 return; |
|
539 } |
|
540 |
|
541 Activation *activation = data_.activations_.activation(); |
|
542 |
|
543 // If JS_SaveFrameChain was called, stop iterating here (unless |
|
544 // GO_THROUGH_SAVED is set). |
|
545 if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) { |
|
546 data_.state_ = DONE; |
|
547 return; |
|
548 } |
|
549 |
|
550 // Skip activations from another context if needed. |
|
551 JS_ASSERT(activation->cx()); |
|
552 JS_ASSERT(data_.cx_); |
|
553 if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) { |
|
554 ++data_.activations_; |
|
555 continue; |
|
556 } |
|
557 |
|
558 // If the caller supplied principals, only show activations which are subsumed (of the same |
|
559 // origin or of an origin accessible) by these principals. |
|
560 if (data_.principals_) { |
|
561 if (JSSubsumesOp subsumes = data_.cx_->runtime()->securityCallbacks->subsumes) { |
|
562 JS::AutoAssertNoGC nogc; |
|
563 if (!subsumes(data_.principals_, activation->compartment()->principals)) { |
|
564 ++data_.activations_; |
|
565 continue; |
|
566 } |
|
567 } |
|
568 } |
|
569 |
|
570 #ifdef JS_ION |
|
571 if (activation->isJit()) { |
|
572 data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); |
|
573 |
|
574 // Stop at the first scripted frame. |
|
575 while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done()) |
|
576 ++data_.jitFrames_; |
|
577 |
|
578 // It's possible to have an JitActivation with no scripted frames, |
|
579 // for instance if we hit an over-recursion during bailout. |
|
580 if (data_.jitFrames_.done()) { |
|
581 ++data_.activations_; |
|
582 continue; |
|
583 } |
|
584 |
|
585 nextJitFrame(); |
|
586 data_.state_ = JIT; |
|
587 return; |
|
588 } |
|
589 |
|
590 if (activation->isAsmJS()) { |
|
591 data_.asmJSFrames_ = AsmJSFrameIterator(data_.activations_->asAsmJS()); |
|
592 |
|
593 if (data_.asmJSFrames_.done()) { |
|
594 ++data_.activations_; |
|
595 continue; |
|
596 } |
|
597 |
|
598 data_.state_ = ASMJS; |
|
599 return; |
|
600 } |
|
601 |
|
602 // ForkJoin activations don't contain iterable frames, so skip them. |
|
603 if (activation->isForkJoin()) { |
|
604 ++data_.activations_; |
|
605 continue; |
|
606 } |
|
607 #endif |
|
608 |
|
609 JS_ASSERT(activation->isInterpreter()); |
|
610 |
|
611 InterpreterActivation *interpAct = activation->asInterpreter(); |
|
612 data_.interpFrames_ = InterpreterFrameIterator(interpAct); |
|
613 |
|
614 // If we OSR'ed into JIT code, skip the interpreter frame so that |
|
615 // the same frame is not reported twice. |
|
616 if (data_.interpFrames_.frame()->runningInJit()) { |
|
617 ++data_.interpFrames_; |
|
618 if (data_.interpFrames_.done()) { |
|
619 ++data_.activations_; |
|
620 continue; |
|
621 } |
|
622 } |
|
623 |
|
624 JS_ASSERT(!data_.interpFrames_.frame()->runningInJit()); |
|
625 data_.pc_ = data_.interpFrames_.pc(); |
|
626 data_.state_ = INTERP; |
|
627 return; |
|
628 } |
|
629 } |
|
630 |
|
631 FrameIter::Data::Data(JSContext *cx, SavedOption savedOption, ContextOption contextOption, |
|
632 JSPrincipals *principals) |
|
633 : cx_(cx), |
|
634 savedOption_(savedOption), |
|
635 contextOption_(contextOption), |
|
636 principals_(principals), |
|
637 pc_(nullptr), |
|
638 interpFrames_(nullptr), |
|
639 activations_(cx->runtime()) |
|
640 #ifdef JS_ION |
|
641 , jitFrames_((uint8_t *)nullptr, SequentialExecution) |
|
642 , ionInlineFrameNo_(0) |
|
643 , asmJSFrames_(nullptr) |
|
644 #endif |
|
645 { |
|
646 } |
|
647 |
|
648 FrameIter::Data::Data(const FrameIter::Data &other) |
|
649 : cx_(other.cx_), |
|
650 savedOption_(other.savedOption_), |
|
651 contextOption_(other.contextOption_), |
|
652 principals_(other.principals_), |
|
653 state_(other.state_), |
|
654 pc_(other.pc_), |
|
655 interpFrames_(other.interpFrames_), |
|
656 activations_(other.activations_) |
|
657 #ifdef JS_ION |
|
658 , jitFrames_(other.jitFrames_) |
|
659 , ionInlineFrameNo_(other.ionInlineFrameNo_) |
|
660 , asmJSFrames_(other.asmJSFrames_) |
|
661 #endif |
|
662 { |
|
663 } |
|
664 |
|
665 FrameIter::FrameIter(JSContext *cx, SavedOption savedOption) |
|
666 : data_(cx, savedOption, CURRENT_CONTEXT, nullptr) |
|
667 #ifdef JS_ION |
|
668 , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr) |
|
669 #endif |
|
670 { |
|
671 // settleOnActivation can only GC if principals are given. |
|
672 JS::AutoAssertNoGC nogc; |
|
673 settleOnActivation(); |
|
674 } |
|
675 |
|
676 FrameIter::FrameIter(JSContext *cx, ContextOption contextOption, |
|
677 SavedOption savedOption, JSPrincipals *principals) |
|
678 : data_(cx, savedOption, contextOption, principals) |
|
679 #ifdef JS_ION |
|
680 , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr) |
|
681 #endif |
|
682 { |
|
683 settleOnActivation(); |
|
684 } |
|
685 |
|
686 FrameIter::FrameIter(const FrameIter &other) |
|
687 : data_(other.data_) |
|
688 #ifdef JS_ION |
|
689 , ionInlineFrames_(other.data_.cx_, |
|
690 data_.jitFrames_.isIonJS() ? &other.ionInlineFrames_ : nullptr) |
|
691 #endif |
|
692 { |
|
693 } |
|
694 |
|
695 FrameIter::FrameIter(const Data &data) |
|
696 : data_(data) |
|
697 #ifdef JS_ION |
|
698 , ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr) |
|
699 #endif |
|
700 { |
|
701 JS_ASSERT(data.cx_); |
|
702 |
|
703 #ifdef JS_ION |
|
704 if (data_.jitFrames_.isIonJS()) { |
|
705 while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_) |
|
706 ++ionInlineFrames_; |
|
707 } |
|
708 #endif |
|
709 } |
|
710 |
|
711 #ifdef JS_ION |
|
712 void |
|
713 FrameIter::nextJitFrame() |
|
714 { |
|
715 if (data_.jitFrames_.isIonJS()) { |
|
716 ionInlineFrames_.resetOn(&data_.jitFrames_); |
|
717 data_.pc_ = ionInlineFrames_.pc(); |
|
718 } else { |
|
719 JS_ASSERT(data_.jitFrames_.isBaselineJS()); |
|
720 data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_); |
|
721 } |
|
722 } |
|
723 |
|
724 void |
|
725 FrameIter::popJitFrame() |
|
726 { |
|
727 JS_ASSERT(data_.state_ == JIT); |
|
728 |
|
729 if (data_.jitFrames_.isIonJS() && ionInlineFrames_.more()) { |
|
730 ++ionInlineFrames_; |
|
731 data_.pc_ = ionInlineFrames_.pc(); |
|
732 return; |
|
733 } |
|
734 |
|
735 ++data_.jitFrames_; |
|
736 while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted()) |
|
737 ++data_.jitFrames_; |
|
738 |
|
739 if (!data_.jitFrames_.done()) { |
|
740 nextJitFrame(); |
|
741 return; |
|
742 } |
|
743 |
|
744 popActivation(); |
|
745 } |
|
746 |
|
747 void |
|
748 FrameIter::popAsmJSFrame() |
|
749 { |
|
750 JS_ASSERT(data_.state_ == ASMJS); |
|
751 |
|
752 ++data_.asmJSFrames_; |
|
753 if (data_.asmJSFrames_.done()) |
|
754 popActivation(); |
|
755 } |
|
756 #endif |
|
757 |
|
758 FrameIter & |
|
759 FrameIter::operator++() |
|
760 { |
|
761 switch (data_.state_) { |
|
762 case DONE: |
|
763 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
764 case INTERP: |
|
765 if (interpFrame()->isDebuggerFrame() && interpFrame()->evalInFramePrev()) { |
|
766 AbstractFramePtr eifPrev = interpFrame()->evalInFramePrev(); |
|
767 MOZ_ASSERT(!eifPrev.isRematerializedFrame()); |
|
768 |
|
769 // Eval-in-frame can cross contexts and works across saved frame |
|
770 // chains. |
|
771 ContextOption prevContextOption = data_.contextOption_; |
|
772 SavedOption prevSavedOption = data_.savedOption_; |
|
773 data_.contextOption_ = ALL_CONTEXTS; |
|
774 data_.savedOption_ = GO_THROUGH_SAVED; |
|
775 |
|
776 popInterpreterFrame(); |
|
777 |
|
778 while (isIon() || abstractFramePtr() != eifPrev) { |
|
779 if (data_.state_ == JIT) { |
|
780 #ifdef JS_ION |
|
781 popJitFrame(); |
|
782 #else |
|
783 MOZ_ASSUME_UNREACHABLE("Invalid state"); |
|
784 #endif |
|
785 } else { |
|
786 popInterpreterFrame(); |
|
787 } |
|
788 } |
|
789 |
|
790 data_.contextOption_ = prevContextOption; |
|
791 data_.savedOption_ = prevSavedOption; |
|
792 data_.cx_ = data_.activations_->cx(); |
|
793 break; |
|
794 } |
|
795 popInterpreterFrame(); |
|
796 break; |
|
797 #ifdef JS_ION |
|
798 case JIT: |
|
799 popJitFrame(); |
|
800 break; |
|
801 case ASMJS: |
|
802 popAsmJSFrame(); |
|
803 break; |
|
804 #else |
|
805 default: |
|
806 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
807 #endif |
|
808 } |
|
809 return *this; |
|
810 } |
|
811 |
|
812 FrameIter::Data * |
|
813 FrameIter::copyData() const |
|
814 { |
|
815 Data *data = data_.cx_->new_<Data>(data_); |
|
816 #ifdef JS_ION |
|
817 JS_ASSERT(data_.state_ != ASMJS); |
|
818 if (data && data_.jitFrames_.isIonJS()) |
|
819 data->ionInlineFrameNo_ = ionInlineFrames_.frameNo(); |
|
820 #endif |
|
821 return data; |
|
822 } |
|
823 |
|
824 AbstractFramePtr |
|
825 FrameIter::copyDataAsAbstractFramePtr() const |
|
826 { |
|
827 AbstractFramePtr frame; |
|
828 if (Data *data = copyData()) |
|
829 frame.ptr_ = uintptr_t(data); |
|
830 return frame; |
|
831 } |
|
832 |
|
833 JSCompartment * |
|
834 FrameIter::compartment() const |
|
835 { |
|
836 switch (data_.state_) { |
|
837 case DONE: |
|
838 break; |
|
839 case INTERP: |
|
840 case JIT: |
|
841 case ASMJS: |
|
842 return data_.activations_->compartment(); |
|
843 } |
|
844 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
845 } |
|
846 |
|
847 bool |
|
848 FrameIter::isFunctionFrame() const |
|
849 { |
|
850 switch (data_.state_) { |
|
851 case DONE: |
|
852 break; |
|
853 case INTERP: |
|
854 return interpFrame()->isFunctionFrame(); |
|
855 case JIT: |
|
856 #ifdef JS_ION |
|
857 JS_ASSERT(data_.jitFrames_.isScripted()); |
|
858 if (data_.jitFrames_.isBaselineJS()) |
|
859 return data_.jitFrames_.isFunctionFrame(); |
|
860 return ionInlineFrames_.isFunctionFrame(); |
|
861 #else |
|
862 break; |
|
863 #endif |
|
864 case ASMJS: |
|
865 return true; |
|
866 } |
|
867 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
868 } |
|
869 |
|
870 bool |
|
871 FrameIter::isGlobalFrame() const |
|
872 { |
|
873 switch (data_.state_) { |
|
874 case DONE: |
|
875 break; |
|
876 case INTERP: |
|
877 return interpFrame()->isGlobalFrame(); |
|
878 case JIT: |
|
879 #ifdef JS_ION |
|
880 if (data_.jitFrames_.isBaselineJS()) |
|
881 return data_.jitFrames_.baselineFrame()->isGlobalFrame(); |
|
882 JS_ASSERT(!script()->isForEval()); |
|
883 return !script()->functionNonDelazifying(); |
|
884 #else |
|
885 break; |
|
886 #endif |
|
887 case ASMJS: |
|
888 return false; |
|
889 } |
|
890 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
891 } |
|
892 |
|
893 bool |
|
894 FrameIter::isEvalFrame() const |
|
895 { |
|
896 switch (data_.state_) { |
|
897 case DONE: |
|
898 break; |
|
899 case INTERP: |
|
900 return interpFrame()->isEvalFrame(); |
|
901 case JIT: |
|
902 #ifdef JS_ION |
|
903 if (data_.jitFrames_.isBaselineJS()) |
|
904 return data_.jitFrames_.baselineFrame()->isEvalFrame(); |
|
905 JS_ASSERT(!script()->isForEval()); |
|
906 return false; |
|
907 #else |
|
908 break; |
|
909 #endif |
|
910 case ASMJS: |
|
911 return false; |
|
912 } |
|
913 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
914 } |
|
915 |
|
916 bool |
|
917 FrameIter::isNonEvalFunctionFrame() const |
|
918 { |
|
919 JS_ASSERT(!done()); |
|
920 switch (data_.state_) { |
|
921 case DONE: |
|
922 break; |
|
923 case INTERP: |
|
924 return interpFrame()->isNonEvalFunctionFrame(); |
|
925 case JIT: |
|
926 return !isEvalFrame() && isFunctionFrame(); |
|
927 case ASMJS: |
|
928 return true; |
|
929 } |
|
930 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
931 } |
|
932 |
|
933 bool |
|
934 FrameIter::isGeneratorFrame() const |
|
935 { |
|
936 switch (data_.state_) { |
|
937 case DONE: |
|
938 break; |
|
939 case INTERP: |
|
940 return interpFrame()->isGeneratorFrame(); |
|
941 case JIT: |
|
942 return false; |
|
943 case ASMJS: |
|
944 return false; |
|
945 } |
|
946 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
947 } |
|
948 |
|
949 JSAtom * |
|
950 FrameIter::functionDisplayAtom() const |
|
951 { |
|
952 JS_ASSERT(isNonEvalFunctionFrame()); |
|
953 |
|
954 switch (data_.state_) { |
|
955 case DONE: |
|
956 break; |
|
957 case INTERP: |
|
958 case JIT: |
|
959 return callee()->displayAtom(); |
|
960 case ASMJS: { |
|
961 #ifdef JS_ION |
|
962 return data_.asmJSFrames_.functionDisplayAtom(); |
|
963 #else |
|
964 break; |
|
965 #endif |
|
966 } |
|
967 } |
|
968 |
|
969 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
970 } |
|
971 |
|
972 ScriptSource * |
|
973 FrameIter::scriptSource() const |
|
974 { |
|
975 switch (data_.state_) { |
|
976 case DONE: |
|
977 break; |
|
978 case INTERP: |
|
979 case JIT: |
|
980 return script()->scriptSource(); |
|
981 case ASMJS: |
|
982 #ifdef JS_ION |
|
983 return data_.activations_->asAsmJS()->module().scriptSource(); |
|
984 #else |
|
985 break; |
|
986 #endif |
|
987 } |
|
988 |
|
989 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
990 } |
|
991 |
|
992 const char * |
|
993 FrameIter::scriptFilename() const |
|
994 { |
|
995 switch (data_.state_) { |
|
996 case DONE: |
|
997 break; |
|
998 case INTERP: |
|
999 case JIT: |
|
1000 return script()->filename(); |
|
1001 case ASMJS: |
|
1002 #ifdef JS_ION |
|
1003 return data_.activations_->asAsmJS()->module().scriptSource()->filename(); |
|
1004 #else |
|
1005 break; |
|
1006 #endif |
|
1007 } |
|
1008 |
|
1009 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1010 } |
|
1011 |
|
1012 unsigned |
|
1013 FrameIter::computeLine(uint32_t *column) const |
|
1014 { |
|
1015 switch (data_.state_) { |
|
1016 case DONE: |
|
1017 break; |
|
1018 case INTERP: |
|
1019 case JIT: |
|
1020 return PCToLineNumber(script(), pc(), column); |
|
1021 case ASMJS: |
|
1022 #ifdef JS_ION |
|
1023 return data_.asmJSFrames_.computeLine(column); |
|
1024 #else |
|
1025 break; |
|
1026 #endif |
|
1027 } |
|
1028 |
|
1029 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1030 } |
|
1031 |
|
1032 JSPrincipals * |
|
1033 FrameIter::originPrincipals() const |
|
1034 { |
|
1035 switch (data_.state_) { |
|
1036 case DONE: |
|
1037 break; |
|
1038 case INTERP: |
|
1039 case JIT: |
|
1040 return script()->originPrincipals(); |
|
1041 case ASMJS: { |
|
1042 #ifdef JS_ION |
|
1043 return data_.activations_->asAsmJS()->module().scriptSource()->originPrincipals(); |
|
1044 #else |
|
1045 break; |
|
1046 #endif |
|
1047 } |
|
1048 } |
|
1049 |
|
1050 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1051 } |
|
1052 |
|
1053 bool |
|
1054 FrameIter::isConstructing() const |
|
1055 { |
|
1056 switch (data_.state_) { |
|
1057 case DONE: |
|
1058 case ASMJS: |
|
1059 break; |
|
1060 case JIT: |
|
1061 #ifdef JS_ION |
|
1062 if (data_.jitFrames_.isIonJS()) |
|
1063 return ionInlineFrames_.isConstructing(); |
|
1064 JS_ASSERT(data_.jitFrames_.isBaselineJS()); |
|
1065 return data_.jitFrames_.isConstructing(); |
|
1066 #else |
|
1067 break; |
|
1068 #endif |
|
1069 case INTERP: |
|
1070 return interpFrame()->isConstructing(); |
|
1071 } |
|
1072 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1073 } |
|
1074 |
|
1075 bool |
|
1076 FrameIter::ensureHasRematerializedFrame() |
|
1077 { |
|
1078 #ifdef JS_ION |
|
1079 MOZ_ASSERT(isIon()); |
|
1080 return !!activation()->asJit()->getRematerializedFrame(activation()->cx(), data_.jitFrames_); |
|
1081 #else |
|
1082 return true; |
|
1083 #endif |
|
1084 } |
|
1085 |
|
1086 bool |
|
1087 FrameIter::hasUsableAbstractFramePtr() const |
|
1088 { |
|
1089 switch (data_.state_) { |
|
1090 case DONE: |
|
1091 case ASMJS: |
|
1092 return false; |
|
1093 case JIT: |
|
1094 #ifdef JS_ION |
|
1095 if (data_.jitFrames_.isBaselineJS()) |
|
1096 return true; |
|
1097 |
|
1098 MOZ_ASSERT(data_.jitFrames_.isIonJS()); |
|
1099 return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(), |
|
1100 ionInlineFrames_.frameNo()); |
|
1101 #endif |
|
1102 break; |
|
1103 case INTERP: |
|
1104 return true; |
|
1105 } |
|
1106 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1107 } |
|
1108 |
|
1109 AbstractFramePtr |
|
1110 FrameIter::abstractFramePtr() const |
|
1111 { |
|
1112 MOZ_ASSERT(hasUsableAbstractFramePtr()); |
|
1113 switch (data_.state_) { |
|
1114 case DONE: |
|
1115 case ASMJS: |
|
1116 break; |
|
1117 case JIT: { |
|
1118 #ifdef JS_ION |
|
1119 if (data_.jitFrames_.isBaselineJS()) |
|
1120 return data_.jitFrames_.baselineFrame(); |
|
1121 |
|
1122 MOZ_ASSERT(data_.jitFrames_.isIonJS()); |
|
1123 return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(), |
|
1124 ionInlineFrames_.frameNo()); |
|
1125 #endif |
|
1126 break; |
|
1127 } |
|
1128 case INTERP: |
|
1129 JS_ASSERT(interpFrame()); |
|
1130 return AbstractFramePtr(interpFrame()); |
|
1131 } |
|
1132 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1133 } |
|
1134 |
|
1135 void |
|
1136 FrameIter::updatePcQuadratic() |
|
1137 { |
|
1138 switch (data_.state_) { |
|
1139 case DONE: |
|
1140 case ASMJS: |
|
1141 break; |
|
1142 case INTERP: { |
|
1143 InterpreterFrame *frame = interpFrame(); |
|
1144 InterpreterActivation *activation = data_.activations_->asInterpreter(); |
|
1145 |
|
1146 // Look for the current frame. |
|
1147 data_.interpFrames_ = InterpreterFrameIterator(activation); |
|
1148 while (data_.interpFrames_.frame() != frame) |
|
1149 ++data_.interpFrames_; |
|
1150 |
|
1151 // Update the pc. |
|
1152 JS_ASSERT(data_.interpFrames_.frame() == frame); |
|
1153 data_.pc_ = data_.interpFrames_.pc(); |
|
1154 return; |
|
1155 } |
|
1156 case JIT: |
|
1157 #ifdef JS_ION |
|
1158 if (data_.jitFrames_.isBaselineJS()) { |
|
1159 jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame(); |
|
1160 jit::JitActivation *activation = data_.activations_->asJit(); |
|
1161 |
|
1162 // ActivationIterator::ionTop_ may be invalid, so create a new |
|
1163 // activation iterator. |
|
1164 data_.activations_ = ActivationIterator(data_.cx_->runtime()); |
|
1165 while (data_.activations_.activation() != activation) |
|
1166 ++data_.activations_; |
|
1167 |
|
1168 // Look for the current frame. |
|
1169 data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); |
|
1170 while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame) |
|
1171 ++data_.jitFrames_; |
|
1172 |
|
1173 // Update the pc. |
|
1174 JS_ASSERT(data_.jitFrames_.baselineFrame() == frame); |
|
1175 data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_); |
|
1176 return; |
|
1177 } |
|
1178 #endif |
|
1179 break; |
|
1180 } |
|
1181 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1182 } |
|
1183 |
|
1184 JSFunction * |
|
1185 FrameIter::callee() const |
|
1186 { |
|
1187 switch (data_.state_) { |
|
1188 case DONE: |
|
1189 case ASMJS: |
|
1190 break; |
|
1191 case INTERP: |
|
1192 JS_ASSERT(isFunctionFrame()); |
|
1193 return &interpFrame()->callee(); |
|
1194 case JIT: |
|
1195 #ifdef JS_ION |
|
1196 if (data_.jitFrames_.isBaselineJS()) |
|
1197 return data_.jitFrames_.callee(); |
|
1198 JS_ASSERT(data_.jitFrames_.isIonJS()); |
|
1199 return ionInlineFrames_.callee(); |
|
1200 #else |
|
1201 break; |
|
1202 #endif |
|
1203 } |
|
1204 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1205 } |
|
1206 |
|
1207 Value |
|
1208 FrameIter::calleev() const |
|
1209 { |
|
1210 switch (data_.state_) { |
|
1211 case DONE: |
|
1212 case ASMJS: |
|
1213 break; |
|
1214 case INTERP: |
|
1215 JS_ASSERT(isFunctionFrame()); |
|
1216 return interpFrame()->calleev(); |
|
1217 case JIT: |
|
1218 #ifdef JS_ION |
|
1219 return ObjectValue(*callee()); |
|
1220 #else |
|
1221 break; |
|
1222 #endif |
|
1223 } |
|
1224 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1225 } |
|
1226 |
|
1227 unsigned |
|
1228 FrameIter::numActualArgs() const |
|
1229 { |
|
1230 switch (data_.state_) { |
|
1231 case DONE: |
|
1232 case ASMJS: |
|
1233 break; |
|
1234 case INTERP: |
|
1235 JS_ASSERT(isFunctionFrame()); |
|
1236 return interpFrame()->numActualArgs(); |
|
1237 case JIT: |
|
1238 #ifdef JS_ION |
|
1239 if (data_.jitFrames_.isIonJS()) |
|
1240 return ionInlineFrames_.numActualArgs(); |
|
1241 |
|
1242 JS_ASSERT(data_.jitFrames_.isBaselineJS()); |
|
1243 return data_.jitFrames_.numActualArgs(); |
|
1244 #else |
|
1245 break; |
|
1246 #endif |
|
1247 } |
|
1248 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1249 } |
|
1250 |
|
1251 unsigned |
|
1252 FrameIter::numFormalArgs() const |
|
1253 { |
|
1254 return script()->functionNonDelazifying()->nargs(); |
|
1255 } |
|
1256 |
|
1257 Value |
|
1258 FrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const |
|
1259 { |
|
1260 return abstractFramePtr().unaliasedActual(i, checkAliasing); |
|
1261 } |
|
1262 |
|
1263 JSObject * |
|
1264 FrameIter::scopeChain() const |
|
1265 { |
|
1266 switch (data_.state_) { |
|
1267 case DONE: |
|
1268 case ASMJS: |
|
1269 break; |
|
1270 case JIT: |
|
1271 #ifdef JS_ION |
|
1272 if (data_.jitFrames_.isIonJS()) |
|
1273 return ionInlineFrames_.scopeChain(); |
|
1274 return data_.jitFrames_.baselineFrame()->scopeChain(); |
|
1275 #else |
|
1276 break; |
|
1277 #endif |
|
1278 case INTERP: |
|
1279 return interpFrame()->scopeChain(); |
|
1280 } |
|
1281 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1282 } |
|
1283 |
|
1284 CallObject & |
|
1285 FrameIter::callObj() const |
|
1286 { |
|
1287 JS_ASSERT(callee()->isHeavyweight()); |
|
1288 |
|
1289 JSObject *pobj = scopeChain(); |
|
1290 while (!pobj->is<CallObject>()) |
|
1291 pobj = pobj->enclosingScope(); |
|
1292 return pobj->as<CallObject>(); |
|
1293 } |
|
1294 |
|
1295 bool |
|
1296 FrameIter::hasArgsObj() const |
|
1297 { |
|
1298 return abstractFramePtr().hasArgsObj(); |
|
1299 } |
|
1300 |
|
1301 ArgumentsObject & |
|
1302 FrameIter::argsObj() const |
|
1303 { |
|
1304 MOZ_ASSERT(hasArgsObj()); |
|
1305 return abstractFramePtr().argsObj(); |
|
1306 } |
|
1307 |
|
1308 bool |
|
1309 FrameIter::computeThis(JSContext *cx) const |
|
1310 { |
|
1311 JS_ASSERT(!done() && !isAsmJS()); |
|
1312 assertSameCompartment(cx, scopeChain()); |
|
1313 return ComputeThis(cx, abstractFramePtr()); |
|
1314 } |
|
1315 |
|
1316 Value |
|
1317 FrameIter::computedThisValue() const |
|
1318 { |
|
1319 return abstractFramePtr().thisValue(); |
|
1320 } |
|
1321 |
|
1322 Value |
|
1323 FrameIter::thisv() const |
|
1324 { |
|
1325 switch (data_.state_) { |
|
1326 case DONE: |
|
1327 case ASMJS: |
|
1328 break; |
|
1329 case JIT: |
|
1330 #ifdef JS_ION |
|
1331 if (data_.jitFrames_.isIonJS()) |
|
1332 return ionInlineFrames_.thisValue(); |
|
1333 return data_.jitFrames_.baselineFrame()->thisValue(); |
|
1334 #else |
|
1335 break; |
|
1336 #endif |
|
1337 case INTERP: |
|
1338 return interpFrame()->thisValue(); |
|
1339 } |
|
1340 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1341 } |
|
1342 |
|
1343 Value |
|
1344 FrameIter::returnValue() const |
|
1345 { |
|
1346 switch (data_.state_) { |
|
1347 case DONE: |
|
1348 case ASMJS: |
|
1349 break; |
|
1350 case JIT: |
|
1351 #ifdef JS_ION |
|
1352 if (data_.jitFrames_.isBaselineJS()) |
|
1353 return data_.jitFrames_.baselineFrame()->returnValue(); |
|
1354 #endif |
|
1355 break; |
|
1356 case INTERP: |
|
1357 return interpFrame()->returnValue(); |
|
1358 } |
|
1359 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1360 } |
|
1361 |
|
1362 void |
|
1363 FrameIter::setReturnValue(const Value &v) |
|
1364 { |
|
1365 switch (data_.state_) { |
|
1366 case DONE: |
|
1367 case ASMJS: |
|
1368 break; |
|
1369 case JIT: |
|
1370 #ifdef JS_ION |
|
1371 if (data_.jitFrames_.isBaselineJS()) { |
|
1372 data_.jitFrames_.baselineFrame()->setReturnValue(v); |
|
1373 return; |
|
1374 } |
|
1375 #endif |
|
1376 break; |
|
1377 case INTERP: |
|
1378 interpFrame()->setReturnValue(v); |
|
1379 return; |
|
1380 } |
|
1381 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1382 } |
|
1383 |
|
1384 size_t |
|
1385 FrameIter::numFrameSlots() const |
|
1386 { |
|
1387 switch (data_.state_) { |
|
1388 case DONE: |
|
1389 case ASMJS: |
|
1390 break; |
|
1391 case JIT: { |
|
1392 #ifdef JS_ION |
|
1393 if (data_.jitFrames_.isIonJS()) { |
|
1394 return ionInlineFrames_.snapshotIterator().numAllocations() - |
|
1395 ionInlineFrames_.script()->nfixed(); |
|
1396 } |
|
1397 jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame(); |
|
1398 return frame->numValueSlots() - data_.jitFrames_.script()->nfixed(); |
|
1399 #else |
|
1400 break; |
|
1401 #endif |
|
1402 } |
|
1403 case INTERP: |
|
1404 JS_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base()); |
|
1405 return data_.interpFrames_.sp() - interpFrame()->base(); |
|
1406 } |
|
1407 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1408 } |
|
1409 |
|
1410 Value |
|
1411 FrameIter::frameSlotValue(size_t index) const |
|
1412 { |
|
1413 switch (data_.state_) { |
|
1414 case DONE: |
|
1415 case ASMJS: |
|
1416 break; |
|
1417 case JIT: |
|
1418 #ifdef JS_ION |
|
1419 if (data_.jitFrames_.isIonJS()) { |
|
1420 jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator()); |
|
1421 index += ionInlineFrames_.script()->nfixed(); |
|
1422 return si.maybeReadAllocByIndex(index); |
|
1423 } |
|
1424 |
|
1425 index += data_.jitFrames_.script()->nfixed(); |
|
1426 return *data_.jitFrames_.baselineFrame()->valueSlot(index); |
|
1427 #else |
|
1428 break; |
|
1429 #endif |
|
1430 case INTERP: |
|
1431 return interpFrame()->base()[index]; |
|
1432 } |
|
1433 MOZ_ASSUME_UNREACHABLE("Unexpected state"); |
|
1434 } |
|
1435 |
|
1436 #if defined(_MSC_VER) |
|
1437 # pragma optimize("", on) |
|
1438 #endif |
|
1439 |
|
1440 #ifdef DEBUG |
|
1441 bool |
|
1442 js::SelfHostedFramesVisible() |
|
1443 { |
|
1444 static bool checked = false; |
|
1445 static bool visible = false; |
|
1446 if (!checked) { |
|
1447 checked = true; |
|
1448 char *env = getenv("MOZ_SHOW_ALL_JS_FRAMES"); |
|
1449 visible = !!env; |
|
1450 } |
|
1451 return visible; |
|
1452 } |
|
1453 #endif |
|
1454 |
|
1455 void |
|
1456 NonBuiltinFrameIter::settle() |
|
1457 { |
|
1458 if (!SelfHostedFramesVisible()) { |
|
1459 while (!done() && hasScript() && script()->selfHosted()) |
|
1460 FrameIter::operator++(); |
|
1461 } |
|
1462 } |
|
1463 |
|
1464 void |
|
1465 NonBuiltinScriptFrameIter::settle() |
|
1466 { |
|
1467 if (!SelfHostedFramesVisible()) { |
|
1468 while (!done() && script()->selfHosted()) |
|
1469 ScriptFrameIter::operator++(); |
|
1470 } |
|
1471 } |
|
1472 |
|
1473 /*****************************************************************************/ |
|
1474 |
|
1475 JSObject * |
|
1476 AbstractFramePtr::evalPrevScopeChain(JSContext *cx) const |
|
1477 { |
|
1478 // Eval frames are not compiled by Ion, though their caller might be. |
|
1479 AllFramesIter iter(cx); |
|
1480 while (iter.isIon() || iter.abstractFramePtr() != *this) |
|
1481 ++iter; |
|
1482 ++iter; |
|
1483 return iter.scopeChain(); |
|
1484 } |
|
1485 |
|
1486 bool |
|
1487 AbstractFramePtr::hasPushedSPSFrame() const |
|
1488 { |
|
1489 if (isInterpreterFrame()) |
|
1490 return asInterpreterFrame()->hasPushedSPSFrame(); |
|
1491 #ifdef JS_ION |
|
1492 return asBaselineFrame()->hasPushedSPSFrame(); |
|
1493 #else |
|
1494 MOZ_ASSUME_UNREACHABLE("Invalid frame"); |
|
1495 #endif |
|
1496 } |
|
1497 |
|
1498 #ifdef DEBUG |
|
1499 void |
|
1500 js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script, uint32_t i) |
|
1501 { |
|
1502 if (!checkAliasing) |
|
1503 return; |
|
1504 |
|
1505 JS_ASSERT(i < script->nfixed()); |
|
1506 if (i < script->bindings.numVars()) { |
|
1507 JS_ASSERT(!script->varIsAliased(i)); |
|
1508 } else { |
|
1509 // FIXME: The callers of this function do not easily have the PC of the |
|
1510 // current frame, and so they do not know the block scope. |
|
1511 } |
|
1512 } |
|
1513 #endif |
|
1514 |
|
1515 jit::JitActivation::JitActivation(JSContext *cx, bool firstFrameIsConstructing, bool active) |
|
1516 : Activation(cx, Jit), |
|
1517 firstFrameIsConstructing_(firstFrameIsConstructing), |
|
1518 active_(active) |
|
1519 #ifdef JS_ION |
|
1520 , rematerializedFrames_(cx) |
|
1521 #endif |
|
1522 { |
|
1523 if (active) { |
|
1524 prevIonTop_ = cx->mainThread().ionTop; |
|
1525 prevJitJSContext_ = cx->mainThread().jitJSContext; |
|
1526 cx->mainThread().jitJSContext = cx; |
|
1527 } else { |
|
1528 prevIonTop_ = nullptr; |
|
1529 prevJitJSContext_ = nullptr; |
|
1530 } |
|
1531 } |
|
1532 |
|
1533 jit::JitActivation::~JitActivation() |
|
1534 { |
|
1535 if (active_) { |
|
1536 cx_->mainThread().ionTop = prevIonTop_; |
|
1537 cx_->mainThread().jitJSContext = prevJitJSContext_; |
|
1538 } |
|
1539 |
|
1540 #ifdef JS_ION |
|
1541 clearRematerializedFrames(); |
|
1542 #endif |
|
1543 } |
|
1544 |
|
1545 // setActive() is inlined in GenerateFFIIonExit() with explicit masm instructions so |
|
1546 // changes to the logic here need to be reflected in GenerateFFIIonExit() in the enable |
|
1547 // and disable activation instruction sequences. |
|
1548 void |
|
1549 jit::JitActivation::setActive(JSContext *cx, bool active) |
|
1550 { |
|
1551 // Only allowed to deactivate/activate if activation is top. |
|
1552 // (Not tested and will probably fail in other situations.) |
|
1553 JS_ASSERT(cx->mainThread().activation_ == this); |
|
1554 JS_ASSERT(active != active_); |
|
1555 active_ = active; |
|
1556 |
|
1557 if (active) { |
|
1558 prevIonTop_ = cx->mainThread().ionTop; |
|
1559 prevJitJSContext_ = cx->mainThread().jitJSContext; |
|
1560 cx->mainThread().jitJSContext = cx; |
|
1561 } else { |
|
1562 cx->mainThread().ionTop = prevIonTop_; |
|
1563 cx->mainThread().jitJSContext = prevJitJSContext_; |
|
1564 } |
|
1565 } |
|
1566 |
|
1567 #ifdef JS_ION |
|
1568 |
|
1569 void |
|
1570 jit::JitActivation::freeRematerializedFramesInVector(RematerializedFrameVector &frames) |
|
1571 { |
|
1572 for (size_t i = 0; i < frames.length(); i++) { |
|
1573 RematerializedFrame *f = frames[i]; |
|
1574 f->RematerializedFrame::~RematerializedFrame(); |
|
1575 js_free(f); |
|
1576 } |
|
1577 frames.clear(); |
|
1578 } |
|
1579 |
|
1580 void |
|
1581 jit::JitActivation::removeRematerializedFrame(uint8_t *top) |
|
1582 { |
|
1583 if (!rematerializedFrames_.initialized()) |
|
1584 return; |
|
1585 |
|
1586 if (RematerializedFrameTable::Ptr p = rematerializedFrames_.lookup(top)) { |
|
1587 freeRematerializedFramesInVector(p->value()); |
|
1588 rematerializedFrames_.remove(p); |
|
1589 } |
|
1590 } |
|
1591 |
|
1592 void |
|
1593 jit::JitActivation::clearRematerializedFrames() |
|
1594 { |
|
1595 if (!rematerializedFrames_.initialized()) |
|
1596 return; |
|
1597 |
|
1598 for (RematerializedFrameTable::Enum e(rematerializedFrames_); !e.empty(); e.popFront()) { |
|
1599 freeRematerializedFramesInVector(e.front().value()); |
|
1600 e.removeFront(); |
|
1601 } |
|
1602 } |
|
1603 |
|
1604 jit::RematerializedFrame * |
|
1605 jit::JitActivation::getRematerializedFrame(JSContext *cx, JitFrameIterator &iter, |
|
1606 size_t inlineDepth) |
|
1607 { |
|
1608 MOZ_ASSERT(iter.activation() == this); |
|
1609 MOZ_ASSERT(iter.isIonJS()); |
|
1610 |
|
1611 if (!rematerializedFrames_.initialized() && !rematerializedFrames_.init()) |
|
1612 return nullptr; |
|
1613 |
|
1614 // The unit of rematerialization is an uninlined frame and its inlined |
|
1615 // frames. Since inlined frames do not exist outside of snapshots, it is |
|
1616 // impossible to synchronize their rematerialized copies to preserve |
|
1617 // identity. Therefore, we always rematerialize an uninlined frame and all |
|
1618 // its inlined frames at once. |
|
1619 |
|
1620 uint8_t *top = iter.fp(); |
|
1621 RematerializedFrameTable::AddPtr p = rematerializedFrames_.lookupForAdd(top); |
|
1622 if (!p) { |
|
1623 RematerializedFrameVector empty(cx); |
|
1624 if (!rematerializedFrames_.add(p, top, Move(empty))) |
|
1625 return nullptr; |
|
1626 |
|
1627 InlineFrameIterator inlineIter(cx, &iter); |
|
1628 if (!p->value().resize(inlineIter.frameCount())) |
|
1629 return nullptr; |
|
1630 |
|
1631 while (true) { |
|
1632 size_t frameNo = inlineIter.frameNo(); |
|
1633 p->value()[frameNo] = RematerializedFrame::New(cx, top, inlineIter); |
|
1634 if (!p->value()[frameNo]) |
|
1635 return nullptr; |
|
1636 |
|
1637 if (!inlineIter.more()) |
|
1638 break; |
|
1639 ++inlineIter; |
|
1640 } |
|
1641 } |
|
1642 |
|
1643 return p->value()[inlineDepth]; |
|
1644 } |
|
1645 |
|
1646 jit::RematerializedFrame * |
|
1647 jit::JitActivation::lookupRematerializedFrame(uint8_t *top, size_t inlineDepth) |
|
1648 { |
|
1649 if (!rematerializedFrames_.initialized()) |
|
1650 return nullptr; |
|
1651 if (RematerializedFrameTable::Ptr p = rematerializedFrames_.lookup(top)) |
|
1652 return inlineDepth < p->value().length() ? p->value()[inlineDepth] : nullptr; |
|
1653 return nullptr; |
|
1654 } |
|
1655 |
|
1656 void |
|
1657 jit::JitActivation::markRematerializedFrames(JSTracer *trc) |
|
1658 { |
|
1659 if (!rematerializedFrames_.initialized()) |
|
1660 return; |
|
1661 for (RematerializedFrameTable::Enum e(rematerializedFrames_); !e.empty(); e.popFront()) { |
|
1662 RematerializedFrameVector &frames = e.front().value(); |
|
1663 for (size_t i = 0; i < frames.length(); i++) |
|
1664 frames[i]->mark(trc); |
|
1665 } |
|
1666 } |
|
1667 |
|
1668 #endif // JS_ION |
|
1669 |
|
1670 AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module) |
|
1671 : Activation(cx, AsmJS), |
|
1672 module_(module), |
|
1673 errorRejoinSP_(nullptr), |
|
1674 profiler_(nullptr), |
|
1675 resumePC_(nullptr), |
|
1676 exitSP_(nullptr) |
|
1677 { |
|
1678 if (cx->runtime()->spsProfiler.enabled()) { |
|
1679 // Use a profiler string that matches jsMatch regex in |
|
1680 // browser/devtools/profiler/cleopatra/js/parserWorker.js. |
|
1681 // (For now use a single static string to avoid further slowing down |
|
1682 // calls into asm.js.) |
|
1683 profiler_ = &cx->runtime()->spsProfiler; |
|
1684 profiler_->enterNative("asm.js code :0", this); |
|
1685 } |
|
1686 |
|
1687 prevAsmJS_ = cx_->runtime()->mainThread.asmJSActivationStack_; |
|
1688 |
|
1689 JSRuntime::AutoLockForInterrupt lock(cx_->runtime()); |
|
1690 cx_->runtime()->mainThread.asmJSActivationStack_ = this; |
|
1691 |
|
1692 (void) errorRejoinSP_; // squelch GCC warning |
|
1693 } |
|
1694 |
|
1695 AsmJSActivation::~AsmJSActivation() |
|
1696 { |
|
1697 if (profiler_) |
|
1698 profiler_->exitNative(); |
|
1699 |
|
1700 JS_ASSERT(cx_->runtime()->mainThread.asmJSActivationStack_ == this); |
|
1701 |
|
1702 JSRuntime::AutoLockForInterrupt lock(cx_->runtime()); |
|
1703 cx_->runtime()->mainThread.asmJSActivationStack_ = prevAsmJS_; |
|
1704 } |
|
1705 |
|
1706 InterpreterFrameIterator & |
|
1707 InterpreterFrameIterator::operator++() |
|
1708 { |
|
1709 JS_ASSERT(!done()); |
|
1710 if (fp_ != activation_->entryFrame_) { |
|
1711 pc_ = fp_->prevpc(); |
|
1712 sp_ = fp_->prevsp(); |
|
1713 fp_ = fp_->prev(); |
|
1714 } else { |
|
1715 pc_ = nullptr; |
|
1716 sp_ = nullptr; |
|
1717 fp_ = nullptr; |
|
1718 } |
|
1719 return *this; |
|
1720 } |
|
1721 |
|
1722 ActivationIterator::ActivationIterator(JSRuntime *rt) |
|
1723 : jitTop_(rt->mainThread.ionTop), |
|
1724 activation_(rt->mainThread.activation_) |
|
1725 { |
|
1726 settle(); |
|
1727 } |
|
1728 |
|
1729 ActivationIterator & |
|
1730 ActivationIterator::operator++() |
|
1731 { |
|
1732 JS_ASSERT(activation_); |
|
1733 if (activation_->isJit() && activation_->asJit()->isActive()) |
|
1734 jitTop_ = activation_->asJit()->prevIonTop(); |
|
1735 activation_ = activation_->prev(); |
|
1736 settle(); |
|
1737 return *this; |
|
1738 } |
|
1739 |
|
1740 void |
|
1741 ActivationIterator::settle() |
|
1742 { |
|
1743 // Stop at the next active activation. No need to update jitTop_, since |
|
1744 // we don't iterate over an active jit activation. |
|
1745 while (!done() && activation_->isJit() && !activation_->asJit()->isActive()) |
|
1746 activation_ = activation_->prev(); |
|
1747 } |