/* 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); }
void JS_FASTCALL stubs::EnterScript(VMFrame &f) { JSStackFrame *fp = f.fp(); JSContext *cx = f.cx; if (fp->script()->debugMode) { if (fp->isExecuteFrame()) { JSInterpreterHook hook = cx->debugHooks->executeHook; if (JS_UNLIKELY(hook != NULL)) fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData)); } else { JSInterpreterHook hook = cx->debugHooks->callHook; if (JS_UNLIKELY(hook != NULL)) fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData)); } } Probes::enterJSFun(cx, fp->maybeFun(), fp->script()); }
/* * 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 JS_FASTCALL stubs::LeaveScript(VMFrame &f) { JSStackFrame *fp = f.fp(); JSContext *cx = f.cx; Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript()); if (fp->script()->debugMode) { void *hookData; JSInterpreterHook hook = fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook; if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData())) { JSBool ok = JS_TRUE; hook(cx, fp, JS_FALSE, &ok, hookData); if (!ok) THROW(); } } }
/* * The strategy for this goes as follows: * * 1) Scan the stack, looking at all return addresses that could go into JIT * code. * 2) If an address corresponds to a call site registered by |callSite| during * the last compilation, remember it. * 3) Purge the old compiled state and return if there were no active frames of * this script on the stack. * 4) Fix up the stack by replacing all saved addresses with the addresses the * new compiler gives us for the call sites. */ bool Recompiler::recompile() { JS_ASSERT(script->hasJITCode()); Vector<PatchableAddress> normalPatches(cx); Vector<PatchableAddress> ctorPatches(cx); JSStackFrame *firstCtorFrame = NULL; JSStackFrame *firstNormalFrame = NULL; // Find all JIT'd stack frames to account for return addresses that will // need to be patched after recompilation. for (VMFrame *f = script->compartment->jaegerCompartment->activeFrame(); f != NULL; f = f->previous) { // Scan all frames owned by this VMFrame. JSStackFrame *end = f->entryfp->prev(); for (JSStackFrame *fp = f->fp(); fp != end; fp = fp->prev()) { // Remember the latest frame for each type of JIT'd code, so the // compiler will have a frame to re-JIT from. if (!firstCtorFrame && fp->script() == script && fp->isConstructing()) firstCtorFrame = fp; else if (!firstNormalFrame && fp->script() == script && !fp->isConstructing()) firstNormalFrame = fp; void **addr = fp->addressOfNativeReturnAddress(); if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { if (!ctorPatches.append(findPatch(script->jitCtor, addr))) return false; } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } void **addr = f->returnAddressLocation(); if (script->jitCtor && script->jitCtor->isValidCode(*addr)) { if (!ctorPatches.append(findPatch(script->jitCtor, addr))) return false; } else if (script->jitNormal && script->jitNormal->isValidCode(*addr)) { if (!normalPatches.append(findPatch(script->jitNormal, addr))) return false; } } Vector<CallSite> normalSites(cx); Vector<CallSite> ctorSites(cx); if (script->jitNormal && !saveTraps(script->jitNormal, &normalSites)) return false; if (script->jitCtor && !saveTraps(script->jitCtor, &ctorSites)) return false; ReleaseScriptCode(cx, script); if (normalPatches.length() && !recompile(firstNormalFrame, normalPatches, normalSites)) { return false; } if (ctorPatches.length() && !recompile(firstCtorFrame, ctorPatches, ctorSites)) { return false; } return true; }
static JSBool Exception(JSContext *cx, uintN argc, Value *vp) { JSString *message, *filename; JSStackFrame *fp; /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * NewNativeClassInstance to find the class prototype, we must get the * class prototype ourselves. */ JSObject &callee = vp[0].toObject(); Value protov; if (!callee.getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov)) return JS_FALSE; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); return JS_FALSE; } JSObject *errProto = &protov.toObject(); JSObject *obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent()); if (!obj) return JS_FALSE; /* * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ if (obj->getClass() == &js_ErrorClass) obj->setPrivate(NULL); /* Set the 'message' property. */ Value *argv = vp + 2; if (argc != 0 && !argv[0].isUndefined()) { message = js_ValueToString(cx, argv[0]); if (!message) return JS_FALSE; argv[0].setString(message); } else { message = NULL; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) return JS_FALSE; argv[1].setString(filename); fp = NULL; } else { fp = js_GetScriptedCaller(cx, NULL); if (fp) { filename = FilenameToString(cx, fp->script()->filename); if (!filename) return JS_FALSE; } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ uint32_t lineno; if (argc > 2) { if (!ValueToECMAUint32(cx, argv[2], &lineno)) return JS_FALSE; } else { if (!fp) fp = js_GetScriptedCaller(cx, NULL); lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0; } if (obj->getClass() == &js_ErrorClass && !InitExnPrivate(cx, obj, message, filename, lineno, NULL)) { return JS_FALSE; } vp->setObject(*obj); return JS_TRUE; }
static jsbytecode * FindExceptionHandler(JSContext *cx) { JSStackFrame *fp = cx->fp(); JSScript *script = fp->script(); top: if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) { // The PC is updated before every stub call, so we can use it here. unsigned offset = cx->regs->pc - script->main; JSTryNoteArray *tnarray = script->trynotes(); for (unsigned i = 0; i < tnarray->length; ++i) { JSTryNote *tn = &tnarray->vector[i]; // The following if condition actually tests two separate conditions: // (1) offset - tn->start >= tn->length // means the PC is not in the range of this try note, so we // should continue searching, after considering: // (2) offset - tn->start == tn->length // means the PC is at the first op of the exception handler // for this try note. This happens when an exception is thrown // during recording: the interpreter sets the PC to the handler // and then exits. In this case, we are in fact at the right // exception handler. // // Hypothetically, the op we are at might have thrown an // exception, in which case this would not be the right handler. // But the first ops of exception handlers generated by our // bytecode compiler cannot throw, so this is not possible. if (offset - tn->start > tn->length) continue; if (tn->stackDepth > cx->regs->sp - fp->base()) continue; jsbytecode *pc = script->main + tn->start + tn->length; JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE); JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth); switch (tn->kind) { case JSTRY_CATCH: JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENTERBLOCK); #if JS_HAS_GENERATORS /* Catch cannot intercept the closing of a generator. */ if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING))) break; #endif /* * Don't clear cx->throwing to save cx->exception from GC * until it is pushed to the stack via [exception] in the * catch block. */ return pc; case JSTRY_FINALLY: /* * Push (true, exception) pair for finally to indicate that * [retsub] should rethrow the exception. */ cx->regs->sp[0].setBoolean(true); cx->regs->sp[1] = cx->getPendingException(); cx->regs->sp += 2; cx->clearPendingException(); return pc; case JSTRY_ITER: { /* * This is similar to JSOP_ENDITER in the interpreter loop, * except the code now uses the stack slot normally used by * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2 * adjustment and regs.sp[1] after, to save and restore the * pending exception. */ Value v = cx->getPendingException(); JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENDITER); cx->clearPendingException(); ok = !!js_CloseIterator(cx, &cx->regs->sp[-1].toObject()); cx->regs->sp -= 1; if (!ok) goto top; cx->setPendingException(v); } } } } return NULL; }
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); // Call the throw hook if necessary JSThrowHook handler = f.cx->debugHooks->throwHook; if (handler) { Value rval; switch (handler(cx, cx->fp()->script(), cx->regs->pc, Jsvalify(&rval), cx->debugHooks->throwHookData)) { case JSTRAP_ERROR: cx->clearPendingException(); return NULL; case JSTRAP_RETURN: cx->clearPendingException(); cx->fp()->setReturnValue(rval); return JS_FUNC_TO_DATA_PTR(void *, cx->jaegerCompartment()->forceReturnTrampoline()); case JSTRAP_THROW: cx->setPendingException(rval); break; default: break; } } jsbytecode *pc = NULL; for (;;) { 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; JSStackFrame *fp = cx->fp(); JSScript *script = fp->script(); return script->nativeCodeForPC(fp->isConstructing(), pc); }