/*
 * Given a frame newer than the entry frame, try to finish it. If it's at a
 * return position, pop the frame. If it's at a safe point, execute it in
 * Jaeger code. Otherwise, try to interpret until a safe point.
 *
 * While this function is guaranteed to make progress, it may not actually
 * finish or pop the current frame. It can either:
 *   1) Finalize a finished frame, or
 *   2) Finish and finalize the frame in the Method JIT, or
 *   3) Interpret, which can:
 *     a) Propagate an error, or
 *     b) Finish the frame, but not finalize it, or
 *     c) Abruptly leave at any point in the frame, or in a newer frame
 *        pushed by a call, that has method JIT'd code.
 */
static bool
EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame)
{
    JSContext *cx = f.cx;
    JSStackFrame *fp = cx->fp();

    /*
     * A "finished" frame is when the interpreter rested on a STOP,
     * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
     * for a safe point. If the frame was finished, we could have already
     * called ScriptEpilogue(), and entering the JIT could call it twice.
     */
    if (!fp->hasImacropc() && FrameIsFinished(cx))
        return HandleFinishedFrame(f, entryFrame);

    if (void *ncode = AtSafePoint(cx)) {
        if (!JaegerShotAtSafePoint(cx, ncode))
            return false;
        InlineReturn(f);
        AdvanceReturnPC(cx);
        return true;
    }

    return PartialInterpret(f);
}
/* Returns whether the current PC has method JIT'd code. */
static inline void *
AtSafePoint(JSContext *cx)
{
    JSStackFrame *fp = cx->fp();
    if (fp->hasImacropc())
        return false;

    JSScript *script = fp->script();
    return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc);
}
/*
 * 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;
    JSStackFrame *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, 0, JSINTERP_SAFEPOINT);

    return ok;
}
void *
RunTracer(VMFrame &f)
#endif
{
    JSContext *cx = f.cx;
    JSStackFrame *entryFrame = f.fp();
    TracePointAction tpa;

    /* :TODO: nuke PIC? */
    if (!cx->traceJitEnabled)
        return NULL;

    /*
     * Force initialization of the entry frame's scope chain and return value,
     * if necessary.  The tracer can query the scope chain without needing to
     * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the
     * correct return value stored if we trace/interpret through to the end
     * of the frame.
     */
    entryFrame->scopeChain();
    entryFrame->returnValue();

    bool blacklist;
    uintN inlineCallCount = 0;
    void **traceData;
    uintN *traceEpoch;
    uint32 *loopCounter;
    uint32 hits;
#if JS_MONOIC
    traceData = &ic.traceData;
    traceEpoch = &ic.traceEpoch;
    loopCounter = &ic.loopCounter;
    *loopCounter = 1;
    hits = ic.loopCounterStart;
#else
    traceData = NULL;
    traceEpoch = NULL;
    loopCounter = NULL;
    hits = 1;
#endif
    tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch,
                            loopCounter, hits);
    JS_ASSERT(!TRACE_RECORDER(cx));

#if JS_MONOIC
    ic.loopCounterStart = *loopCounter;
    if (blacklist)
        DisableTraceHint(entryFrame->jit(), ic);
#endif

    // Even though ExecuteTree() bypasses the interpreter, it should propagate
    // error failures correctly.
    JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error);

	f.fp() = cx->fp();
    JS_ASSERT(f.fp() == cx->fp());
    switch (tpa) {
      case TPA_Nothing:
        return NULL;

      case TPA_Error:
        if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter()))
            THROWV(NULL);
        JS_ASSERT(!cx->fp()->hasImacropc());
        break;

      case TPA_RanStuff:
      case TPA_Recorded:
        break;
    }

    /*
     * The tracer could have dropped us off on any frame at any position.
     * Well, it could not have removed frames (recursion is disabled).
     *
     * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint()
     * unless each is at a safe point. We can JaegerShotAtSafePoint these
     * frames individually, but we must unwind to the entryFrame.
     *
     * Note carefully that JaegerShotAtSafePoint can resume methods at
     * arbitrary safe points whereas JaegerShot cannot.
     *
     * If we land on entryFrame without a safe point in sight, we'll end up
     * at the RETURN op. This is an edge case with two paths:
     *
     * 1) The entryFrame is the last inline frame. If it fell on a RETURN,
     *    move the return value down.
     * 2) The entryFrame is NOT the last inline frame. Pop the frame.
     *
     * In both cases, we hijack the stub to return to the force-return
     * trampoline. This trampoline simulates the frame-popping portion of
     * emitReturn (except without the benefit of the FrameState) and will
     * produce the necessary register state to return to the caller.
     */

  restart:
    /* Step 1. Finish frames created after the entry frame. */
    if (!FinishExcessFrames(f, entryFrame))
        THROWV(NULL);

    /* IMacros are guaranteed to have been removed by now. */
    JS_ASSERT(f.fp() == entryFrame);
    JS_ASSERT(!entryFrame->hasImacropc());

    /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
    if (FrameIsFinished(cx)) {
        if (!HandleFinishedFrame(f, entryFrame))
            THROWV(NULL);

        void *retPtr = JS_FUNC_TO_DATA_PTR(void *,
                       cx->jaegerCompartment()->forceReturnTrampoline());
        *f.returnAddressLocation() = retPtr;
        return NULL;
    }

    /* Step 3. If entryFrame is at a safe point, just leave. */
    if (void *ncode = AtSafePoint(cx))
        return ncode;

    /* Step 4. Do a partial interp, then restart the whole process. */
    if (!PartialInterpret(f)) {
        if (!HandleErrorInExcessFrame(f, entryFrame))
            THROWV(NULL);
    }

    goto restart;
}
/*
 * 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, JSStackFrame *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.
     */
    JSStackFrame *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;
}