Changeset View
Standalone View
source/scriptinterface/ScriptInterface.cpp
Show First 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | JSClass global_class = { | ||||
nullptr, nullptr, | nullptr, nullptr, | ||||
nullptr, nullptr, nullptr, | nullptr, nullptr, nullptr, | ||||
nullptr, nullptr, nullptr, nullptr, | nullptr, nullptr, nullptr, nullptr, | ||||
JS_GlobalObjectTraceHook | JS_GlobalObjectTraceHook | ||||
}; | }; | ||||
void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) | void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report) | ||||
{ | { | ||||
std::stringstream msg; | std::stringstream msg; | ||||
bool isWarning = JSREPORT_IS_WARNING(report->flags); | bool isWarning = JSREPORT_IS_WARNING(report->flags); | ||||
msg << (isWarning ? "JavaScript warning: " : "JavaScript error: "); | msg << (isWarning ? "JavaScript warning: " : "JavaScript error: "); | ||||
if (report->filename) | if (report->filename) | ||||
{ | { | ||||
msg << report->filename; | msg << report->filename; | ||||
msg << " line " << report->lineno << "\n"; | msg << " line " << report->lineno << "\n"; | ||||
} | } | ||||
msg << message; | msg << message; | ||||
// If there is an exception, then print its stack trace | // If there is an exception, then print its stack trace | ||||
JS::RootedValue excn(cx); | JS::RootedValue excn(cx); | ||||
elexis: Unless there is a superseding reason for it, I assume there is a missing JSAutoRequest(cx) here. | |||||
if (JS_GetPendingException(cx, &excn) && excn.isObject()) | if (JS_GetPendingException(cx, &excn) && excn.isObject()) | ||||
{ | { | ||||
JS::RootedObject excnObj(cx, &excn.toObject()); | // NB: this violates the docs ("The error reporter callback must not reenter the JSAPI.") | ||||
// TODO: this violates the docs ("The error reporter callback must not reenter the JSAPI.") | // It's unclear how bad this really is. | ||||
Not Done Inline ActionsEvaluate is bad practice in general unless one really really, really wants it. rP8063 added the exception printing, rP8975 added the violation TODO. I don't see any rights to the codes existence if the whole circus is done solely for one tab indentation. I've created D2152 which should remove this hunk. elexis: Evaluate is bad practice in general unless one really really, really wants it.
And here it is… | |||||
// Hide the exception from EvaluateScript | // Hide the exception from Evaluate | ||||
JSExceptionState* excnState = JS_SaveExceptionState(cx); | JSExceptionState* excnState = JS_SaveExceptionState(cx); | ||||
JS_ClearPendingException(cx); | JS_ClearPendingException(cx); | ||||
JS::RootedValue rval(cx); | JS::RootedValue rval(cx); | ||||
const char dumpStack[] = "this.stack.trimRight().replace(/^/mg, ' ')"; // indent each line | const char dumpStack[] = "function __stackTrace(a) { return a.stack.trimRight().replace(/^/mg, ' '); }"; // indent each line | ||||
JS::CompileOptions opts(cx); | JS::CompileOptions opts(cx); | ||||
if (JS::Evaluate(cx, excnObj, opts.setFileAndLine("(eval)", 1), dumpStack, ARRAY_SIZE(dumpStack)-1, &rval)) | JS::Evaluate(cx, opts.setFileAndLine("(ErrorReporter)", 1), dumpStack, ARRAY_SIZE(dumpStack)-1, &rval); | ||||
JS::RootedObject glob(cx, JS::CurrentGlobalOrNull(cx)); | |||||
Not Done Inline ActionsRooting in a variable usually mean that one is in JS Request mode for that context. elexis: Rooting in a variable usually mean that one is in JS Request mode for that context. | |||||
if (JS_CallFunctionName(cx, glob, "__stackTrace", JS::HandleValueArray(excn), &rval)) | |||||
wraitiiUnsubmitted Not Done Inline ActionsSlightly cleaner approach perhaps? wraitii: Slightly cleaner approach perhaps? | |||||
Not Done Inline ActionsNot a blocking concern: It sounds like we could just expose __stackTrace as an engine function cleanly in ScriptInterface instead of evaluating ing it here? wraitii: Not a blocking concern: It sounds like we could just expose `__stackTrace` as an engine… | |||||
Done Inline ActionsI'm not 100% sure what you propose, you want to add an Engine.stacktrace? If it's not used elsewhere I don't see the point? Or maybe create a new diff, independent from the SM upgrade, demonstrating how we could use that new engine function ? Itms: I'm not 100% sure what you propose, you want to add an `Engine.stacktrace`? If it's not used… | |||||
Not Done Inline ActionsI wonder if one can't obtain the readable stack without an Evaluate elexis: I wonder if one can't obtain the readable stack without an `Evaluate` | |||||
{ | { | ||||
std::string stackTrace; | std::string stackTrace; | ||||
if (ScriptInterface::FromJSVal(cx, rval, stackTrace)) | if (ScriptInterface::FromJSVal(cx, rval, stackTrace)) | ||||
msg << "\n" << stackTrace; | msg << "\n" << stackTrace; | ||||
JS_RestoreExceptionState(cx, excnState); | JS_RestoreExceptionState(cx, excnState); | ||||
} | } | ||||
else | else | ||||
▲ Show 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr<ScriptRuntime>& runtime) : | ||||
JS_SetErrorReporter(m_runtime->m_rt, ErrorReporter); | JS_SetErrorReporter(m_runtime->m_rt, ErrorReporter); | ||||
JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_ION_ENABLE, 1); | JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_ION_ENABLE, 1); | ||||
JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1); | JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1); | ||||
JS::RuntimeOptionsRef(m_cx).setExtraWarnings(1) | JS::RuntimeOptionsRef(m_cx).setExtraWarnings(1) | ||||
.setWerror(0) | .setWerror(0) | ||||
.setVarObjFix(1) | |||||
wraitiiUnsubmitted Not Done Inline ActionsRemoved. In general functions don't get passed a context object as much, so a number of parameters could be removed. wraitii: Removed. In general functions don't get passed a context object as much, so a number of… | |||||
.setStrictMode(1); | .setStrictMode(1); | ||||
JS::CompartmentOptions opt; | JS::CompartmentOptions opt; | ||||
opt.setVersion(JSVERSION_LATEST); | opt.setVersion(JSVERSION_LATEST); | ||||
// Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement. | // Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement. | ||||
opt.setPreserveJitCode(true); | opt.setPreserveJitCode(true); | ||||
JSAutoRequest rq(m_cx); | JSAutoRequest rq(m_cx); | ||||
▲ Show 20 Lines • Show All 168 Lines • ▼ Show 20 Lines | void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs) | ||||
if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end()) | if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end()) | ||||
{ | { | ||||
// This type already exists | // This type already exists | ||||
throw PSERROR_Scripting_DefineType_AlreadyExists(); | throw PSERROR_Scripting_DefineType_AlreadyExists(); | ||||
} | } | ||||
JS::RootedObject global(m->m_cx, m->m_glob); | JS::RootedObject global(m->m_cx, m->m_glob); | ||||
JS::RootedObject obj(m->m_cx, JS_InitClass(m->m_cx, global, JS::NullPtr(), | JS::RootedObject obj(m->m_cx, JS_InitClass(m->m_cx, global, nullptr, | ||||
wraitiiUnsubmitted Not Done Inline ActionsNullPtr() was deleted, see SM45 changelog. wraitii: NullPtr() was deleted, see SM45 changelog. | |||||
clasp, | clasp, | ||||
constructor, minArgs, // Constructor, min args | constructor, minArgs, // Constructor, min args | ||||
ps, fs, // Properties, methods | ps, fs, // Properties, methods | ||||
static_ps, static_fs)); // Constructor properties, methods | static_ps, static_fs)); // Constructor properties, methods | ||||
if (obj == NULL) | if (obj == NULL) | ||||
throw PSERROR_Scripting_DefineType_CreationFailed(); | throw PSERROR_Scripting_DefineType_CreationFailed(); | ||||
Show All 34 Lines | |||||
} | } | ||||
JS::Value ScriptInterface::GetGlobalObject() const | JS::Value ScriptInterface::GetGlobalObject() const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
return JS::ObjectValue(*JS::CurrentGlobalOrNull(m->m_cx)); | return JS::ObjectValue(*JS::CurrentGlobalOrNull(m->m_cx)); | ||||
} | } | ||||
bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace) | bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate) | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
JS::RootedObject global(m->m_cx, m->m_glob); | JS::RootedObject global(m->m_cx, m->m_glob); | ||||
if (!replace) | if (!replace) | ||||
{ | { | ||||
bool found; | bool found; | ||||
if (!JS_HasProperty(m->m_cx, global, name, &found)) | if (!JS_HasProperty(m->m_cx, global, name, &found)) | ||||
return false; | return false; | ||||
if (found) | if (found) | ||||
{ | { | ||||
JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name); | JS_ReportError(m->m_cx, "SetGlobal \"%s\" called multiple times", name); | ||||
return false; | return false; | ||||
} | } | ||||
} | } | ||||
bool ok = JS_DefineProperty(m->m_cx, global, name, value, JSPROP_ENUMERATE | JSPROP_READONLY | uint attrs = 0; | ||||
| JSPROP_PERMANENT); | if (constant) | ||||
attrs |= JSPROP_READONLY | JSPROP_PERMANENT; | |||||
if (enumerate) | |||||
attrs |= JSPROP_ENUMERATE; | |||||
bool ok = JS_DefineProperty(m->m_cx, global, name, value, attrs); | |||||
wraitiiUnsubmitted Not Done Inline ActionsNecessary, as that is now enforced and g_Progress for example is updated. wraitii: Necessary, as that is now enforced and g_Progress for example is updated. | |||||
return ok; | return ok; | ||||
} | } | ||||
bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const | bool ScriptInterface::SetProperty_(JS::HandleValue obj, const char* name, JS::HandleValue value, bool constant, bool enumerate) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
uint attrs = 0; | uint attrs = 0; | ||||
if (constant) | if (constant) | ||||
▲ Show 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | if (!objVal.isObjectOrNull()) | ||||
LOGERROR("EnumeratePropertyNamesWithPrefix expected object type!"); | LOGERROR("EnumeratePropertyNamesWithPrefix expected object type!"); | ||||
return false; | return false; | ||||
} | } | ||||
if (objVal.isNull()) | if (objVal.isNull()) | ||||
return true; // reached the end of the prototype chain | return true; // reached the end of the prototype chain | ||||
JS::RootedObject obj(m->m_cx, &objVal.toObject()); | JS::RootedObject obj(m->m_cx, &objVal.toObject()); | ||||
JS::AutoIdArray props(m->m_cx, JS_Enumerate(m->m_cx, obj)); | JS::Rooted<JS::IdVector> props(m->m_cx, m->m_cx); | ||||
if (!props) | bool hasProps = JS_Enumerate(m->m_cx, obj, &props); | ||||
if (!hasProps) | |||||
wraitiiUnsubmitted Not Done Inline ActionsAPI change, but the docs for SM45 don't reflect it (the comments in HEAD do though) wraitii: API change, but the docs for SM45 don't reflect it (the comments in HEAD do though) | |||||
Not Done Inline Actions(same, both) elexis: (same, both) | |||||
return false; | return false; | ||||
for (size_t i = 0; i < props.length(); ++i) | for (size_t i = 0; i < props.length(); ++i) | ||||
{ | { | ||||
JS::RootedId id(m->m_cx, props[i]); | JS::RootedId id(m->m_cx, props[i]); | ||||
JS::RootedValue val(m->m_cx); | JS::RootedValue val(m->m_cx); | ||||
if (!JS_IdToValue(m->m_cx, id, &val)) | if (!JS_IdToValue(m->m_cx, id, &val)) | ||||
return false; | return false; | ||||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Lines | bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code) const | ||||
utf16string codeUtf16(code.begin(), code.end()); | utf16string codeUtf16(code.begin(), code.end()); | ||||
uint lineNo = 1; | uint lineNo = 1; | ||||
// CompileOptions does not copy the contents of the filename string pointer. | // CompileOptions does not copy the contents of the filename string pointer. | ||||
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. | // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. | ||||
std::string filenameStr = filename.string8(); | std::string filenameStr = filename.string8(); | ||||
JS::CompileOptions options(m->m_cx); | JS::CompileOptions options(m->m_cx); | ||||
options.setFileAndLine(filenameStr.c_str(), lineNo); | options.setFileAndLine(filenameStr.c_str(), lineNo); | ||||
options.setCompileAndGo(true); | |||||
JS::RootedFunction func(m->m_cx); | JS::RootedFunction func(m->m_cx); | ||||
JS::AutoObjectVector emptyScopeChain(m->m_cx); | JS::AutoObjectVector emptyScopeChain(m->m_cx); | ||||
if (!JS::CompileFunction(m->m_cx, emptyScopeChain, options, NULL, 0, NULL, | if (!JS::CompileFunction(m->m_cx, emptyScopeChain, options, NULL, 0, NULL, | ||||
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &func)) | reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &func)) | ||||
return false; | return false; | ||||
JS::RootedValue rval(m->m_cx); | JS::RootedValue rval(m->m_cx); | ||||
return JS_CallFunction(m->m_cx, JS::NullPtr(), func, JS::HandleValueArray::empty(), &rval); | return JS_CallFunction(m->m_cx, nullptr, func, JS::HandleValueArray::empty(), &rval); | ||||
} | } | ||||
shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime> parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger) | shared_ptr<ScriptRuntime> ScriptInterface::CreateRuntime(shared_ptr<ScriptRuntime> parentRuntime, int runtimeSize, int heapGrowthBytesGCTrigger) | ||||
{ | { | ||||
return shared_ptr<ScriptRuntime>(new ScriptRuntime(parentRuntime, runtimeSize, heapGrowthBytesGCTrigger)); | return shared_ptr<ScriptRuntime>(new ScriptRuntime(parentRuntime, runtimeSize, heapGrowthBytesGCTrigger)); | ||||
} | } | ||||
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const | bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
JS::RootedObject global(m->m_cx, m->m_glob); | |||||
utf16string codeUtf16(code.begin(), code.end()); | utf16string codeUtf16(code.begin(), code.end()); | ||||
uint lineNo = 1; | uint lineNo = 1; | ||||
// CompileOptions does not copy the contents of the filename string pointer. | // CompileOptions does not copy the contents of the filename string pointer. | ||||
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. | // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. | ||||
std::string filenameStr = filename.string8(); | std::string filenameStr = filename.string8(); | ||||
JS::RootedValue rval(m->m_cx); | JS::RootedValue rval(m->m_cx); | ||||
JS::CompileOptions opts(m->m_cx); | JS::CompileOptions opts(m->m_cx); | ||||
opts.setFileAndLine(filenameStr.c_str(), lineNo); | opts.setFileAndLine(filenameStr.c_str(), lineNo); | ||||
return JS::Evaluate(m->m_cx, global, opts, | return JS::Evaluate(m->m_cx, opts, | ||||
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval); | reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval); | ||||
} | } | ||||
bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const | bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
JS::RootedObject global(m->m_cx, m->m_glob); | |||||
if (!VfsFileExists(path)) | if (!VfsFileExists(path)) | ||||
{ | { | ||||
LOGERROR("File '%s' does not exist", path.string8()); | LOGERROR("File '%s' does not exist", path.string8()); | ||||
return false; | return false; | ||||
} | } | ||||
CVFSFile file; | CVFSFile file; | ||||
Show All 11 Lines | bool ScriptInterface::LoadGlobalScriptFile(const VfsPath& path) const | ||||
uint lineNo = 1; | uint lineNo = 1; | ||||
// CompileOptions does not copy the contents of the filename string pointer. | // CompileOptions does not copy the contents of the filename string pointer. | ||||
// Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. | // Passing a temporary string there will cause undefined behaviour, so we create a separate string to avoid the temporary. | ||||
std::string filenameStr = path.string8(); | std::string filenameStr = path.string8(); | ||||
JS::RootedValue rval(m->m_cx); | JS::RootedValue rval(m->m_cx); | ||||
JS::CompileOptions opts(m->m_cx); | JS::CompileOptions opts(m->m_cx); | ||||
opts.setFileAndLine(filenameStr.c_str(), lineNo); | opts.setFileAndLine(filenameStr.c_str(), lineNo); | ||||
return JS::Evaluate(m->m_cx, global, opts, | return JS::Evaluate(m->m_cx, opts, | ||||
reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval); | reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)(codeUtf16.length()), &rval); | ||||
} | } | ||||
bool ScriptInterface::Eval(const char* code) const | bool ScriptInterface::Eval(const char* code) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
JS::RootedValue rval(m->m_cx); | JS::RootedValue rval(m->m_cx); | ||||
return Eval_(code, &rval); | return Eval_(code, &rval); | ||||
} | } | ||||
bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval) const | bool ScriptInterface::Eval_(const char* code, JS::MutableHandleValue rval) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
JS::RootedObject global(m->m_cx, m->m_glob); | |||||
utf16string codeUtf16(code, code+strlen(code)); | utf16string codeUtf16(code, code+strlen(code)); | ||||
JS::CompileOptions opts(m->m_cx); | JS::CompileOptions opts(m->m_cx); | ||||
opts.setFileAndLine("(eval)", 1); | opts.setFileAndLine("(eval)", 1); | ||||
return JS::Evaluate(m->m_cx, global, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); | return JS::Evaluate(m->m_cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); | ||||
} | } | ||||
bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval) const | bool ScriptInterface::Eval_(const wchar_t* code, JS::MutableHandleValue rval) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
JS::RootedObject global(m->m_cx, m->m_glob); | |||||
utf16string codeUtf16(code, code+wcslen(code)); | utf16string codeUtf16(code, code+wcslen(code)); | ||||
JS::CompileOptions opts(m->m_cx); | JS::CompileOptions opts(m->m_cx); | ||||
opts.setFileAndLine("(eval)", 1); | opts.setFileAndLine("(eval)", 1); | ||||
return JS::Evaluate(m->m_cx, global, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); | return JS::Evaluate(m->m_cx, opts, reinterpret_cast<const char16_t*>(codeUtf16.c_str()), (uint)codeUtf16.length(), rval); | ||||
} | } | ||||
bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const | bool ScriptInterface::ParseJSON(const std::string& string_utf8, JS::MutableHandleValue out) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
std::wstring attrsW = wstring_from_utf8(string_utf8); | std::wstring attrsW = wstring_from_utf8(string_utf8); | ||||
utf16string string(attrsW.begin(), attrsW.end()); | utf16string string(attrsW.begin(), attrsW.end()); | ||||
if (JS_ParseJSON(m->m_cx, reinterpret_cast<const char16_t*>(string.c_str()), (u32)string.size(), out)) | if (JS_ParseJSON(m->m_cx, reinterpret_cast<const char16_t*>(string.c_str()), (u32)string.size(), out)) | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
// TODO: It's not quite clear why JS_Stringify needs JS::MutableHandleValue. |obj| should not get modified. | // 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. | // It probably has historical reasons and could be changed by SpiderMonkey in the future. | ||||
std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) const | std::string ScriptInterface::StringifyJSON(JS::MutableHandleValue obj, bool indent) const | ||||
{ | { | ||||
JSAutoRequest rq(m->m_cx); | JSAutoRequest rq(m->m_cx); | ||||
Stringifier str; | Stringifier str; | ||||
JS::RootedValue indentVal(m->m_cx, indent ? JS::Int32Value(2) : JS::UndefinedValue()); | 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)) | if (!JS_Stringify(m->m_cx, obj, nullptr, indentVal, &Stringifier::callback, &str)) | ||||
{ | { | ||||
JS_ClearPendingException(m->m_cx); | JS_ClearPendingException(m->m_cx); | ||||
LOGERROR("StringifyJSON failed"); | LOGERROR("StringifyJSON failed"); | ||||
return std::string(); | return std::string(); | ||||
} | } | ||||
return str.stream.str(); | return str.stream.str(); | ||||
} | } | ||||
Show All 11 Lines | std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) const | ||||
if (pretty) | if (pretty) | ||||
{ | { | ||||
Stringifier str; | Stringifier str; | ||||
JS::RootedValue indentVal(m->m_cx, JS::Int32Value(2)); | JS::RootedValue indentVal(m->m_cx, JS::Int32Value(2)); | ||||
// Temporary disable the error reporter, so we don't print complaints about cyclic values | // Temporary disable the error reporter, so we don't print complaints about cyclic values | ||||
JSErrorReporter er = JS_SetErrorReporter(m->m_runtime->m_rt, NULL); | JSErrorReporter er = JS_SetErrorReporter(m->m_runtime->m_rt, NULL); | ||||
bool ok = JS_Stringify(m->m_cx, obj, JS::NullPtr(), indentVal, &Stringifier::callback, &str); | bool ok = JS_Stringify(m->m_cx, obj, nullptr, indentVal, &Stringifier::callback, &str); | ||||
// Restore error reporter | // Restore error reporter | ||||
JS_SetErrorReporter(m->m_runtime->m_rt, er); | JS_SetErrorReporter(m->m_runtime->m_rt, er); | ||||
if (ok) | if (ok) | ||||
return str.stream.str(); | return str.stream.str(); | ||||
// Clear the exception set when Stringify failed | // Clear the exception set when Stringify failed | ||||
▲ Show 20 Lines • Show All 85 Lines • Show Last 20 Lines |
Unless there is a superseding reason for it, I assume there is a missing JSAutoRequest(cx) here.
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS::Rooted
https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference/JS_GetPendingException