static bool ProcessLine(AutoJSAPI& jsapi, const char* buffer, int startline) { JSContext* cx = jsapi.cx(); JS::RootedScript script(cx); JS::RootedValue result(cx); JS::CompileOptions options(cx); options.setFileAndLine("typein", startline) .setIsRunOnce(true); if (!JS_CompileScript(cx, buffer, strlen(buffer), options, &script)) return false; if (compileOnly) return true; if (!JS_ExecuteScript(cx, script, &result)) return false; if (result.isUndefined()) return true; RootedString str(cx); if (!(str = ToString(cx, result))) return false; JSAutoByteString bytes; if (!bytes.encodeLatin1(cx, str)) return false; fprintf(gOutFile, "%s\n", bytes.ptr()); return true; }
static JSBool DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp) { const char *fileName = NULL; JSAutoByteString fileNameBytes; if (argc > 0) { Value v = JS_ARGV(cx, vp)[0]; if (v.isString()) { JSString *str = v.toString(); if (!fileNameBytes.encodeLatin1(cx, str)) return false; fileName = fileNameBytes.ptr(); } } FILE *dumpFile; if (!fileName) { dumpFile = stdout; } else { dumpFile = fopen(fileName, "w"); if (!dumpFile) { JS_ReportError(cx, "can't open %s", fileName); return false; } } js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile); fclose(dumpFile); JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; }
static void ThrowBadArg(JSContext *cx, nsresult rv, const char *ifaceName, jsid memberId, const char *memberName, unsigned paramnum) { /* Only one memberId or memberName should be given. */ MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName); // From XPCThrower::ThrowBadParam. char* sz; const char* format; if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format)) format = ""; JSAutoByteString memberNameBytes; if (!memberName) { memberName = JSID_IS_STRING(memberId) ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId)) : "unknown"; } sz = JS_smprintf("%s arg %u [%s.%s]", format, (unsigned int) paramnum, ifaceName, memberName); dom::Throw(cx, rv, sz); if (sz) JS_smprintf_free(sz); }
static bool DumpHeapComplete(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); DumpHeapNurseryBehaviour nurseryBehaviour = js::IgnoreNurseryObjects; FILE *dumpFile = nullptr; unsigned i = 0; if (argc > i) { Value v = args[i]; if (v.isString()) { JSString *str = v.toString(); bool same = false; if (!JS_StringEqualsAscii(cx, str, "collectNurseryBeforeDump", &same)) return false; if (same) { nurseryBehaviour = js::CollectNurseryBeforeDump; ++i; } } } if (argc > i) { Value v = args[i]; if (v.isString()) { if (!fuzzingSafe) { JSString *str = v.toString(); JSAutoByteString fileNameBytes; if (!fileNameBytes.encodeLatin1(cx, str)) return false; const char *fileName = fileNameBytes.ptr(); dumpFile = fopen(fileName, "w"); if (!dumpFile) { JS_ReportError(cx, "can't open %s", fileName); return false; } } ++i; } } if (i != argc) { JS_ReportError(cx, "bad arguments passed to dumpHeapComplete"); return false; } js::DumpHeapComplete(JS_GetRuntime(cx), dumpFile ? dumpFile : stdout, nurseryBehaviour); if (dumpFile) fclose(dumpFile); JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; }
bool ModuleBuilder::appendLocalExportEntry(HandleExportEntryObject exp) { if (!module_->initialEnvironment().lookup(cx_, AtomToId(exp->localName()))) { JSAutoByteString str; str.encodeLatin1(cx_, exp->localName()); JS_ReportErrorNumber(cx_, GetErrorMessage, nullptr, JSMSG_MISSING_EXPORT, str.ptr()); return false; } return localExportEntries_.append(exp); }
static bool ThrowCallFailed(JSContext *cx, nsresult rv, const char *ifaceName, HandleId memberId, const char *memberName) { /* Only one of memberId or memberName should be given. */ MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName); // From XPCThrower::ThrowBadResult. char* sz; const char* format; const char* name; // If the cx already has a pending exception, just throw that. // // We used to check here to make sure the exception matched rv (whatever // that means). But this meant that we'd be calling into JSAPI below with // a pending exception, which isn't really kosher. The first exception thrown // should generally take precedence anyway. if (JS_IsExceptionPending(cx)) return false; // else... if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nullptr, &format) || !format) { format = ""; } JSAutoByteString memberNameBytes; if (!memberName) { memberName = JSID_IS_STRING(memberId) ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId)) : "unknown"; } if (nsXPCException::NameAndFormatForNSResult(rv, &name, nullptr) && name) { sz = JS_smprintf("%s 0x%x (%s) [%s.%s]", format, rv, name, ifaceName, memberName); } else { sz = JS_smprintf("%s 0x%x [%s.%s]", format, rv, ifaceName, memberName); } dom::Throw(cx, rv, sz); if (sz) JS_smprintf_free(sz); return false; }
static bool ThrowCallFailed(JSContext *cx, nsresult rv, const char *ifaceName, HandleId memberId, const char *memberName) { /* Only one of memberId or memberName should be given. */ MOZ_ASSERT(JSID_IS_VOID(memberId) != !memberName); // From XPCThrower::ThrowBadResult. char* sz; const char* format; const char* name; /* * If there is a pending exception when the native call returns and * it has the same error result as returned by the native call, then * the native call may be passing through an error from a previous JS * call. So we'll just throw that exception into our JS. */ if (XPCThrower::CheckForPendingException(rv, cx)) return false; // else... if (!nsXPCException::NameAndFormatForNSResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE, nullptr, &format) || !format) { format = ""; } JSAutoByteString memberNameBytes; if (!memberName) { memberName = JSID_IS_STRING(memberId) ? memberNameBytes.encodeLatin1(cx, JSID_TO_STRING(memberId)) : "unknown"; } if (nsXPCException::NameAndFormatForNSResult(rv, &name, nullptr) && name) { sz = JS_smprintf("%s 0x%x (%s) [%s.%s]", format, rv, name, ifaceName, memberName); } else { sz = JS_smprintf("%s 0x%x [%s.%s]", format, rv, ifaceName, memberName); } XPCThrower::BuildAndThrowException(cx, rv, sz); if (sz) JS_smprintf_free(sz); return false; }
JSErrorReport* js::ErrorObject::getOrCreateErrorReport(JSContext* cx) { if (JSErrorReport* r = getErrorReport()) return r; // We build an error report on the stack and then use CopyErrorReport to do // the nitty-gritty malloc stuff. JSErrorReport report; // Type. JSExnType type_ = type(); report.exnType = type_; // Filename. JSAutoByteString filenameStr; if (!filenameStr.encodeLatin1(cx, fileName(cx))) return nullptr; report.filename = filenameStr.ptr(); // Coordinates. report.lineno = lineNumber(); report.column = columnNumber(); // Message. Note that |new Error()| will result in an undefined |message| // slot, so we need to explicitly substitute the empty string in that case. RootedString message(cx, getMessage()); if (!message) message = cx->runtime()->emptyString; if (!message->ensureFlat(cx)) return nullptr; UniquePtr<char[], JS::FreePolicy> utf8 = StringToNewUTF8CharsZ(cx, *message); if (!utf8) return nullptr; report.initOwnedMessage(utf8.release()); // Cache and return. JSErrorReport* copy = CopyErrorReport(cx, &report); if (!copy) return nullptr; setReservedSlot(ERROR_REPORT_SLOT, PrivateValue(copy)); return copy; }
// static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, bool own) { char* sz = nullptr; if (ccx.HasInterfaceAndMember()) { XPCNativeInterface* iface = ccx.GetInterface(); jsid id = ccx.GetMember()->GetName(); JSAutoByteString bytes; const char* name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id)); if (!name) { name = ""; } sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name); } if (sz) { if (own) JS_smprintf_free(*psz); *psz = sz; } }
void XPCShellEnvironment::ProcessFile(JSContext *cx, JS::Handle<JSObject*> obj, const char *filename, FILE *file, bool forceTTY) { XPCShellEnvironment* env = this; JS::Rooted<JS::Value> result(cx); int lineno, startline; bool ok, hitEOF; char *bufp, buffer[4096]; JSString *str; if (forceTTY) { file = stdin; } else if (!isatty(fileno(file))) { /* * It's not interactive - just execute it. * * Support the UNIX #! shell hack; gobble the first line if it starts * with '#'. TODO - this isn't quite compatible with sharp variables, * as a legal js program (using sharp variables) might start with '#'. * But that would require multi-character lookahead. */ int ch = fgetc(file); if (ch == '#') { while((ch = fgetc(file)) != EOF) { if(ch == '\n' || ch == '\r') break; } } ungetc(ch, file); JSAutoRequest ar(cx); JSAutoCompartment ac(cx, obj); JS::CompileOptions options(cx); options.setUTF8(true) .setFileAndLine(filename, 1); JS::Rooted<JSScript*> script(cx, JS::Compile(cx, obj, options, file)); if (script) (void)JS_ExecuteScript(cx, obj, script, result.address()); return; } /* It's an interactive filehandle; drop into read-eval-print loop. */ lineno = 1; hitEOF = false; do { bufp = buffer; *bufp = '\0'; JSAutoRequest ar(cx); JSAutoCompartment ac(cx, obj); /* * Accumulate lines until we get a 'compilable unit' - one that either * generates an error (before running out of source) or that compiles * cleanly. This should be whenever we get a complete statement that * coincides with the end of a line. */ startline = lineno; do { if (!GetLine(bufp, file, startline == lineno ? "js> " : "")) { hitEOF = true; break; } bufp += strlen(bufp); lineno++; } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer))); /* Clear any pending exception from previous failed compiles. */ JS_ClearPendingException(cx); JS::CompileOptions options(cx); options.setFileAndLine("typein", startline); JS::Rooted<JSScript*> script(cx, JS_CompileScript(cx, obj, buffer, strlen(buffer), options)); if (script) { JSErrorReporter older; ok = JS_ExecuteScript(cx, obj, script, result.address()); if (ok && result != JSVAL_VOID) { /* Suppress error reports from JS::ToString(). */ older = JS_SetErrorReporter(cx, nullptr); str = JS::ToString(cx, result); JSAutoByteString bytes; if (str) bytes.encodeLatin1(cx, str); JS_SetErrorReporter(cx, older); if (!!bytes) fprintf(stdout, "%s\n", bytes.ptr()); else ok = false; } } } while (!hitEOF && !env->IsQuitting()); fprintf(stdout, "\n"); }
static char * FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num, bool showArgs, bool showLocals, bool showThisProps) { JS_ASSERT(!cx->isExceptionPending()); RootedScript script(cx, iter.script()); jsbytecode* pc = iter.pc(); RootedObject scopeChain(cx, iter.scopeChain()); JSAutoCompartment ac(cx, scopeChain); const char *filename = script->filename(); unsigned lineno = PCToLineNumber(script, pc); RootedFunction fun(cx, iter.maybeCallee()); RootedString funname(cx); if (fun) funname = fun->atom(); RootedValue thisVal(cx); AutoPropertyDescArray thisProps(cx); if (iter.computeThis(cx)) { thisVal = iter.thisv(); if (showThisProps && !thisVal.isPrimitive()) { RootedObject thisObj(cx, &thisVal.toObject()); thisProps.fetch(thisObj); } } // print the frame number and function name if (funname) { JSAutoByteString funbytes; buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname)); } else if (fun) { buf = JS_sprintf_append(buf, "%d anonymous(", num); } else { buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); } if (!buf) return buf; if (showArgs && iter.hasArgs()) { BindingVector bindings(cx); if (fun && fun->isInterpreted()) { if (!FillBindingVector(script, &bindings)) return buf; } bool first = true; for (unsigned i = 0; i < iter.numActualArgs(); i++) { RootedValue arg(cx); if (i < iter.numFormalArgs() && script->formalIsAliased(i)) { for (AliasedFormalIter fi(script); ; fi++) { if (fi.frameIndex() == i) { arg = iter.callObj().aliasedVar(fi); break; } } } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) { arg = iter.argsObj().arg(i); } else { arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING); } JSAutoByteString valueBytes; const char *value = FormatValue(cx, arg, valueBytes); JSAutoByteString nameBytes; const char *name = nullptr; if (i < bindings.length()) { name = nameBytes.encodeLatin1(cx, bindings[i].name()); if (!buf) return nullptr; } if (value) { buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", !first ? ", " : "", name ? name :"", name ? " = " : "", arg.isString() ? "\"" : "", value ? value : "?unknown?", arg.isString() ? "\"" : ""); if (!buf) return buf; first = false; } else { buf = JS_sprintf_append(buf, " <Failed to get argument while inspecting stack frame>\n"); if (!buf) return buf; cx->clearPendingException(); } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if (!buf) return buf; // 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)); const char *str = nullptr; if (thisValStr && (str = thisValBytes.encodeLatin1(cx, thisValStr))) { buf = JS_sprintf_append(buf, " this = %s\n", str); if (!buf) return buf; } else { buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n"); cx->clearPendingException(); } } } // print the properties of 'this', if it is an object if (showThisProps && thisProps->array) { for (uint32_t i = 0; i < thisProps->length; i++) { JSPropertyDesc* desc = &thisProps->array[i]; if (desc->flags & JSPD_ENUMERATE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } else { buf = JS_sprintf_append(buf, " <Failed to format values while inspecting stack frame>\n"); cx->clearPendingException(); } } } } JS_ASSERT(!cx->isExceptionPending()); return buf; }
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; }
static char * FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int num, JSBool showArgs, JSBool showLocals, JSBool showThisProps) { RootedScript script(cx, iter.script()); jsbytecode* pc = iter.pc(); RootedObject scopeChain(cx, iter.scopeChain()); JSAutoCompartment ac(cx, scopeChain); const char *filename = script->filename(); unsigned lineno = PCToLineNumber(script, pc); RootedFunction fun(cx, iter.maybeCallee()); RootedString funname(cx); if (fun) funname = fun->atom(); RootedObject callObj(cx); AutoPropertyDescArray callProps(cx); if (!iter.isIon() && (showArgs || showLocals)) { JSAbstractFramePtr frame(Jsvalify(iter.abstractFramePtr())); callObj = frame.callObject(cx); if (callObj) callProps.fetch(callObj); } RootedValue thisVal(cx); AutoPropertyDescArray thisProps(cx); if (iter.computeThis()) { thisVal = iter.thisv(); if (showThisProps && !thisVal.isPrimitive()) thisProps.fetch(&thisVal.toObject()); } // print the frame number and function name if (funname) { JSAutoByteString funbytes; buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname)); } else if (fun) { buf = JS_sprintf_append(buf, "%d anonymous(", num); } else { buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); } if (!buf) return buf; // print the function arguments if (showArgs && callObj) { uint32_t namedArgCount = 0; for (uint32_t i = 0; i < callProps->length; i++) { JSPropertyDesc* desc = &callProps->array[i]; JSAutoByteString nameBytes; const char *name = NULL; if (JSVAL_IS_STRING(desc->id)) name = FormatValue(cx, desc->id, nameBytes); JSAutoByteString valueBytes; const char *value = FormatValue(cx, desc->value, valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", namedArgCount ? ", " : "", name ? name :"", name ? " = " : "", desc->value.isString() ? "\"" : "", value ? value : "?unknown?", desc->value.isString() ? "\"" : ""); if (!buf) return buf; namedArgCount++; } // print any unnamed trailing args (found in 'arguments' object) RootedValue val(cx); if (JS_GetProperty(cx, callObj, "arguments", val.address()) && val.isObject()) { uint32_t argCount; RootedObject argsObj(cx, &val.toObject()); if (JS_GetProperty(cx, argsObj, "length", val.address()) && ToUint32(cx, val, &argCount) && argCount > namedArgCount) { for (uint32_t k = namedArgCount; k < argCount; k++) { char number[8]; JS_snprintf(number, 8, "%d", (int) k); if (JS_GetProperty(cx, argsObj, number, val.address())) { JSAutoByteString valueBytes; const char *value = FormatValue(cx, val, valueBytes); buf = JS_sprintf_append(buf, "%s%s%s%s", k ? ", " : "", val.isString() ? "\"" : "", value ? value : "?unknown?", val.isString() ? "\"" : ""); if (!buf) return buf; } } } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if (!buf) return buf; // print local variables if (showLocals && callProps->array) { for (uint32_t i = 0; i < callProps->length; i++) { JSPropertyDesc* desc = &callProps->array[i]; JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " %s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } } } // print the value of 'this' if (showLocals) { if (!thisVal.isUndefined()) { JSAutoByteString thisValBytes; RootedString thisValStr(cx, ToString<CanGC>(cx, thisVal)); if (thisValStr) { if (const char *str = thisValBytes.encodeLatin1(cx, thisValStr)) { buf = JS_sprintf_append(buf, " this = %s\n", str); if (!buf) return buf; } } } else { buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n"); } } // print the properties of 'this', if it is an object if (showThisProps && thisProps->array) { for (uint32_t i = 0; i < thisProps->length; i++) { JSPropertyDesc* desc = &thisProps->array[i]; if (desc->flags & JSPD_ENUMERATE) { JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char *name = FormatValue(cx, desc->id, nameBytes); const char *value = FormatValue(cx, desc->value, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n", name, desc->value.isString() ? "\"" : "", value, desc->value.isString() ? "\"" : ""); if (!buf) return buf; } } } } return buf; }
static char* FormatFrame(JSContext* cx, const ScriptFrameIter& iter, char* buf, int num, bool showArgs, bool showLocals, bool showThisProps) { MOZ_ASSERT(!cx->isExceptionPending()); RootedScript script(cx, iter.script()); jsbytecode* pc = iter.pc(); RootedObject scopeChain(cx, iter.scopeChain(cx)); JSAutoCompartment ac(cx, scopeChain); 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.computeThis(cx)) { thisVal = iter.computedThisValue(); } // print the frame number and function name if (funname) { JSAutoByteString funbytes; buf = JS_sprintf_append(buf, "%d %s(", num, funbytes.encodeLatin1(cx, funname)); } else if (fun) { buf = JS_sprintf_append(buf, "%d anonymous(", num); } else { buf = JS_sprintf_append(buf, "%d <TOP LEVEL>", num); } if (!buf) return buf; if (showArgs && iter.hasArgs()) { BindingIter bi(script); bool first = true; for (unsigned i = 0; i < iter.numActualArgs(); i++) { RootedValue arg(cx); if (i < iter.numFormalArgs() && script->formalIsAliased(i)) { for (AliasedFormalIter fi(script); ; fi++) { if (fi.frameIndex() == i) { arg = iter.callObj(cx).aliasedVar(fi); break; } } } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) { arg = iter.argsObj().arg(i); } else { if (iter.hasUsableAbstractFramePtr()) arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING); else arg = MagicValue(JS_OPTIMIZED_OUT); } JSAutoByteString valueBytes; const char* value = FormatValue(cx, arg, valueBytes); JSAutoByteString nameBytes; const char* name = nullptr; if (i < iter.numFormalArgs()) { MOZ_ASSERT(i == bi.argIndex()); name = nameBytes.encodeLatin1(cx, bi->name()); if (!buf) return nullptr; bi++; } if (value) { buf = JS_sprintf_append(buf, "%s%s%s%s%s%s", !first ? ", " : "", name ? name :"", name ? " = " : "", arg.isString() ? "\"" : "", value, arg.isString() ? "\"" : ""); if (!buf) return buf; first = false; } else { buf = JS_sprintf_append(buf, " <Failed to get argument while inspecting stack frame>\n"); if (!buf) return buf; cx->clearPendingException(); } } } // print filename and line number buf = JS_sprintf_append(buf, "%s [\"%s\":%d]\n", fun ? ")" : "", filename ? filename : "<unknown>", lineno); if (!buf) return buf; // 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)); const char* str = nullptr; if (thisValStr && (str = thisValBytes.encodeLatin1(cx, thisValStr))) { buf = JS_sprintf_append(buf, " this = %s\n", str); if (!buf) return buf; } else { buf = JS_sprintf_append(buf, " <failed to get 'this' value>\n"); cx->clearPendingException(); } } } if (showThisProps && thisVal.isObject()) { RootedObject obj(cx, &thisVal.toObject()); AutoIdVector keys(cx); if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys)) { cx->clearPendingException(); return buf; } 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)) { buf = JS_sprintf_append(buf, " <Failed to fetch property while inspecting stack frame>\n"); cx->clearPendingException(); continue; } JSAutoByteString nameBytes; JSAutoByteString valueBytes; const char* name = FormatValue(cx, key, nameBytes); const char* value = FormatValue(cx, v, valueBytes); if (name && value) { buf = JS_sprintf_append(buf, " this.%s = %s%s%s\n", name, v.isString() ? "\"" : "", value, v.isString() ? "\"" : ""); if (!buf) return buf; } else { buf = JS_sprintf_append(buf, " <Failed to format values while inspecting stack frame>\n"); cx->clearPendingException(); } } } MOZ_ASSERT(!cx->isExceptionPending()); return buf; }
static bool DefinePropertyIfFound(XPCCallContext& ccx, HandleObject obj, HandleId idArg, XPCNativeSet* set, XPCNativeInterface* ifaceArg, XPCNativeMember* member, XPCWrappedNativeScope* scope, bool reflectToStringAndToSource, XPCWrappedNative* wrapperToReflectInterfaceNames, XPCWrappedNative* wrapperToReflectDoubleWrap, XPCNativeScriptableInfo* scriptableInfo, unsigned propFlags, bool* resolved) { RootedId id(ccx, idArg); RefPtr<XPCNativeInterface> iface = ifaceArg; XPCJSContext* xpccx = ccx.GetContext(); bool found; const char* name; propFlags |= JSPROP_RESOLVING; if (set) { if (iface) found = true; else found = set->FindMember(id, &member, &iface); } else found = (nullptr != (member = iface->FindMember(id))); if (!found) { if (reflectToStringAndToSource) { JSNative call; uint32_t flags = 0; if (scriptableInfo) { nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface( scriptableInfo->GetCallback()); if (classInfo) { nsresult rv = classInfo->GetFlags(&flags); if (NS_FAILED(rv)) return Throw(rv, ccx); } } bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT) || Preferences::GetBool("dom.XPCToStringForDOMClasses", false); if(id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) && overwriteToString) { call = XPC_WN_Shared_ToString; name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING); } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) { call = XPC_WN_Shared_ToSource; name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE); } else if (id == SYMBOL_TO_JSID( JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive))) { call = XPC_WN_Shared_toPrimitive; name = "[Symbol.toPrimitive]"; } else { call = nullptr; } if (call) { RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name)); if (!fun) { JS_ReportOutOfMemory(ccx); return false; } AutoResolveName arn(ccx, id); if (resolved) *resolved = true; RootedObject value(ccx, JS_GetFunctionObject(fun)); return JS_DefinePropertyById(ccx, obj, id, value, propFlags & ~JSPROP_ENUMERATE); } } // This *might* be a tearoff name that is not yet part of our // set. Let's lookup the name and see if it is the name of an // interface. Then we'll see if the object actually *does* this // interface and add a tearoff as necessary. if (wrapperToReflectInterfaceNames) { JSAutoByteString name; RefPtr<XPCNativeInterface> iface2; XPCWrappedNativeTearOff* to; RootedObject jso(ccx); nsresult rv = NS_OK; if (JSID_IS_STRING(id) && name.encodeLatin1(ccx, JSID_TO_STRING(id)) && (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) && nullptr != (to = wrapperToReflectInterfaceNames-> FindTearOff(iface2, true, &rv)) && nullptr != (jso = to->GetJSObject())) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, jso, propFlags & ~JSPROP_ENUMERATE); } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) { return Throw(rv, ccx); } } // This *might* be a double wrapped JSObject if (wrapperToReflectDoubleWrap && id == xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT) && GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) { // We build and add a getter function. // A security check is done on a per-get basis. JSFunction* fun; id = xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT); name = xpccx->GetStringName(XPCJSContext::IDX_WRAPPED_JSOBJECT); fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter, 0, 0, name); if (!fun) return false; RootedObject funobj(ccx, JS_GetFunctionObject(fun)); if (!funobj) return false; propFlags |= JSPROP_GETTER | JSPROP_SHARED; propFlags &= ~JSPROP_ENUMERATE; AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags, JS_DATA_TO_FUNC_PTR(JSNative, funobj.get()), nullptr); } if (resolved) *resolved = false; return true; } if (!member) { if (wrapperToReflectInterfaceNames) { XPCWrappedNativeTearOff* to = wrapperToReflectInterfaceNames->FindTearOff(iface, true); if (!to) return false; RootedObject jso(ccx, to->GetJSObject()); if (!jso) return false; AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, jso, propFlags & ~JSPROP_ENUMERATE); } if (resolved) *resolved = false; return true; } if (member->IsConstant()) { RootedValue val(ccx); AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return member->GetConstantValue(ccx, iface, val.address()) && JS_DefinePropertyById(ccx, obj, id, val, propFlags); } if (scope->HasInterposition()) { Rooted<PropertyDescriptor> desc(ccx); if (!xpc::InterposeProperty(ccx, obj, iface->GetIID(), id, &desc)) return false; if (desc.object()) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; desc.attributesRef() |= JSPROP_RESOLVING; return JS_DefinePropertyById(ccx, obj, id, desc); } } if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) || id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE) || (scriptableInfo && scriptableInfo->GetFlags().DontEnumQueryInterface() && id == xpccx->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE))) propFlags &= ~JSPROP_ENUMERATE; RootedValue funval(ccx); if (!member->NewFunctionObject(ccx, iface, obj, funval.address())) return false; if (member->IsMethod()) { AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, funval, propFlags); } // else... MOZ_ASSERT(member->IsAttribute(), "way broken!"); propFlags |= JSPROP_GETTER | JSPROP_SHARED; propFlags &= ~JSPROP_READONLY; JSObject* funobj = funval.toObjectOrNull(); JSNative getter = JS_DATA_TO_FUNC_PTR(JSNative, funobj); JSNative setter; if (member->IsWritableAttribute()) { propFlags |= JSPROP_SETTER; setter = JS_DATA_TO_FUNC_PTR(JSNative, funobj); } else { setter = nullptr; } AutoResolveName arn(ccx, id); if (resolved) *resolved = true; return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags, getter, setter); }