void LiveSavedFrameCache::find(JSContext* cx, FrameIter& frameIter, MutableHandleSavedFrame frame) const { MOZ_ASSERT(initialized()); MOZ_ASSERT(!frameIter.done()); MOZ_ASSERT(frameIter.hasCachedSavedFrame()); Maybe<FramePtr> maybeFramePtr = getFramePtr(frameIter); MOZ_ASSERT(maybeFramePtr.isSome()); FramePtr framePtr(*maybeFramePtr); jsbytecode* pc = frameIter.pc(); size_t numberStillValid = 0; frame.set(nullptr); for (auto* p = frames->begin(); p < frames->end(); p++) { numberStillValid++; if (framePtr == p->framePtr && pc == p->pc) { frame.set(p->savedFrame); break; } } if (!frame) { frames->clear(); return; } MOZ_ASSERT(0 < numberStillValid && numberStillValid <= frames->length()); if (frame->compartment() != cx->compartment()) { frame.set(nullptr); numberStillValid--; } // Everything after the cached SavedFrame are stale younger frames we have // since popped. frames->shrinkBy(frames->length() - numberStillValid); }
bool SavedStacks::insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFrame frame, unsigned maxFrameCount) { // In order to lookup a cached SavedFrame object, we need to have its parent // SavedFrame, which means we need to walk the stack from oldest frame to // youngest. However, FrameIter walks the stack from youngest frame to // oldest. The solution is to append stack frames to a vector as we walk the // stack with FrameIter, and then do a second pass through that vector in // reverse order after the traversal has completed and get or create the // SavedFrame objects at that time. // // To avoid making many copies of FrameIter (whose copy constructor is // relatively slow), we save the subset of FrameIter's data that is relevant // to our needs in a FrameState object, and maintain a vector of FrameState // objects instead of a vector of FrameIter objects. // Accumulate the vector of FrameState objects in |stackState|. AutoFrameStateVector stackState(cx); while (!iter.done()) { AutoLocationValueRooter location(cx); { AutoCompartment ac(cx, iter.compartment()); if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location)) return false; } { FrameState frameState(iter); frameState.location = location.get(); if (!stackState->append(frameState)) return false; } ++iter; if (maxFrameCount == 0) { // If maxFrameCount is zero, then there's no limit on the number of // frames. continue; } else if (maxFrameCount == 1) { // Since we were only asked to save one frame, do not continue // walking the stack and saving frame state. break; } else { maxFrameCount--; } } // Iterate through |stackState| in reverse order and get or create the // actual SavedFrame instances. RootedSavedFrame parentFrame(cx, nullptr); for (size_t i = stackState->length(); i != 0; i--) { SavedFrame::AutoLookupRooter lookup(cx, stackState[i-1].location.source, stackState[i-1].location.line, stackState[i-1].location.column, stackState[i-1].name, parentFrame, stackState[i-1].principals); parentFrame.set(getOrCreateSavedFrame(cx, lookup)); if (!parentFrame) return false; } frame.set(parentFrame); return true; }
bool SavedStacks::insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFrame frame, unsigned maxFrameCount) { if (iter.done()) { frame.set(nullptr); return true; } // Don't report the over-recursion error because if we are blowing the stack // here, we already blew the stack in JS, reported it, and we are creating // the saved stack for the over-recursion error object. We do this check // here, rather than inside saveCurrentStack, because in some cases we will // pass the check there, despite later failing the check here (for example, // in js/src/jit-test/tests/saved-stacks/bug-1006876-too-much-recursion.js). JS_CHECK_RECURSION_DONT_REPORT(cx, return false); JSPrincipals* principals = iter.compartment()->principals; RootedAtom name(cx, iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr); // When we have a |JSScript| for this frame, use |getLocation| to get a // potentially memoized location result and copy it into |location|. When we // do not have a |JSScript| for this frame (asm.js frames), we take a slow // path that doesn't employ memoization, and update |location|'s slots // directly. AutoLocationValueRooter location(cx); if (iter.hasScript()) { JSScript *script = iter.script(); jsbytecode *pc = iter.pc(); { AutoCompartment ac(cx, iter.compartment()); if (!cx->compartment()->savedStacks().getLocation(cx, script, pc, &location)) return false; } } else { const char *filename = iter.scriptFilename(); if (!filename) filename = ""; location.get().source = Atomize(cx, filename, strlen(filename)); if (!location.get().source) return false; uint32_t column; location.get().line = iter.computeLine(&column); location.get().column = column; } RootedSavedFrame parentFrame(cx); // If maxFrameCount is zero, then there's no limit on the number of frames. if (maxFrameCount == 0) { if (!insertFrames(cx, ++iter, &parentFrame, 0)) return false; } else if (maxFrameCount == 1) { // Since we were only asked to save one frame, the SavedFrame we're // building here should have no parent, even if there are older frames // on the stack. parentFrame = nullptr; } else { if (!insertFrames(cx, ++iter, &parentFrame, maxFrameCount - 1)) return false; } SavedFrame::AutoLookupRooter lookup(cx, location.get().source, location.get().line, location.get().column, name, parentFrame, principals); frame.set(getOrCreateSavedFrame(cx, lookup)); return frame.get() != nullptr; }