Example #1
0
SavedStacks::FrameState::FrameState(const FrameIter &iter)
    : principals(iter.compartment()->principals),
      name(iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr),
      location()
{
    if (principals)
        JS_HoldPrincipals(principals);
}
Example #2
0
bool
SavedStacks::getLocation(JSContext *cx, const FrameIter &iter, MutableHandleLocationValue locationp)
{
    // We should only ever be caching location values for scripts in this
    // compartment. Otherwise, we would get dead cross-compartment scripts in
    // the cache because our compartment's sweep method isn't called when their
    // compartment gets collected.
    assertSameCompartment(cx, this, iter.compartment());

    // When we have a |JSScript| for this frame, use a potentially memoized
    // location from our PCLocationMap and copy it into |locationp|. 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 |locationp|'s slots directly.

    if (!iter.hasScript()) {
        if (const char16_t *displayURL = iter.scriptDisplayURL()) {
            locationp->source = AtomizeChars(cx, displayURL, js_strlen(displayURL));
        } else {
            const char *filename = iter.scriptFilename() ? iter.scriptFilename() : "";
            locationp->source = Atomize(cx, filename, strlen(filename));
        }
        if (!locationp->source)
            return false;

        locationp->line = iter.computeLine(&locationp->column);
        return true;
    }

    RootedScript script(cx, iter.script());
    jsbytecode *pc = iter.pc();

    PCKey key(script, pc);
    PCLocationMap::AddPtr p = pcLocationMap.lookupForAdd(key);

    if (!p) {
        RootedAtom source(cx);
        if (const char16_t *displayURL = iter.scriptDisplayURL()) {
            source = AtomizeChars(cx, displayURL, js_strlen(displayURL));
        } else {
            const char *filename = script->filename() ? script->filename() : "";
            source = Atomize(cx, filename, strlen(filename));
        }
        if (!source)
            return false;

        uint32_t column;
        uint32_t line = PCToLineNumber(script, pc, &column);

        LocationValue value(source, line, column);
        if (!pcLocationMap.add(p, key, value))
            return false;
    }

    locationp.set(p->value());
    return true;
}
Example #3
0
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);
}
Example #4
0
static JS::UniqueChars
FormatWasmFrame(JSContext* cx, const FrameIter& iter, JS::UniqueChars&& inBuf, int num,
                bool showArgs)
{
    JSAtom* functionDisplayAtom = iter.functionDisplayAtom();
    UniqueChars nameStr;
    if (functionDisplayAtom)
        nameStr = StringToNewUTF8CharsZ(cx, *functionDisplayAtom);

    JS::UniqueChars buf = sprintf_append(cx, Move(inBuf), "%d %s()",
                                         num,
                                         nameStr ? nameStr.get() : "<wasm-function>");
    if (!buf)
        return nullptr;
    const char* filename = iter.filename();
    uint32_t lineno = iter.computeLine();
    buf = sprintf_append(cx, Move(buf), " [\"%s\":%d]\n",
                         filename ? filename : "<unknown>",
                         lineno);

    MOZ_ASSERT(!cx->isExceptionPending());
    return buf;
}
Example #5
0
static JS::UniqueChars
FormatFrame(JSContext* cx, const FrameIter& iter, JS::UniqueChars&& inBuf, int num,
            bool showArgs, bool showLocals, bool showThisProps)
{
    MOZ_ASSERT(!cx->isExceptionPending());
    RootedScript script(cx, iter.script());
    jsbytecode* pc = iter.pc();

    RootedObject envChain(cx, iter.environmentChain(cx));
    JSAutoCompartment ac(cx, envChain);

    const char* filename = script->filename();
    unsigned lineno = PCToLineNumber(script, pc);
    RootedFunction fun(cx, iter.maybeCallee(cx));
    RootedString funname(cx);
    if (fun)
        funname = fun->displayAtom();

    RootedValue thisVal(cx);
    if (iter.hasUsableAbstractFramePtr() &&
        iter.isFunctionFrame() &&
        fun && !fun->isArrow() && !fun->isDerivedClassConstructor() &&
        !(fun->isBoundFunction() && iter.isConstructing()))
    {
        if (!GetFunctionThis(cx, iter.abstractFramePtr(), &thisVal))
            return nullptr;
    }

    // print the frame number and function name
    JS::UniqueChars buf(Move(inBuf));
    if (funname) {
        JSAutoByteString funbytes;
        char* str = funbytes.encodeLatin1(cx, funname);
        if (!str)
            return nullptr;
        buf = sprintf_append(cx, Move(buf), "%d %s(", num, str);
    } else if (fun) {
        buf = sprintf_append(cx, Move(buf), "%d anonymous(", num);
    } else {
        buf = sprintf_append(cx, Move(buf), "%d <TOP LEVEL>", num);
    }
    if (!buf)
        return nullptr;

    if (showArgs && iter.hasArgs()) {
        PositionalFormalParameterIter fi(script);
        bool first = true;
        for (unsigned i = 0; i < iter.numActualArgs(); i++) {
            RootedValue arg(cx);
            if (i < iter.numFormalArgs() && fi.closedOver()) {
                arg = iter.callObj(cx).aliasedBinding(fi);
            } else if (iter.hasUsableAbstractFramePtr()) {
                if (script->analyzedArgsUsage() &&
                    script->argsObjAliasesFormals() &&
                    iter.hasArgsObj())
                {
                    arg = iter.argsObj().arg(i);
                } else {
                    arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
                }
            } else {
                arg = MagicValue(JS_OPTIMIZED_OUT);
            }

            JSAutoByteString valueBytes;
            const char* value = FormatValue(cx, arg, valueBytes);
            if (!value) {
                if (cx->isThrowingOutOfMemory())
                    return nullptr;
                cx->clearPendingException();
            }

            JSAutoByteString nameBytes;
            const char* name = nullptr;

            if (i < iter.numFormalArgs()) {
                MOZ_ASSERT(fi.argumentSlot() == i);
                if (!fi.isDestructured()) {
                    name = nameBytes.encodeLatin1(cx, fi.name());
                    if (!name)
                        return nullptr;
                } else {
                    name = "(destructured parameter)";
                }
                fi++;
            }

            if (value) {
                buf = sprintf_append(cx, Move(buf), "%s%s%s%s%s%s",
                                     !first ? ", " : "",
                                     name ? name :"",
                                     name ? " = " : "",
                                     arg.isString() ? "\"" : "",
                                     value,
                                     arg.isString() ? "\"" : "");
                if (!buf)
                    return nullptr;

                first = false;
            } else {
                buf = sprintf_append(cx, Move(buf),
                                     "    <Failed to get argument while inspecting stack frame>\n");
                if (!buf)
                    return nullptr;

            }
        }
    }

    // print filename and line number
    buf = sprintf_append(cx, Move(buf), "%s [\"%s\":%d]\n",
                         fun ? ")" : "",
                         filename ? filename : "<unknown>",
                         lineno);
    if (!buf)
        return nullptr;


    // Note: Right now we don't dump the local variables anymore, because
    // that is hard to support across all the JITs etc.

    // print the value of 'this'
    if (showLocals) {
        if (!thisVal.isUndefined()) {
            JSAutoByteString thisValBytes;
            RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal));
            if (!thisValStr) {
                if (cx->isThrowingOutOfMemory())
                    return nullptr;
                cx->clearPendingException();
            }
            if (thisValStr) {
                const char* str = thisValBytes.encodeLatin1(cx, thisValStr);
                if (!str)
                    return nullptr;
                buf = sprintf_append(cx, Move(buf), "    this = %s\n", str);
            } else {
                buf = sprintf_append(cx, Move(buf), "    <failed to get 'this' value>\n");
            }
            if (!buf)
                return nullptr;
        }
    }

    if (showThisProps && thisVal.isObject()) {
        RootedObject obj(cx, &thisVal.toObject());

        AutoIdVector keys(cx);
        if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) {
            if (cx->isThrowingOutOfMemory())
                return nullptr;
            cx->clearPendingException();
        }

        RootedId id(cx);
        for (size_t i = 0; i < keys.length(); i++) {
            RootedId id(cx, keys[i]);
            RootedValue key(cx, IdToValue(id));
            RootedValue v(cx);

            if (!GetProperty(cx, obj, obj, id, &v)) {
                if (cx->isThrowingOutOfMemory())
                    return nullptr;
                cx->clearPendingException();
                buf = sprintf_append(cx, Move(buf),
                                     "    <Failed to fetch property while inspecting stack frame>\n");
                if (!buf)
                    return nullptr;
                continue;
            }

            JSAutoByteString nameBytes;
            const char* name = FormatValue(cx, key, nameBytes);
            if (!name) {
                if (cx->isThrowingOutOfMemory())
                    return nullptr;
                cx->clearPendingException();
            }

            JSAutoByteString valueBytes;
            const char* value = FormatValue(cx, v, valueBytes);
            if (!value) {
                if (cx->isThrowingOutOfMemory())
                    return nullptr;
                cx->clearPendingException();
            }

            if (name && value) {
                buf = sprintf_append(cx, Move(buf), "    this.%s = %s%s%s\n",
                                     name,
                                     v.isString() ? "\"" : "",
                                     value,
                                     v.isString() ? "\"" : "");
            } else {
                buf = sprintf_append(cx, Move(buf),
                                     "    <Failed to format values while inspecting stack frame>\n");
            }
            if (!buf)
                return nullptr;
        }
    }

    MOZ_ASSERT(!cx->isExceptionPending());
    return buf;
}
Example #6
0
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;
}
Example #7
0
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;
}