JSObject* js::Allocate(ExclusiveContext* cx, AllocKind kind, size_t nDynamicSlots, InitialHeap heap, const Class* clasp) { static_assert(mozilla::IsConvertible<T*, JSObject*>::value, "must be JSObject derived"); MOZ_ASSERT(IsObjectAllocKind(kind)); size_t thingSize = Arena::thingSize(kind); MOZ_ASSERT(thingSize == Arena::thingSize(kind)); MOZ_ASSERT(thingSize >= sizeof(JSObject_Slots0)); static_assert(sizeof(JSObject_Slots0) >= CellSize, "All allocations must be at least the allocator-imposed minimum size."); // Off-main-thread alloc cannot trigger GC or make runtime assertions. if (!cx->isJSContext()) return GCRuntime::tryNewTenuredObject<NoGC>(cx, kind, thingSize, nDynamicSlots); JSContext* ncx = cx->asJSContext(); JSRuntime* rt = ncx->runtime(); if (!rt->gc.checkAllocatorState<allowGC>(ncx, kind)) return nullptr; if (ncx->nursery().isEnabled() && heap != TenuredHeap) { JSObject* obj = rt->gc.tryNewNurseryObject<allowGC>(ncx, thingSize, nDynamicSlots, clasp); if (obj) return obj; // Our most common non-jit allocation path is NoGC; thus, if we fail the // alloc and cannot GC, we *must* return nullptr here so that the caller // will do a CanGC allocation to clear the nursery. Failing to do so will // cause all allocations on this path to land in Tenured, and we will not // get the benefit of the nursery. if (!allowGC) return nullptr; } return GCRuntime::tryNewTenuredObject<allowGC>(cx, kind, thingSize, nDynamicSlots); }
/* * Since memory has been exhausted, avoid the normal error-handling path which * allocates an error object, report and callstack. If code is running, simply * throw the static atom "out of memory". If code is not running, call the * error reporter directly. * * Furthermore, callers of js_ReportOutOfMemory (viz., malloc) assume a GC does * not occur, so GC must be avoided or suppressed. */ void js_ReportOutOfMemory(ThreadSafeContext *cxArg) { #ifdef JS_MORE_DETERMINISTIC /* * OOMs are non-deterministic, especially across different execution modes * (e.g. interpreter vs JIT). In more-deterministic builds, print to stderr * so that the fuzzers can detect this. */ fprintf(stderr, "js_ReportOutOfMemory called\n"); #endif if (cxArg->isForkJoinContext()) { cxArg->asForkJoinContext()->setPendingAbortFatal(ParallelBailoutOutOfMemory); return; } if (!cxArg->isJSContext()) return; JSContext *cx = cxArg->asJSContext(); cx->runtime()->hadOutOfMemory = true; /* Report the oom. */ if (JS::OutOfMemoryCallback oomCallback = cx->runtime()->oomCallback) { AutoSuppressGC suppressGC(cx); oomCallback(cx, cx->runtime()->oomCallbackData); } if (JS_IsRunning(cx)) { cx->setPendingException(StringValue(cx->names().outOfMemory)); return; } /* Get the message for this error, but we don't expand any arguments. */ const JSErrorFormatString *efs = js_GetLocalizedErrorMessage(cx, nullptr, nullptr, JSMSG_OUT_OF_MEMORY); const char *msg = efs ? efs->format : "Out of memory"; /* Fill out the report, but don't do anything that requires allocation. */ JSErrorReport report; PodZero(&report); report.flags = JSREPORT_ERROR; report.errorNumber = JSMSG_OUT_OF_MEMORY; PopulateReportBlame(cx, &report); /* Report the error. */ if (JSErrorReporter onError = cx->errorReporter) { AutoSuppressGC suppressGC(cx); onError(cx, msg, &report); } /* * We would like to enforce the invariant that any exception reported * during an OOM situation does not require wrapping. Besides avoiding * allocation when memory is low, this reduces the number of places where * we might need to GC. * * When JS code is running, we set the pending exception to an atom, which * does not need wrapping. If no JS code is running, no exception should be * set at all. */ JS_ASSERT(!cx->isExceptionPending()); }
uint32_t jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo) { JSContext* cx = GetJSContextFromJitCode(); MOZ_ASSERT(bailoutInfo); // We don't have an exit frame. MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) && IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000), "Fake jitTop pointer should be within the first page."); cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); MOZ_ASSERT(!iter.ionScript()->invalidated()); CommonFrameLayout* currentFramePtr = iter.current(); TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger_Bailout); JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo, /* excInfo = */ nullptr); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); if (retval != BAILOUT_RETURN_OK) { JSScript* script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), /* popSPSFrame = */ false); EnsureExitFrame(iter.jsFrame()); } // This condition was wrong when we entered this bailout function, but it // might be true now. A GC might have reclaimed all the Jit code and // invalidated all frames which are currently on the stack. As we are // already in a bailout, we could not switch to an invalidation // bailout. When the code of an IonScript which is on the stack is // invalidated (see InvalidateActivation), we remove references to it and // increment the reference counter for each activation that appear on the // stack. As the bailed frame is one of them, we have to decrement it now. if (iter.ionScript()->invalidated()) iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); // NB: Commentary on how |lastProfilingFrame| is set from bailouts. // // Once we return to jitcode, any following frames might get clobbered, // but the current frame will not (as it will be clobbered "in-place" // with a baseline frame that will share the same frame prefix). // However, there may be multiple baseline frames unpacked from this // single Ion frame, which means we will need to once again reset // |lastProfilingFrame| to point to the correct unpacked last frame // in |FinishBailoutToBaseline|. // // In the case of error, the jitcode will jump immediately to an // exception handler, which will unwind the frames and properly set // the |lastProfilingFrame| to point to the frame being resumed into // (see |AutoResetLastProfilerFrameOnReturnFromException|). // // In both cases, we want to temporarily set the |lastProfilingFrame| // to the current frame being bailed out, and then fix it up later. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr); return retval; }
uint32_t jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut, BaselineBailoutInfo** bailoutInfo) { sp->checkInvariants(); JSContext* cx = GetJSContextFromJitCode(); // We don't have an exit frame. cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); CommonFrameLayout* currentFramePtr = iter.current(); TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger_Invalidation); JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.frameSize(); MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo, /* excInfo = */ nullptr); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); if (retval != BAILOUT_RETURN_OK) { // If the bailout failed, then bailout trampoline will pop the // current frame and jump straight to exception handling code when // this function returns. Any SPS entry pushed for this frame will // be silently forgotten. // // We call ExitScript here to ensure that if the ionScript had SPS // instrumentation, then the SPS entry for it is popped. // // However, if the bailout was during argument check, then a // pseudostack frame would not have been pushed in the first // place, so don't pop anything in that case. JSScript* script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), /* popSPSFrame = */ false); JitFrameLayout* frame = iter.jsFrame(); JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s): converting to exit frame", (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion"); JitSpew(JitSpew_IonInvalidate, " orig calleeToken %p", (void*) frame->calleeToken()); JitSpew(JitSpew_IonInvalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); JitSpew(JitSpew_IonInvalidate, " orig ra %p", (void*) frame->returnAddress()); frame->replaceCalleeToken(nullptr); EnsureExitFrame(frame); JitSpew(JitSpew_IonInvalidate, " new calleeToken %p", (void*) frame->calleeToken()); JitSpew(JitSpew_IonInvalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); JitSpew(JitSpew_IonInvalidate, " new ra %p", (void*) frame->returnAddress()); } iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); // Make the frame being bailed out the top profiled frame. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr); return retval; }
uint32_t jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, BaselineBailoutInfo **bailoutInfo) { sp->checkInvariants(); JSContext *cx = GetJSContextFromJitCode(); // We don't have an exit frame. cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; gc::AutoSuppressGC suppress(cx); JitActivationIterator jitActivations(cx->runtime()); IonBailoutIterator iter(jitActivations, sp); JitActivation *activation = jitActivations->asJit(); TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger::Invalidation); 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(); JS_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); JS_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); if (retval != BAILOUT_RETURN_OK) { // If the bailout failed, then bailout trampoline will pop the // current frame and jump straight to exception handling code when // this function returns. Any SPS entry pushed for this frame will // be silently forgotten. // // We call ExitScript here to ensure that if the ionScript had SPS // instrumentation, then the SPS entry for it is popped. JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), iter.ionScript()->hasSPSInstrumentation()); IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "Bailout failed (%s): converting to exit frame", (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion"); 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(nullptr); 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; }
void FrameIter::settleOnActivation() { while (true) { if (data_.activations_.done()) { data_.state_ = DONE; return; } Activation *activation = data_.activations_.activation(); // If JS_SaveFrameChain was called, stop iterating here (unless // GO_THROUGH_SAVED is set). if (data_.savedOption_ == STOP_AT_SAVED && activation->hasSavedFrameChain()) { data_.state_ = DONE; return; } // Skip activations from another context if needed. JS_ASSERT(activation->cx()); JS_ASSERT(data_.cx_); if (data_.contextOption_ == CURRENT_CONTEXT && activation->cx() != data_.cx_) { ++data_.activations_; continue; } // If the caller supplied principals, only show activations which are subsumed (of the same // origin or of an origin accessible) by these principals. if (data_.principals_) { JSContext *cx = data_.cx_->asJSContext(); if (JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes) { if (!subsumes(data_.principals_, activation->compartment()->principals)) { ++data_.activations_; continue; } } } #ifdef JS_ION if (activation->isJit()) { data_.jitFrames_ = jit::JitFrameIterator(data_.activations_); // Stop at the first scripted frame. while (!data_.jitFrames_.isScripted() && !data_.jitFrames_.done()) ++data_.jitFrames_; // It's possible to have an JitActivation with no scripted frames, // for instance if we hit an over-recursion during bailout. if (data_.jitFrames_.done()) { ++data_.activations_; continue; } nextJitFrame(); data_.state_ = JIT; return; } if (activation->isAsmJS()) { data_.asmJSFrames_ = AsmJSFrameIterator(data_.activations_->asAsmJS()); if (data_.asmJSFrames_.done()) { ++data_.activations_; continue; } data_.state_ = ASMJS; return; } // ForkJoin activations don't contain iterable frames, so skip them. if (activation->isForkJoin()) { ++data_.activations_; continue; } #endif JS_ASSERT(activation->isInterpreter()); InterpreterActivation *interpAct = activation->asInterpreter(); data_.interpFrames_ = InterpreterFrameIterator(interpAct); // If we OSR'ed into JIT code, skip the interpreter frame so that // the same frame is not reported twice. if (data_.interpFrames_.frame()->runningInJit()) { ++data_.interpFrames_; if (data_.interpFrames_.done()) { ++data_.activations_; continue; } } JS_ASSERT(!data_.interpFrames_.frame()->runningInJit()); data_.pc_ = data_.interpFrames_.pc(); data_.state_ = INTERP; return; } }
bool BaselineFrame::initForOsr(InterpreterFrame *fp, uint32_t numStackValues) { mozilla::PodZero(this); scopeChain_ = fp->scopeChain(); if (fp->hasCallObjUnchecked()) flags_ |= BaselineFrame::HAS_CALL_OBJ; if (fp->isEvalFrame()) { flags_ |= BaselineFrame::EVAL; evalScript_ = fp->script(); } if (fp->script()->needsArgsObj() && fp->hasArgsObj()) { flags_ |= BaselineFrame::HAS_ARGS_OBJ; argsObj_ = &fp->argsObj(); } if (fp->hasReturnValue()) setReturnValue(fp->returnValue()); // If the interpreter pushed an SPS frame when it entered the function, the // interpreter will pop it after the OSR trampoline returns. In order for // the Baseline frame to have its SPS flag set, it must have its own SPS // frame, which the Baseline code will pop on return. Note that the // profiler may have been enabled or disabled after the function was entered // but before OSR. JSContext *cx = GetJSContextFromJitCode(); SPSProfiler *p = &(cx->runtime()->spsProfiler); if (p->enabled()) { p->enter(fp->script(), fp->maybeFun()); flags_ |= BaselineFrame::HAS_PUSHED_SPS_FRAME; } frameSize_ = BaselineFrame::FramePointerOffset + BaselineFrame::Size() + numStackValues * sizeof(Value); MOZ_ASSERT(numValueSlots() == numStackValues); for (uint32_t i = 0; i < numStackValues; i++) *valueSlot(i) = fp->slots()[i]; if (cx->compartment()->debugMode()) { // In debug mode, update any Debugger.Frame objects for the // InterpreterFrame to point to the BaselineFrame. // The caller pushed a fake return address. ScriptFrameIter, used by the // debugger, wants a valid return address, but it's okay to just pick one. // In debug mode there's always at least 1 ICEntry (since there are always // debug prologue/epilogue calls). JitFrameIterator iter(cx); MOZ_ASSERT(iter.returnAddress() == nullptr); BaselineScript *baseline = fp->script()->baselineScript(); iter.current()->setReturnAddress(baseline->returnAddressForIC(baseline->icEntry(0))); if (!Debugger::handleBaselineOsr(cx, fp, this)) return false; } return true; }
bool js::XDRAtom(XDRState<mode> *xdr, MutableHandleAtom atomp) { if (mode == XDR_ENCODE) { static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits"); uint32_t length = atomp->length(); uint32_t lengthAndEncoding = (length << 1) | uint32_t(atomp->hasLatin1Chars()); if (!xdr->codeUint32(&lengthAndEncoding)) return false; JS::AutoCheckCannotGC nogc; return atomp->hasLatin1Chars() ? xdr->codeChars(atomp->latin1Chars(nogc), length) : xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length); } /* Avoid JSString allocation for already existing atoms. See bug 321985. */ uint32_t lengthAndEncoding; if (!xdr->codeUint32(&lengthAndEncoding)) return false; uint32_t length = lengthAndEncoding >> 1; bool latin1 = lengthAndEncoding & 0x1; JSContext *cx = xdr->cx(); JSAtom *atom; if (latin1) { const Latin1Char *chars = reinterpret_cast<const Latin1Char *>(xdr->buf.read(length)); atom = AtomizeChars(cx, chars, length); } else { #if IS_LITTLE_ENDIAN /* Directly access the little endian chars in the XDR buffer. */ const char16_t *chars = reinterpret_cast<const char16_t *>(xdr->buf.read(length * sizeof(char16_t))); atom = AtomizeChars(cx, chars, length); #else /* * We must copy chars to a temporary buffer to convert between little and * big endian data. */ char16_t *chars; char16_t stackChars[256]; if (length <= ArrayLength(stackChars)) { chars = stackChars; } else { /* * This is very uncommon. Don't use the tempLifoAlloc arena for this as * most allocations here will be bigger than tempLifoAlloc's default * chunk size. */ chars = cx->runtime()->pod_malloc<char16_t>(length); if (!chars) return false; } JS_ALWAYS_TRUE(xdr->codeChars(chars, length)); atom = AtomizeChars(cx, chars, length); if (chars != stackChars) js_free(chars); #endif /* !IS_LITTLE_ENDIAN */ } if (!atom) return false; atomp.set(atom); return true; }
~SuppressErrorsGuard() { JS_SetErrorReporter(cx->runtime(), prevReporter); }
explicit SuppressErrorsGuard(JSContext* cx) : cx(cx), prevReporter(JS_SetErrorReporter(cx->runtime(), nullptr)), prevState(cx) {}
uint32_t jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) { JSContext *cx = GetJSContextFromJitCode(); MOZ_ASSERT(bailoutInfo); // We don't have an exit frame. MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) && IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000), "Fake jitTop pointer should be within the first page."); cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); MOZ_ASSERT(!iter.ionScript()->invalidated()); TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger::Bailout); JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); MOZ_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; bool poppedLastSPSFrame = false; uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo, /* excInfo = */ nullptr, &poppedLastSPSFrame); MOZ_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); if (retval != BAILOUT_RETURN_OK) { // If the bailout failed, then bailout trampoline will pop the // current frame and jump straight to exception handling code when // this function returns. Any SPS entry pushed for this frame will // be silently forgotten. // // We call ExitScript here to ensure that if the ionScript had SPS // instrumentation, then the SPS entry for it is popped. // // However, if the bailout was during argument check, then a // pseudostack frame would not have been pushed in the first // place, so don't pop anything in that case. bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() && (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) && !poppedLastSPSFrame; JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); EnsureExitFrame(iter.jsFrame()); } // This condition was wrong when we entered this bailout function, but it // might be true now. A GC might have reclaimed all the Jit code and // invalidated all frames which are currently on the stack. As we are // already in a bailout, we could not switch to an invalidation // bailout. When the code of an IonScript which is on the stack is // invalidated (see InvalidateActivation), we remove references to it and // increment the reference counter for each activation that appear on the // stack. As the bailed frame is one of them, we have to decrement it now. if (iter.ionScript()->invalidated()) iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); return retval; }