std::wstring ScriptInterface::ToString(jsval obj, bool pretty) { if (JSVAL_IS_VOID(obj)) return L"(void 0)"; // Try to stringify as JSON if possible // (TODO: this is maybe a bad idea since it'll drop 'undefined' values silently) if (pretty) { StringifierW str; // Temporary disable the error reporter, so we don't print complaints about cyclic values JSErrorReporter er = JS_SetErrorReporter(m->m_cx, NULL); bool ok = JS_Stringify(m->m_cx, &obj, NULL, INT_TO_JSVAL(2), &StringifierW::callback, &str) == JS_TRUE; // Restore error reporter JS_SetErrorReporter(m->m_cx, er); if (ok) return str.stream.str(); // Clear the exception set when Stringify failed JS_ClearPendingException(m->m_cx); } // Caller didn't want pretty output, or JSON conversion failed (e.g. due to cycles), // so fall back to obj.toSource() std::wstring source = L"(error)"; CallFunction(obj, "toSource", source); return source; }
std::string CThreadDebugger::StringifyCyclicJSON(jsval obj, bool indent) { CyclicRefWorkaround::Stringifier str; CyclicRefWorkaround::g_ProcessedObjects.clear(); CyclicRefWorkaround::g_LastKey = JSVAL_VOID; JSObject* pGlob = JSVAL_TO_OBJECT(m->m_pScriptInterface->GetGlobalObject()); JSFunction* fun = JS_DefineFunction(m->m_pScriptInterface->GetContext(), pGlob, "replacer", CyclicRefWorkaround::replacer, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT); JSObject* replacer = JS_GetFunctionObject(fun); if (!JS_Stringify(m->m_pScriptInterface->GetContext(), &obj, replacer, indent ? INT_TO_JSVAL(2) : JSVAL_VOID, &CyclicRefWorkaround::Stringifier::callback, &str)) { LOGERROR(L"StringifyJSON failed"); jsval exec; jsval execString; if (JS_GetPendingException(m->m_pScriptInterface->GetContext(), &exec)) { if (JSVAL_IS_OBJECT(exec)) { JS_GetProperty(m->m_pScriptInterface->GetContext(), JSVAL_TO_OBJECT(exec), "message", &execString); if (JSVAL_IS_STRING(execString)) { std::string strExec = JS_EncodeString(m->m_pScriptInterface->GetContext(), JSVAL_TO_STRING(execString)); LOGERROR(L"Error: %hs", strExec.c_str()); } } } JS_ClearPendingException(m->m_pScriptInterface->GetContext()); return ""; } return str.stream.str(); }
nsresult nsJSON::EncodeInternal(JSContext* cx, const JS::Value& aValue, nsJSONWriter* writer) { JSAutoRequest ar(cx); // Backward compatibility: // nsIJSON does not allow to serialize anything other than objects if (!aValue.isObject()) { return NS_ERROR_INVALID_ARG; } JSObject* obj = &aValue.toObject(); JS::Value val = aValue; /* Backward compatibility: * Manually call toJSON if implemented by the object and check that * the result is still an object * Note: It is perfectly fine to not implement toJSON, so it is * perfectly fine for GetMethod to fail */ JS::Value toJSON; if (JS_GetMethod(cx, obj, "toJSON", NULL, &toJSON) && !JSVAL_IS_PRIMITIVE(toJSON) && JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(toJSON))) { // If toJSON is implemented, it must not throw if (!JS_CallFunctionValue(cx, obj, toJSON, 0, NULL, &val)) { if (JS_IsExceptionPending(cx)) // passing NS_OK will throw the pending exception return NS_OK; // No exception, but still failed return NS_ERROR_FAILURE; } // Backward compatibility: // nsIJSON does not allow to serialize anything other than objects if (JSVAL_IS_PRIMITIVE(val)) return NS_ERROR_INVALID_ARG; } // GetMethod may have thrown else if (JS_IsExceptionPending(cx)) // passing NS_OK will throw the pending exception return NS_OK; // Backward compatibility: // function shall not pass, just "plain" objects and arrays JSType type = JS_TypeOfValue(cx, val); if (type == JSTYPE_FUNCTION) return NS_ERROR_INVALID_ARG; // We're good now; try to stringify if (!JS_Stringify(cx, &val, NULL, JSVAL_NULL, WriteCallback, writer)) return NS_ERROR_FAILURE; return NS_OK; }
nsresult nsJSON::EncodeInternal(nsJSONWriter *writer) { nsresult rv; nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (!xpc) return NS_ERROR_FAILURE; nsAXPCNativeCallContext *cc = nsnull; rv = xpc->GetCurrentNativeCallContext(&cc); NS_ENSURE_SUCCESS(rv, rv); JSContext *cx = nsnull; rv = cc->GetJSContext(&cx); NS_ENSURE_SUCCESS(rv, rv); JSAutoRequest ar(cx); PRUint32 argc = 0; rv = cc->GetArgc(&argc); NS_ENSURE_SUCCESS(rv, rv); // Now fish for the JS arguments. If it's a call to encode, we'll // want the first two arguments. If it's a call to encodeToStream, // we'll want the fourth and fifth; PRUint32 firstArg = writer->mStream ? 3 : 0; // Get the object we're going to serialize. JSObject *inputObj = nsnull; jsval *argv = nsnull; rv = cc->GetArgvPtr(&argv); NS_ENSURE_SUCCESS(rv, rv); if (argc <= firstArg || !(JSVAL_IS_OBJECT(argv[firstArg]) && (inputObj = JSVAL_TO_OBJECT(argv[firstArg])))) { // return if it's not something we can deal with return NS_ERROR_INVALID_ARG; } jsval *vp = &argv[firstArg]; JSBool ok = JS_TryJSON(cx, vp); JSType type; if (!(ok && !JSVAL_IS_PRIMITIVE(*vp) && (type = JS_TypeOfValue(cx, *vp)) != JSTYPE_FUNCTION && type != JSTYPE_XML)) { return NS_ERROR_INVALID_ARG; } ok = JS_Stringify(cx, vp, NULL, JSVAL_NULL, WriteCallback, writer); if (!ok) return NS_ERROR_FAILURE; return NS_OK; }
nsresult nsJSON::EncodeInternal(JSContext* cx, const JS::Value& aValue, nsJSONWriter* writer) { // Backward compatibility: // nsIJSON does not allow to serialize anything other than objects if (!aValue.isObject()) { return NS_ERROR_INVALID_ARG; } JS::Rooted<JSObject*> obj(cx, &aValue.toObject()); /* Backward compatibility: * Manually call toJSON if implemented by the object and check that * the result is still an object * Note: It is perfectly fine to not implement toJSON, so it is * perfectly fine for GetMethod to fail */ JS::Rooted<JS::Value> val(cx, aValue); JS::Rooted<JS::Value> toJSON(cx); if (JS_GetProperty(cx, obj, "toJSON", &toJSON) && toJSON.isObject() && JS_ObjectIsCallable(cx, &toJSON.toObject())) { // If toJSON is implemented, it must not throw if (!JS_CallFunctionValue(cx, obj, toJSON, 0, nullptr, val.address())) { if (JS_IsExceptionPending(cx)) // passing NS_OK will throw the pending exception return NS_OK; // No exception, but still failed return NS_ERROR_FAILURE; } // Backward compatibility: // nsIJSON does not allow to serialize anything other than objects if (val.isPrimitive()) return NS_ERROR_INVALID_ARG; } // GetMethod may have thrown else if (JS_IsExceptionPending(cx)) // passing NS_OK will throw the pending exception return NS_OK; // Backward compatibility: // function shall not pass, just "plain" objects and arrays JSType type = JS_TypeOfValue(cx, val); if (type == JSTYPE_FUNCTION) return NS_ERROR_INVALID_ARG; // We're good now; try to stringify if (!JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, writer)) return NS_ERROR_FAILURE; return NS_OK; }
std::string ScriptInterface::StringifyJSON(jsval obj, bool indent) { Stringifier str; if (!JS_Stringify(m->m_cx, &obj, NULL, indent ? INT_TO_JSVAL(2) : JSVAL_VOID, &Stringifier::callback, &str)) { JS_ClearPendingException(m->m_cx); LOGERROR(L"StringifyJSON failed"); JS_ClearPendingException(m->m_cx); return std::string(); } return str.stream.str(); }
// TODO: It's not quite clear why JS_Stringify needs JS::MutableHandleValue. |obj| should not get modified. // It probably has historical reasons and could be changed by SpiderMonkey in the future. std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) { JSAutoRequest rq(m->m_cx); Stringifier str; JS::RootedValue indentVal(m->m_cx, indent ? JS::Int32Value(2) : JS::UndefinedValue()); if (!JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &Stringifier::callback, &str)) { JS_ClearPendingException(m->m_cx); LOGERROR("StringifyJSON failed"); return std::string(); } return str.stream.str(); }
NS_IMETHODIMP nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result) { result.Truncate(); // Begin a new request JSAutoRequest ar(cx); JSAutoEnterCompartment ac; nsIScriptSecurityManager *ssm = nsnull; if (value->isObject()) { JSObject *obj = &value->toObject(); if (!ac.enter(cx, obj)) { return NS_ERROR_FAILURE; } nsCOMPtr<nsIPrincipal> principal; ssm = nsContentUtils::GetSecurityManager(); nsresult rv = ssm->GetObjectPrincipal(cx, obj, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); JSStackFrame *fp = nsnull; rv = ssm->PushContextPrincipal(cx, JS_FrameIterator(cx, &fp), principal); NS_ENSURE_SUCCESS(rv, rv); } nsJSONWriter writer; JSBool ok = JS_Stringify(cx, value, NULL, JSVAL_NULL, WriteCallback, &writer); if (ssm) { ssm->PopContextPrincipal(cx); } if (!ok) { return NS_ERROR_XPC_BAD_CONVERT_JS; } NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED); writer.FlushBuffer(); result.Assign(writer.mOutputString); return NS_OK; }
NS_IMETHODIMP nsJSON::EncodeFromJSVal(jsval *value, JSContext *cx, nsAString &result) { result.Truncate(); // Begin a new request JSAutoRequest ar(cx); nsJSONWriter writer; JSBool ok = JS_Stringify(cx, value, NULL, JSVAL_NULL, WriteCallback, &writer); if (!ok) { return NS_ERROR_XPC_BAD_CONVERT_JS; } NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED); writer.FlushBuffer(); result.Assign(writer.mOutputString); return NS_OK; }
NS_IMETHODIMP nsJSON::EncodeFromJSVal(JS::Value *value, JSContext *cx, nsAString &result) { result.Truncate(); mozilla::Maybe<JSAutoCompartment> ac; if (value->isObject()) { JS::Rooted<JSObject*> obj(cx, &value->toObject()); ac.construct(cx, obj); } nsJSONWriter writer; if (!JS_Stringify(cx, value, NULL, JSVAL_NULL, WriteCallback, &writer)) { return NS_ERROR_XPC_BAD_CONVERT_JS; } NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED); writer.FlushBuffer(); result.Assign(writer.mOutputString); return NS_OK; }
std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) { JSAutoRequest rq(m->m_cx); if (obj.isUndefined()) return "(void 0)"; // Try to stringify as JSON if possible // (TODO: this is maybe a bad idea since it'll drop 'undefined' values silently) if (pretty) { Stringifier str; JS::RootedValue indentVal(m->m_cx, JS::Int32Value(2)); // Temporary disable the error reporter, so we don't print complaints about cyclic values JSErrorReporter er = JS_SetErrorReporter(m->m_cx, NULL); bool ok = JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &Stringifier::callback, &str); // Restore error reporter JS_SetErrorReporter(m->m_cx, er); if (ok) return str.stream.str(); // Clear the exception set when Stringify failed JS_ClearPendingException(m->m_cx); } // Caller didn't want pretty output, or JSON conversion failed (e.g. due to cycles), // so fall back to obj.toSource() std::wstring source = L"(error)"; CallFunction(obj, "toSource", source); return utf8_from_wstring(source); }
nsresult SaveProfileTask::Run() { // Get file path #if defined(SPS_PLAT_arm_android) && !defined(MOZ_WIDGET_GONK) nsCString tmpPath; tmpPath.AppendPrintf("/sdcard/profile_%i_%i.txt", XRE_GetProcessType(), getpid()); #else nsCOMPtr<nsIFile> tmpFile; nsAutoCString tmpPath; if (NS_FAILED(NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmpFile)))) { LOG("Failed to find temporary directory."); return NS_ERROR_FAILURE; } tmpPath.AppendPrintf("profile_%i_%i.txt", XRE_GetProcessType(), getpid()); nsresult rv = tmpFile->AppendNative(tmpPath); if (NS_FAILED(rv)) return rv; rv = tmpFile->GetNativePath(tmpPath); if (NS_FAILED(rv)) return rv; #endif // Create a JSContext to run a JSObjectBuilder :( // Based on XPCShellEnvironment JSRuntime *rt; JSContext *cx; nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); if (!rtsvc || NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) { LOG("failed to get RuntimeService"); return NS_ERROR_FAILURE;; } cx = JS_NewContext(rt, 8192); if (!cx) { LOG("Failed to get context"); return NS_ERROR_FAILURE; } { JSAutoRequest ar(cx); static const JSClass c = { "global", JSCLASS_GLOBAL_FLAGS, JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub }; JSObject *obj = JS_NewGlobalObject(cx, &c, nullptr, JS::FireOnNewGlobalHook); std::ofstream stream; stream.open(tmpPath.get()); if (stream.is_open()) { JSAutoCompartment autoComp(cx, obj); JSObject* profileObj = profiler_get_profile_jsobject(cx); JS::Rooted<JS::Value> val(cx, OBJECT_TO_JSVAL(profileObj)); JS_Stringify(cx, &val, JS::NullPtr(), JS::NullHandleValue, WriteCallback, &stream); stream.close(); LOGF("Saved to %s", tmpPath.get()); } else { LOG("Fail to open profile log file."); } } JS_DestroyContext(cx); return NS_OK; }
JSBool RemoteStagedService::evaluate(struct mg_connection *conn, void *closures) { RemoteStagedService *that = (RemoteStagedService *) closures; mg_send_header(conn, "Content-Type", "application/json"); if(!that->cleanUp()) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_GENERAL, "cleanUp"); return JS_TRUE; } if(!(conn->query_string && (strlen(conn->query_string)<STG_RECEIVE_SRC_SIZE))) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_INVALID_ARGS, ""); return JS_TRUE; } JSString *queryStr = JS_InternString(that->cx, conn->query_string + 4); JSString *queryEscStr; if(!JS_UnescapedString(that->cx, queryStr, &queryEscStr)) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_GENERAL, "unescapeString"); return JS_TRUE; } char *queryEscChars = JS_EncodeString(that->cx, queryEscStr); jsSrcBufferSize = STG_RECEIVE_SRC_SIZE; if(!JS_DecodeBytes(that->cx, queryEscChars, strlen(queryEscChars), jsSrcBuffer, &jsSrcBufferSize)) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_GENERAL, "JS_DecodeBytes"); return JS_TRUE; } jsval parseVal; if (!reflect_parse_from_string(that->cx, jsSrcBuffer, jsSrcBufferSize, &parseVal)) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_REFLECT_PARSE, "reflection parse error"); return JS_TRUE; } JS::Value unparseArgs[] = { parseVal }; JS::Value unparseRes; if (!JS_CallFunctionName(that->cx, that->global, "unparse", 1, unparseArgs, &unparseRes)){ HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_GENERAL, "unparse"); return JS_TRUE; } JS::Value *stringifyArg = { &OBJECT_TO_JSVAL(that->respArray) }; if(!JS_Stringify(that->cx, stringifyArg, 0, js::NullValue(), onJSONStringifyFinished, 0)) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_GENERAL, "JS_Stringify"); return JS_TRUE; } char srcBuffer[STG_RECEIVE_SRC_SIZE]; size_t srcBufferSize=STG_RECEIVE_SRC_SIZE; if (!DeflateStringToBuffer(that->cx, jsSrcBuffer, jsSrcBufferSize, srcBuffer, &srcBufferSize)) { HttpHandler::response(conn, NULL, RESP_FAIL_STR, ERR_CODE_GENERAL, "DeflateStringToBuffer"); return JS_TRUE; } srcBuffer[srcBufferSize] = 0; HttpHandler::response(conn, NULL, RESP_SUCCESS_STR, srcBuffer); return JS_TRUE; }