Changeset View
Changeset View
Standalone View
Standalone View
ps/trunk/source/scriptinterface/ScriptInterface.cpp
Show First 20 Lines • Show All 346 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) | JS::RuntimeOptionsRef(m_cx) | ||||
.setExtraWarnings(true) | .setExtraWarnings(true) | ||||
.setWerror(false) | .setWerror(false) | ||||
.setVarObjFix(true) | |||||
.setStrictMode(true); | .setStrictMode(true); | ||||
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 154 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, | ||||
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(); | ||||
CustomType& type = m_CustomObjectTypes[typeName]; | CustomType& type = m_CustomObjectTypes[typeName]; | ||||
type.m_Prototype.init(m->m_cx, obj); | type.m_Prototype.init(m->m_cx, obj); | ||||
type.m_Class = clasp; | type.m_Class = clasp; | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate) | ||||
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::Rooted<JSPropertyDescriptor> desc(m->m_cx); | JS::Rooted<JSPropertyDescriptor> desc(m->m_cx); | ||||
if (!JS_GetOwnPropertyDescriptor(m->m_cx, global, name, &desc)) | if (!JS_GetOwnPropertyDescriptor(m->m_cx, global, name, &desc)) | ||||
return false; | return false; | ||||
if (desc.isReadonly()) | if (!desc.writable()) | ||||
{ | { | ||||
if (!replace) | if (!replace) | ||||
{ | { | ||||
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; | ||||
} | } | ||||
// This is not supposed to happen, unless the user has called SetProperty with constant = true on the global object | // This is not supposed to happen, unless the user has called SetProperty with constant = true on the global object | ||||
// instead of using SetGlobal. | // instead of using SetGlobal. | ||||
if (desc.isPermanent()) | if (!desc.configurable()) | ||||
{ | { | ||||
JS_ReportError(m->m_cx, "The global \"%s\" is permanent and cannot be hotloaded", name); | JS_ReportError(m->m_cx, "The global \"%s\" is permanent and cannot be hotloaded", name); | ||||
return false; | return false; | ||||
} | } | ||||
LOGMESSAGE("Hotloading new value for global \"%s\".", name); | LOGMESSAGE("Hotloading new value for global \"%s\".", name); | ||||
ENSURE(JS_DeleteProperty(m->m_cx, global, name)); | ENSURE(JS_DeleteProperty(m->m_cx, global, name)); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 139 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, JS::IdVector(m->m_cx)); | ||||
if (!props) | if (!JS_Enumerate(m->m_cx, obj, &props)) | ||||
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); | options.setIsRunOnce(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 |
Wildfire Games · Phabricator