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; }