uint32 ion::ReflowTypeInfo(uint32 bailoutResult) { JSContext *cx = GetIonContext()->cx; IonActivation *activation = cx->runtime->ionActivation; IonSpew(IonSpew_Bailouts, "reflowing type info"); if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) { IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry"); ReflowArgTypes(cx); return true; } RootedScript script(cx, cx->fp()->script()); jsbytecode *pc = activation->bailout()->bailoutPc(); JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); IonSpew(IonSpew_Bailouts, "reflowing type info at %s:%d pcoff %d", script->filename, script->lineno, pc - script->code); types::AutoEnterTypeInference enter(cx); if (bailoutResult == BAILOUT_RETURN_TYPE_BARRIER) script->analysis()->breakTypeBarriers(cx, pc - script->code, false); else JS_ASSERT(bailoutResult == BAILOUT_RETURN_MONITOR); // When a type barrier fails, the bad value is at the top of the stack. Value &result = cx->regs().sp[-1]; types::TypeScript::Monitor(cx, script, pc, result); return true; }
/* * Given a frame that is about to return, make sure its return value and * activation objects are fixed up. Then, pop the frame and advance the * current PC. Note that while we could enter the JIT at this point, the * logic would still be necessary for the interpreter, so it's easier * (and faster) to finish frames in C++ even if at a safe point here. */ static bool HandleFinishedFrame(VMFrame &f, StackFrame *entryFrame) { JSContext *cx = f.cx; JS_ASSERT(FrameIsFinished(cx)); /* * This is the most difficult and complicated piece of the tracer * integration, and historically has been very buggy. The problem is that * although this frame has to be popped (see RemoveExcessFrames), it may * be at a JSOP_RETURN opcode, and it might not have ever been executed. * That is, fp->rval may not be set to the top of the stack, and if it * has, the stack has already been decremented. Note that fp->rval is not * the only problem: the epilogue may never have been executed. * * Here are the edge cases and whether the frame has been exited cleanly: * 1. No: A trace exited directly before a RETURN op, and the * interpreter never ran. * 2. Yes: The interpreter exited cleanly. * 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT * is not used in between JSOP_RETURN and advancing the PC, * therefore, it cannot have been run if at a safe point. * 4. No: Somewhere in the RunTracer call tree, we removed a frame, * and we returned to a JSOP_RETURN opcode. Note carefully * that in this situation, FrameIsFinished() returns true! * 5. Yes: The function exited in the method JIT, during * FinishExcessFrames() However, in this case, we'll never enter * HandleFinishedFrame(): we always immediately pop JIT'd frames. * * Since the only scenario where this fixup is NOT needed is a normal exit * from the interpreter, we can cleanly check for this scenario by checking * a bit it sets in the frame. */ bool returnOK = true; if (!cx->fp()->finishedInInterpreter()) { if (JSOp(*cx->regs().pc) == JSOP_RETURN) cx->fp()->setReturnValue(f.regs.sp[-1]); returnOK = ScriptEpilogue(cx, cx->fp(), true); } if (cx->fp() != entryFrame) { InlineReturn(f); AdvanceReturnPC(cx); } return returnOK; }
/* * Interprets until either a safe point is reached that has method JIT'd * code, or the current frame tries to return. */ static inline JSBool PartialInterpret(VMFrame &f) { JSContext *cx = f.cx; StackFrame *fp = cx->fp(); #ifdef DEBUG JSScript *script = fp->script(); JS_ASSERT(!fp->finishedInInterpreter()); JS_ASSERT(fp->hasImacropc() || !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc)); #endif JSBool ok = JS_TRUE; ok = Interpret(cx, fp, JSINTERP_SAFEPOINT); return ok; }
uint32 ion::ThunkToInterpreter(Value *vp) { JSContext *cx = GetIonContext()->cx; IonActivation *activation = cx->runtime->ionActivation; BailoutClosure *br = activation->takeBailout(); if (!EnsureHasCallObject(cx, cx->fp())) return Interpret_Error; // By default we set the forbidOsr flag on the ion script, but if a GC // happens just after we re-enter the interpreter, the ion script get // invalidated and we do not see the forbidOsr flag. This may cause a loop // which apear with eager compilation and gc zeal enabled. This code is a // workaround to avoid recompiling with OSR just after a bailout followed by // a GC. (see Bug 746691 & Bug 751383) jsbytecode *pc = cx->regs().pc; while (JSOp(*pc) == JSOP_GOTO) pc += GET_JUMP_OFFSET(pc); if (JSOp(*pc) == JSOP_LOOPENTRY) cx->regs().pc = GetNextPc(pc); // When JSScript::argumentsOptimizationFailed, we cannot access Ion frames // in order to create an arguments object for them. However, there is an // invariant that script->needsArgsObj() implies fp->hasArgsObj() (after the // prologue), so we must create one now for each inlined frame which needs // one. { br->entryfp()->clearRunningInIon(); ScriptFrameIter iter(cx); StackFrame *fp = NULL; Rooted<JSScript*> script(cx, NULL); do { fp = iter.interpFrame(); script = iter.script(); if (script->needsArgsObj()) { // Currently IonMonkey does not compile if the script needs an // arguments object, so the frame should not have any argument // object yet. JS_ASSERT(!fp->hasArgsObj()); ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, fp); if (!argsobj) return Interpret_Error; InternalBindingsHandle bindings(script, &script->bindings); const unsigned var = Bindings::argumentsVarIndex(cx, bindings); // The arguments is a local binding and needsArgsObj does not // check if it is clobbered. Ensure that the local binding // restored during bailout before storing the arguments object // to the slot. if (fp->unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS)) fp->unaliasedLocal(var) = ObjectValue(*argsobj); } ++iter; } while (fp != br->entryfp()); } if (activation->entryfp() == br->entryfp()) { // If the bailout entry fp is the same as the activation entryfp, then // there are no scripted frames below us. In this case, just shortcut // out with a special return code, and resume interpreting in the // original Interpret activation. vp->setMagic(JS_ION_BAILOUT); js_delete(br); return Interpret_Ok; } InterpretStatus status = Interpret(cx, br->entryfp(), JSINTERP_BAILOUT); if (status == Interpret_OSR) { // The interpreter currently does not ask to perform inline OSR, so // this path is unreachable. JS_NOT_REACHED("invalid"); IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d", cx->fp()->script()->filename, PCToLineNumber(cx->fp()->script(), cx->regs().pc)); // We want to OSR again. We need to avoid the problem where frequent // bailouts cause recursive nestings of Interpret and EnterIon. The // interpreter therefore shortcuts out, and now we're responsible for // completing the OSR inline. // // Note that we set runningInIon so that if we re-enter C++ from within // the inlined OSR, StackIter will know to traverse these frames. StackFrame *fp = cx->fp(); fp->setRunningInIon(); vp->setPrivate(fp); js_delete(br); return Interpret_OSR; } if (status == Interpret_Ok) *vp = br->entryfp()->returnValue(); // The BailoutFrameGuard's destructor will ensure that the frame is // removed. js_delete(br); return status; }
uint32 ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut) { AssertCanGC(); sp->checkInvariants(); JSContext *cx = GetIonContext()->cx; // We don't have an exit frame. cx->runtime->ionTop = NULL; IonActivationIterator ionActivations(cx); IonBailoutIterator iter(ionActivations, sp); IonActivation *activation = ionActivations.activation(); IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.topFrameSize(); uint32 retval = ConvertFrames(cx, activation, iter); { IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(NULL); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); } iter.ionScript()->decref(cx->runtime->defaultFreeOp()); if (cx->runtime->hasIonReturnOverride()) cx->regs().sp[-1] = cx->runtime->takeIonReturnOverride(); if (retval != BAILOUT_RETURN_FATAL_ERROR) { if (activation->entryfp()) { if (void *annotation = activation->entryfp()->annotation()) { // If the entry frame has an annotation, then we invalidated and have // immediately returned into this bailout. Transfer the annotation to // the new topmost frame. activation->entryfp()->setAnnotation(NULL); cx->fp()->setAnnotation(annotation); } } // If invalidation was triggered inside a stub call, we may still have to // monitor the result, since the bailout happens before the MMonitorTypes // instruction is executed. jsbytecode *pc = activation->bailout()->bailoutPc(); // If this is not a ResumeAfter bailout, there's nothing to monitor, // we will redo the op in the interpreter. bool isResumeAfter = GetNextPc(pc) == cx->regs().pc; if ((js_CodeSpec[*pc].format & JOF_TYPESET) && isResumeAfter) { JS_ASSERT(retval == BAILOUT_RETURN_OK); return BAILOUT_RETURN_MONITOR; } return retval; } return BAILOUT_RETURN_FATAL_ERROR; }
uint32 ion::ThunkToInterpreter(Value *vp) { JSContext *cx = GetIonContext()->cx; IonActivation *activation = cx->runtime->ionActivation; BailoutClosure *br = activation->takeBailout(); if (!EnsureHasCallObject(cx, cx->fp())) return Interpret_Error; // By default we set the forbidOsr flag on the ion script, but if a GC // happens just after we re-enter the interpreter, the ion script get // invalidated and we do not see the forbidOsr flag. This may cause a loop // which apear with eager compilation and gc zeal enabled. This code is a // workaround to avoid recompiling with OSR just after a bailout followed by // a GC. (see Bug 746691 & Bug 751383) jsbytecode *pc = cx->regs().pc; while (JSOp(*pc) == JSOP_GOTO) pc += GET_JUMP_OFFSET(pc); if (JSOp(*pc) == JSOP_LOOPENTRY) cx->regs().pc = GetNextPc(pc); if (activation->entryfp() == br->entryfp()) { // If the bailout entry fp is the same as the activation entryfp, then // there are no scripted frames below us. In this case, just shortcut // out with a special return code, and resume interpreting in the // original Interpret activation. vp->setMagic(JS_ION_BAILOUT); js_delete(br); return Interpret_Ok; } InterpretStatus status = Interpret(cx, br->entryfp(), JSINTERP_BAILOUT); if (status == Interpret_OSR) { // The interpreter currently does not ask to perform inline OSR, so // this path is unreachable. JS_NOT_REACHED("invalid"); IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d", cx->fp()->script()->filename, PCToLineNumber(cx->fp()->script(), cx->regs().pc)); // We want to OSR again. We need to avoid the problem where frequent // bailouts cause recursive nestings of Interpret and EnterIon. The // interpreter therefore shortcuts out, and now we're responsible for // completing the OSR inline. // // Note that we set runningInIon so that if we re-enter C++ from within // the inlined OSR, StackIter will know to traverse these frames. StackFrame *fp = cx->fp(); fp->setRunningInIon(); vp->setPrivate(fp); js_delete(br); return Interpret_OSR; } if (status == Interpret_Ok) *vp = br->entryfp()->returnValue(); // The BailoutFrameGuard's destructor will ensure that the frame is // removed. js_delete(br); return status; }
extern "C" void * js_InternalThrow(VMFrame &f) { JSContext *cx = f.cx; ExpandInlineFrames(cx->compartment); // The current frame may have an associated orphaned native, if the native // or SplatApplyArgs threw an exception. RemoveOrphanedNative(cx, f.fp()); JS_ASSERT(!f.fp()->finishedInInterpreter()); // Make sure sp is up to date. JS_ASSERT(&cx->regs() == &f.regs); jsbytecode *pc = NULL; for (;;) { if (cx->isExceptionPending()) { // Call the throw hook if necessary JSThrowHook handler = cx->runtime->debugHooks.throwHook; if (handler || !cx->compartment->getDebuggees().empty()) { Value rval; JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval); if (st == JSTRAP_CONTINUE && handler) { RootedScript fscript(cx, cx->fp()->script()); st = handler(cx, fscript, cx->regs().pc, &rval, cx->runtime->debugHooks.throwHookData); } switch (st) { case JSTRAP_ERROR: cx->clearPendingException(); break; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: cx->clearPendingException(); cx->fp()->setReturnValue(rval); return cx->jaegerRuntime().forceReturnFromExternC(); case JSTRAP_THROW: cx->setPendingException(rval); break; default: JS_NOT_REACHED("bad onExceptionUnwind status"); } } } pc = FindExceptionHandler(cx); if (pc) break; // The JIT guarantees that ScriptDebugEpilogue() and ScriptEpilogue() // have always been run upon exiting to its caller. This is important // for consistency, where execution modes make similar guarantees about // prologues and epilogues. Interpret(), and Invoke() all rely on this // property. JS_ASSERT(!f.fp()->finishedInInterpreter()); UnwindScope(cx, 0); f.regs.setToEndOfScript(); if (cx->compartment->debugMode()) { // This can turn a throw or error into a healthy return. Note that // we will run ScriptDebugEpilogue again (from AnyFrameEpilogue); // ScriptDebugEpilogue is prepared for this eventuality. if (js::ScriptDebugEpilogue(cx, f.fp(), false)) return cx->jaegerRuntime().forceReturnFromExternC(); } f.fp()->epilogue(f.cx); // Don't remove the last frame, this is the responsibility of // JaegerShot()'s caller. We only guarantee that ScriptEpilogue() // has been run. if (f.entryfp == f.fp()) break; f.cx->stack.popInlineFrame(f.regs); DebugOnly<JSOp> op = JSOp(*f.regs.pc); JS_ASSERT(op == JSOP_CALL || op == JSOP_NEW || op == JSOP_EVAL || op == JSOP_FUNCALL || op == JSOP_FUNAPPLY); f.regs.pc += JSOP_CALL_LENGTH; } JS_ASSERT(&cx->regs() == &f.regs); if (!pc) return NULL; StackFrame *fp = cx->fp(); RootedScript script(cx, fp->script()); /* * Fall back to EnterMethodJIT and finish the frame in the interpreter. * With type inference enabled, we may wipe out all JIT code on the * stack without patching ncode values to jump to the interpreter, and * thus can only enter JIT code via EnterMethodJIT (which overwrites * its entry frame's ncode). See ClearAllFrames. */ cx->jaegerRuntime().setLastUnfinished(Jaeger_Unfinished); if (!JSScript::ensureRanAnalysis(cx, script)) { js_ReportOutOfMemory(cx); return NULL; } analyze::AutoEnterAnalysis enter(cx); /* * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go * back into the interpreter with a pending exception. This will cause * it to immediately rethrow. */ if (cx->isExceptionPending()) { JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK); StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock(); Value *vp = cx->regs().sp + blockObj.slotCount(); SetValueRangeToUndefined(cx->regs().sp, vp); cx->regs().sp = vp; if (!cx->regs().fp()->pushBlock(cx, blockObj)) return NULL; JS_ASSERT(JSOp(pc[JSOP_ENTERBLOCK_LENGTH]) == JSOP_EXCEPTION); cx->regs().sp[0] = cx->getPendingException(); cx->clearPendingException(); cx->regs().sp++; cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH; } *f.oldregs = f.regs; return NULL; }
uint32_t ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, BaselineBailoutInfo **bailoutInfo) { sp->checkInvariants(); JSContext *cx = GetIonContext()->cx; // We don't have an exit frame. cx->mainThread().ionTop = NULL; IonActivationIterator ionActivations(cx); IonBailoutIterator iter(ionActivations, sp); IonActivation *activation = ionActivations.activation(); IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.topFrameSize(); uint32_t retval; if (IsBaselineEnabled(cx)) { *bailoutInfo = NULL; retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); JS_ASSERT(retval == BAILOUT_RETURN_BASELINE || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); JS_ASSERT_IF(retval == BAILOUT_RETURN_BASELINE, *bailoutInfo != NULL); if (retval != BAILOUT_RETURN_BASELINE) { IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(NULL); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); } iter.ionScript()->decref(cx->runtime->defaultFreeOp()); return retval; } else { retval = ConvertFrames(cx, activation, iter); IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(NULL); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); iter.ionScript()->decref(cx->runtime->defaultFreeOp()); // Only need to take ion return override if resuming to interpreter. if (cx->runtime->hasIonReturnOverride()) cx->regs().sp[-1] = cx->runtime->takeIonReturnOverride(); if (retval != BAILOUT_RETURN_FATAL_ERROR) { // If invalidation was triggered inside a stub call, we may still have to // monitor the result, since the bailout happens before the MMonitorTypes // instruction is executed. jsbytecode *pc = activation->bailout()->bailoutPc(); // If this is not a ResumeAfter bailout, there's nothing to monitor, // we will redo the op in the interpreter. bool isResumeAfter = GetNextPc(pc) == cx->regs().pc; if ((js_CodeSpec[*pc].format & JOF_TYPESET) && isResumeAfter) { JS_ASSERT(retval == BAILOUT_RETURN_OK); return BAILOUT_RETURN_MONITOR; } return retval; } return BAILOUT_RETURN_FATAL_ERROR; } }
/* * Called when an error is in progress and the topmost frame could not handle * it. This will unwind to a given frame, or find and align to an exception * handler in the process. */ static inline bool HandleErrorInExcessFrame(VMFrame &f, StackFrame *stopFp, bool searchedTopmostFrame = true) { JSContext *cx = f.cx; /* * Callers of this called either Interpret() or JaegerShot(), which would * have searched for exception handlers already. If we see stopFp, just * return false. Otherwise, pop the frame, since it's guaranteed useless. * * Note that this also guarantees ScriptEpilogue() has been called. */ StackFrame *fp = cx->fp(); if (searchedTopmostFrame) { /* * This is a special case meaning that fp->finishedInInterpreter() is * true. If so, and fp == stopFp, our only choice is to propagate this * error up, back to the method JIT, and then to js_InternalThrow, * where this becomes a special case. See the comment there and bug * 624100. */ if (fp == stopFp) return false; /* * Otherwise, the protocol here (like Invoke) is to assume that the * execution mode finished the frame, and to just pop it. */ InlineReturn(f); } /* Remove the bottom frame. */ bool returnOK = false; for (;;) { fp = cx->fp(); /* Clear imacros. */ if (fp->hasImacropc()) { cx->regs().pc = fp->imacropc(); fp->clearImacropc(); } JS_ASSERT(!fp->hasImacropc()); /* If there's an exception and a handler, set the pc and leave. */ if (cx->isExceptionPending()) { jsbytecode *pc = FindExceptionHandler(cx); if (pc) { cx->regs().pc = pc; returnOK = true; break; } } /* Don't unwind if this was the entry frame. */ if (fp == stopFp) break; /* Unwind and return. */ returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending())); returnOK = ScriptEpilogue(cx, fp, returnOK); InlineReturn(f); } JS_ASSERT(&f.regs == &cx->regs()); JS_ASSERT_IF(!returnOK, cx->fp() == stopFp); return returnOK; }
extern "C" void * js_InternalThrow(VMFrame &f) { JSContext *cx = f.cx; // It's possible that from within RunTracer(), Interpret() returned with // an error and finished the frame (i.e., called ScriptEpilogue), but has // not yet performed an inline return. // // In this case, RunTracer() has no choice but to propagate the error // up to the method JIT, and thus to this function. But ScriptEpilogue() // has already been called. Detect this, and avoid double-finishing the // frame. See HandleErrorInExcessFrame() and bug 624100. if (f.fp()->finishedInInterpreter()) { // If it's the last frame, just propagate the failure up again. if (f.fp() == f.entryfp) return NULL; InlineReturn(f); } // Make sure sp is up to date. JS_ASSERT(&cx->regs() == &f.regs); jsbytecode *pc = NULL; for (;;) { if (cx->isExceptionPending()) { // Call the throw hook if necessary JSThrowHook handler = cx->debugHooks->throwHook; if (handler || !cx->compartment->getDebuggees().empty()) { Value rval; JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval); if (st == JSTRAP_CONTINUE && handler) { st = handler(cx, cx->fp()->script(), cx->regs().pc, Jsvalify(&rval), cx->debugHooks->throwHookData); } switch (st) { case JSTRAP_ERROR: cx->clearPendingException(); return NULL; case JSTRAP_RETURN: cx->clearPendingException(); cx->fp()->setReturnValue(rval); return cx->jaegerCompartment()->forceReturnFromExternC(); case JSTRAP_THROW: cx->setPendingException(rval); break; default: break; } } } pc = FindExceptionHandler(cx); if (pc) break; // The JIT guarantees that ScriptEpilogue() has always been run // upon exiting to its caller. This is important for consistency, // where execution modes make similar guarantees about prologues // and epilogues. RunTracer(), Interpret(), and Invoke() all // rely on this property. JS_ASSERT(!f.fp()->finishedInInterpreter()); js_UnwindScope(cx, 0, cx->isExceptionPending()); ScriptEpilogue(f.cx, f.fp(), false); // Don't remove the last frame, this is the responsibility of // JaegerShot()'s caller. We only guarantee that ScriptEpilogue() // has been run. if (f.entryfp == f.fp()) break; JS_ASSERT(f.regs.sp == cx->regs().sp); InlineReturn(f); } JS_ASSERT(f.regs.sp == cx->regs().sp); if (!pc) return NULL; StackFrame *fp = cx->fp(); JSScript *script = fp->script(); return script->nativeCodeForPC(fp->isConstructing(), pc); }
extern "C" void * js_InternalThrow(VMFrame &f) { JSContext *cx = f.cx; ExpandInlineFrames(cx->compartment); // The current frame may have an associated orphaned native, if the native // or SplatApplyArgs threw an exception. RemoveOrphanedNative(cx, f.fp()); JS_ASSERT(!f.fp()->finishedInInterpreter()); // Make sure sp is up to date. JS_ASSERT(&cx->regs() == &f.regs); jsbytecode *pc = NULL; for (;;) { if (cx->isExceptionPending()) { // Call the throw hook if necessary JSThrowHook handler = cx->debugHooks->throwHook; if (handler || !cx->compartment->getDebuggees().empty()) { Value rval; JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval); if (st == JSTRAP_CONTINUE && handler) { st = handler(cx, cx->fp()->script(), cx->regs().pc, &rval, cx->debugHooks->throwHookData); } switch (st) { case JSTRAP_ERROR: cx->clearPendingException(); return NULL; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: cx->clearPendingException(); cx->fp()->setReturnValue(rval); return cx->jaegerCompartment()->forceReturnFromExternC(); case JSTRAP_THROW: cx->setPendingException(rval); break; default: JS_NOT_REACHED("bad onExceptionUnwind status"); } } } pc = FindExceptionHandler(cx); if (pc) break; // The JIT guarantees that ScriptEpilogue() has always been run // upon exiting to its caller. This is important for consistency, // where execution modes make similar guarantees about prologues // and epilogues. RunTracer(), Interpret(), and Invoke() all // rely on this property. JS_ASSERT(!f.fp()->finishedInInterpreter()); UnwindScope(cx, 0, cx->isExceptionPending()); if (cx->compartment->debugMode()) js::ScriptDebugEpilogue(cx, f.fp(), false); ScriptEpilogue(f.cx, f.fp(), false); // Don't remove the last frame, this is the responsibility of // JaegerShot()'s caller. We only guarantee that ScriptEpilogue() // has been run. if (f.entryfp == f.fp()) break; JS_ASSERT(f.regs.sp == cx->regs().sp); InlineReturn(f); } JS_ASSERT(f.regs.sp == cx->regs().sp); if (!pc) return NULL; StackFrame *fp = cx->fp(); JSScript *script = fp->script(); /* * Fall back to EnterMethodJIT and finish the frame in the interpreter. * With type inference enabled, we may wipe out all JIT code on the * stack without patching ncode values to jump to the interpreter, and * thus can only enter JIT code via EnterMethodJIT (which overwrites * its entry frame's ncode). See ClearAllFrames. */ cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished); if (!script->ensureRanAnalysis(cx, NULL)) { js_ReportOutOfMemory(cx); return NULL; } analyze::AutoEnterAnalysis enter(cx); cx->regs().pc = pc; cx->regs().sp = fp->base() + script->analysis()->getCode(pc).stackDepth; /* * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go * back into the interpreter with a pending exception. This will cause * it to immediately rethrow. */ if (cx->isExceptionPending()) { JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK); JSObject *obj = script->getObject(GET_SLOTNO(pc)); Value *vp = cx->regs().sp + OBJ_BLOCK_COUNT(cx, obj); SetValueRangeToUndefined(cx->regs().sp, vp); cx->regs().sp = vp; JS_ASSERT(JSOp(pc[JSOP_ENTERBLOCK_LENGTH]) == JSOP_EXCEPTION); cx->regs().sp[0] = cx->getPendingException(); cx->clearPendingException(); cx->regs().sp++; cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH; } *f.oldregs = f.regs; return NULL; }