void ClonedBlockObject::put(JSContext *cx) { StackFrame *fp = cx->fp(); JS_ASSERT(maybeStackFrame() == js_FloatingFrameIfGenerator(cx, fp)); uint32_t count = slotCount(); uint32_t depth = stackDepth(); /* The block and its locals must be on the current stack for GC safety. */ JS_ASSERT(depth <= uint32_t(cx->regs().sp - fp->base())); JS_ASSERT(count <= uint32_t(cx->regs().sp - fp->base() - depth)); /* See comments in CheckDestructuring in frontend/Parser.cpp. */ JS_ASSERT(count >= 1); copySlotRange(RESERVED_SLOTS, fp->base() + depth, count); /* We must clear the private slot even with errors. */ setPrivate(NULL); fp->setScopeChainNoCallObj(enclosingScope()); }
static uint32 ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it) { AssertCanGC(); IonSpew(IonSpew_Bailouts, "Bailing out %s:%u, IonScript %p", it.script()->filename, it.script()->lineno, (void *) it.ionScript()); IonSpew(IonSpew_Bailouts, " reading from snapshot offset %u size %u", it.snapshotOffset(), it.ionScript()->snapshotsSize()); #ifdef DEBUG // Use count is reset after invalidation. Log use count on bailouts to // determine if we have a critical sequence of bailout. if (it.script()->ion == it.ionScript()) { IonSpew(IonSpew_Bailouts, " Current script use count is %u", it.script()->getUseCount()); } #endif SnapshotIterator iter(it); // Set a flag to avoid bailing out on every iteration or function call. Ion can // compile and run the script again after an invalidation. it.ionScript()->setBailoutExpected(); // We use OffTheBooks instead of cx because at this time we cannot iterate // on the stack safely and the reported error attempts to walk the IonMonkey // frames. We cannot iterate on the stack because we have no exit frame to // start Ion frames iteratons. BailoutClosure *br = js_new<BailoutClosure>(); if (!br) return BAILOUT_RETURN_FATAL_ERROR; activation->setBailout(br); StackFrame *fp; if (it.isEntryJSFrame() && cx->fp()->runningInIon() && activation->entryfp()) { // Avoid creating duplicate interpreter frames. This is necessary to // avoid blowing out the interpreter stack, and must be used in // conjunction with inline-OSR from within bailouts (since each Ion // activation must be tied to a unique JSStackFrame for StackIter to // work). // // Note: If the entry frame is a placeholder (a stub frame pushed for // JM -> Ion calls), then we cannot re-use it as it does not have // enough slots. JS_ASSERT(cx->fp() == activation->entryfp()); fp = cx->fp(); cx->regs().sp = fp->base(); } else { br->constructFrame(); if (!cx->stack.pushBailoutArgs(cx, it, br->argsGuard())) return BAILOUT_RETURN_FATAL_ERROR; fp = cx->stack.pushBailoutFrame(cx, it, *br->argsGuard(), br->frameGuard()); } if (!fp) return BAILOUT_RETURN_OVERRECURSED; br->setEntryFrame(fp); JSFunction *callee = it.maybeCallee(); if (callee) fp->formals()[-2].setObject(*callee); if (it.isConstructing()) fp->setConstructing(); while (true) { IonSpew(IonSpew_Bailouts, " restoring frame"); fp->initFromBailout(cx, iter); if (!iter.moreFrames()) break; iter.nextFrame(); fp = PushInlinedFrame(cx, fp); if (!fp) return BAILOUT_RETURN_OVERRECURSED; } jsbytecode *bailoutPc = fp->script()->code + iter.pcOffset(); br->setBailoutPc(bailoutPc); switch (iter.bailoutKind()) { case Bailout_Normal: return BAILOUT_RETURN_OK; case Bailout_TypeBarrier: return BAILOUT_RETURN_TYPE_BARRIER; case Bailout_Monitor: return BAILOUT_RETURN_MONITOR; case Bailout_RecompileCheck: return BAILOUT_RETURN_RECOMPILE_CHECK; case Bailout_BoundsCheck: return BAILOUT_RETURN_BOUNDS_CHECK; case Bailout_Invalidate: return BAILOUT_RETURN_INVALIDATE; case Bailout_CachedShapeGuard: return BAILOUT_RETURN_CACHED_SHAPE_GUARD; // When bailing out from an argument check, none of the code of the // function has run yet. When profiling, this means that the function // hasn't flagged its entry just yet. It has been "entered," however, so // we flag it here manually that the entry has happened. case Bailout_ArgumentCheck: fp->unsetPushedSPSFrame(); Probes::enterScript(cx, fp->script().unsafeGet(), fp->script()->function(), fp); return BAILOUT_RETURN_ARGUMENT_CHECK; } JS_NOT_REACHED("bad bailout kind"); return BAILOUT_RETURN_FATAL_ERROR; }
static jsbytecode * FindExceptionHandler(JSContext *cx) { StackFrame *fp = cx->fp(); JSScript *script = fp->script(); if (!script->hasTrynotes()) return NULL; error: if (cx->isExceptionPending()) { for (TryNoteIter tni(cx->regs()); !tni.done(); ++tni) { JSTryNote *tn = *tni; UnwindScope(cx, tn->stackDepth); /* * Set pc to the first bytecode after the the try note to point * to the beginning of catch or finally or to [enditer] closing * the for-in loop. */ jsbytecode *pc = script->main() + tn->start + tn->length; cx->regs().pc = pc; cx->regs().sp = fp->base() + tn->stackDepth; switch (tn->kind) { case JSTRY_CATCH: JS_ASSERT(JSOp(*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. */ JS_ASSERT(JSOp(*pc) == JSOP_ENDITER); bool ok = UnwindIteratorForException(cx, &cx->regs().sp[-1].toObject()); cx->regs().sp -= 1; if (!ok) goto error; } } } } else { UnwindForUncatchableException(cx, cx->regs()); } return NULL; }
static jsbytecode * FindExceptionHandler(JSContext *cx) { StackFrame *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; UnwindScope(cx, tn->stackDepth); jsbytecode *pc = script->main() + tn->start + tn->length; cx->regs().pc = pc; cx->regs().sp = fp->base() + tn->stackDepth; switch (tn->kind) { case JSTRY_CATCH: JS_ASSERT(JSOp(*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(JSOp(*pc) == JSOP_ENDITER); cx->clearPendingException(); bool 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; 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); f.regs.sp = f.fp()->base(); 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); StaticBlockObject &blockObj = script->getObject(GET_SLOTNO(pc))->asStaticBlock(); Value *vp = cx->regs().sp + blockObj.slotCount(); 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; cx->regs().fp()->setBlockChain(&blockObj); } *f.oldregs = f.regs; return NULL; }