1
0
forked from mirrors/0ad

Explicitly make ScriptInterface a Compartment wrapper.

ScriptInterface is now a wrapper around a JSCompartment, and thus always
has a well-defined global.

The error reporter is moved to ScriptRuntime in anticipation of that
handling JSContext in a later diff.

Part of the SM52 migration, stage: SM45 compatible.

Patch by: Itms
Tested By: Freagarach
Refs #4893

Differential Revision: https://code.wildfiregames.com/D3090
This was SVN commit r24180.
This commit is contained in:
wraitii
2020-11-14 08:46:32 +00:00
parent 0046783e73
commit aae417bd29
24 changed files with 210 additions and 188 deletions
+41 -102
View File
@@ -62,24 +62,32 @@ struct ScriptInterface_impl
// members have to be called before the runtime destructor.
shared_ptr<ScriptRuntime> m_runtime;
JS::PersistentRootedObject m_glob; // global scope object
boost::rand48* m_rng;
JS::PersistentRootedObject m_nativeScope; // native function scope object
friend ScriptInterface::Request;
private:
JSContext* m_cx;
JSCompartment* m_formerCompartment;
JS::PersistentRootedObject m_glob; // global scope object
public:
boost::rand48* m_rng;
JS::PersistentRootedObject m_nativeScope; // native function scope object
};
ScriptInterface::Request::Request(const ScriptInterface& scriptInterface) :
cx(scriptInterface.m->m_cx)
{
JS_BeginRequest(cx);
m_formerCompartment = JS_EnterCompartment(cx, scriptInterface.m->m_glob);
glob = JS::CurrentGlobalOrNull(cx);
}
JS::Value ScriptInterface::Request::globalValue() const
{
return JS::ObjectValue(*glob);
}
ScriptInterface::Request::~Request()
{
JS_LeaveCompartment(cx, m_formerCompartment);
JS_EndRequest(cx);
}
@@ -95,46 +103,6 @@ JSClass global_class = {
JS_GlobalObjectTraceHook
};
void ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
{
ScriptInterface::Request rq(*ScriptInterface::GetScriptInterfaceAndCBData(cx)->pScriptInterface);
std::stringstream msg;
bool isWarning = JSREPORT_IS_WARNING(report->flags);
msg << (isWarning ? "JavaScript warning: " : "JavaScript error: ");
if (report->filename)
{
msg << report->filename;
msg << " line " << report->lineno << "\n";
}
msg << message;
// If there is an exception, then print its stack trace
JS::RootedValue excn(cx);
if (JS_GetPendingException(cx, &excn) && excn.isObject())
{
JS::RootedValue stackVal(cx);
JS::RootedObject excnObj(cx, &excn.toObject());
JS_GetProperty(cx, excnObj, "stack", &stackVal);
std::string stackText;
ScriptInterface::FromJSVal(rq, stackVal, stackText);
std::istringstream stream(stackText);
for (std::string line; std::getline(stream, line);)
msg << "\n " << line;
}
if (isWarning)
LOGWARNING("%s", msg.str().c_str());
else
LOGERROR("%s", msg.str().c_str());
// When running under Valgrind, print more information in the error message
// VALGRIND_PRINTF_BACKTRACE("->");
}
// Functions in the global namespace:
bool print(JSContext* cx, uint argc, JS::Value* vp)
@@ -351,70 +319,47 @@ bool ScriptInterface::MathRandom(double& nbr)
}
ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const shared_ptr<ScriptRuntime>& runtime) :
m_runtime(runtime), m_glob(runtime->m_rt), m_nativeScope(runtime->m_rt)
m_runtime(runtime), m_cx(runtime->GetGeneralJSContext()), m_glob(runtime->GetJSRuntime()), m_nativeScope(runtime->GetJSRuntime())
{
m_cx = JS_NewContext(m_runtime->m_rt, STACK_CHUNK_SIZE);
ENSURE(m_cx);
JS_SetOffthreadIonCompilationEnabled(m_runtime->m_rt, true);
// For GC debugging:
// JS_SetGCZeal(m_cx, 2, JS_DEFAULT_ZEAL_FREQ);
JS_SetContextPrivate(m_cx, NULL);
JS_SetErrorReporter(m_runtime->m_rt, ErrorReporter);
JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_ION_ENABLE, 1);
JS_SetGlobalJitCompilerOption(m_runtime->m_rt, JSJITCOMPILER_BASELINE_ENABLE, 1);
JS::RuntimeOptionsRef(m_cx)
.setExtraWarnings(true)
.setWerror(false)
.setStrictMode(true);
JS::CompartmentOptions opt;
opt.setVersion(JSVERSION_LATEST);
// Keep JIT code during non-shrinking GCs. This brings a quite big performance improvement.
opt.setPreserveJitCode(true);
JSAutoRequest rq(m_cx);
JS::RootedObject globalRootedVal(m_cx, JS_NewGlobalObject(m_cx, &global_class, NULL, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt));
m_formerCompartment = JS_EnterCompartment(m_cx, globalRootedVal);
ENSURE(JS_InitStandardClasses(m_cx, globalRootedVal));
m_glob = globalRootedVal.get();
m_glob = JS_NewGlobalObject(m_cx, &global_class, nullptr, JS::OnNewGlobalHookOption::FireOnNewGlobalHook, opt);
JS_DefineProperty(m_cx, m_glob, "global", globalRootedVal, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JSAutoCompartment autoCmpt(m_cx, m_glob);
ENSURE(JS_InitStandardClasses(m_cx, m_glob));
JS_DefineProperty(m_cx, m_glob, "global", m_glob, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, nullptr, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "clone", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, globalRootedVal, "deepfreeze", ::deepfreeze, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "log", ::logmsg, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "warn", ::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "error", ::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "clone", ::deepcopy, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "deepfreeze", ::deepfreeze, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
Register("ProfileStart", ::ProfileStart, 1);
Register("ProfileStop", ::ProfileStop, 0);
Register("ProfileAttribute", ::ProfileAttribute, 1);
runtime->RegisterContext(m_cx);
m_runtime->RegisterCompartment(js::GetObjectCompartment(m_glob));
}
ScriptInterface_impl::~ScriptInterface_impl()
{
m_runtime->UnRegisterContext(m_cx);
{
JSAutoRequest rq(m_cx);
JS_LeaveCompartment(m_cx, m_formerCompartment);
}
JS_DestroyContext(m_cx);
m_runtime->UnRegisterCompartment(js::GetObjectCompartment(m_glob));
}
void ScriptInterface_impl::Register(const char* name, JSNative fptr, uint nargs) const
{
JSAutoRequest rq(m_cx);
JSAutoCompartment autoCmpt(m_cx, m_glob);
JS::RootedObject nativeScope(m_cx, m_nativeScope);
JS::RootedFunction func(m_cx, JS_DefineFunction(m_cx, nativeScope, name, fptr, nargs, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT));
}
@@ -431,7 +376,7 @@ ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugN
Request rq(this);
m_CmptPrivate.pScriptInterface = this;
JS_SetContextPrivate(rq.cx, (void*)&m_CmptPrivate);
JS_SetCompartmentPrivate(js::GetObjectCompartment(rq.glob), (void*)&m_CmptPrivate);
}
ScriptInterface::~ScriptInterface()
@@ -450,7 +395,7 @@ void ScriptInterface::SetCallbackData(void* pCBData)
ScriptInterface::CmptPrivate* ScriptInterface::GetScriptInterfaceAndCBData(JSContext* cx)
{
CmptPrivate* pCmptPrivate = (CmptPrivate*)JS_GetContextPrivate(cx);
CmptPrivate* pCmptPrivate = (CmptPrivate*)JS_GetCompartmentPrivate(js::GetContextCompartment(cx));
return pCmptPrivate;
}
@@ -478,7 +423,7 @@ bool ScriptInterface::ReplaceNondeterministicRNG(boost::rand48& rng)
{
Request rq(this);
JS::RootedValue math(rq.cx);
JS::RootedObject global(rq.cx, m->m_glob);
JS::RootedObject global(rq.cx, rq.glob);
if (JS_GetProperty(rq.cx, global, "Math", &math) && math.isObject())
{
JS::RootedObject mathObj(rq.cx, &math.toObject());
@@ -502,7 +447,7 @@ void ScriptInterface::Register(const char* name, JSNative fptr, size_t nargs) co
JSRuntime* ScriptInterface::GetJSRuntime() const
{
return m->m_runtime->m_rt;
return m->m_runtime->GetJSRuntime();
}
shared_ptr<ScriptRuntime> ScriptInterface::GetRuntime() const
@@ -535,7 +480,7 @@ void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructo
throw PSERROR_Scripting_DefineType_AlreadyExists();
}
JS::RootedObject global(rq.cx, m->m_glob);
JS::RootedObject global(rq.cx, rq.glob);
JS::RootedObject obj(rq.cx, JS_InitClass(rq.cx, global, nullptr,
clasp,
constructor, minArgs, // Constructor, min args
@@ -597,16 +542,10 @@ void ScriptInterface::CreateArray(const Request& rq, JS::MutableHandleValue obje
throw PSERROR_Scripting_CreateObjectFailed();
}
JS::Value ScriptInterface::GetGlobalObject() const
{
Request rq(this);
return JS::ObjectValue(*JS::CurrentGlobalOrNull(rq.cx));
}
bool ScriptInterface::SetGlobal_(const char* name, JS::HandleValue value, bool replace, bool constant, bool enumerate)
{
Request rq(this);
JS::RootedObject global(rq.cx, m->m_glob);
JS::RootedObject global(rq.cx, rq.glob);
bool found;
if (!JS_HasProperty(rq.cx, global, name, &found))
@@ -834,7 +773,7 @@ bool ScriptInterface::FreezeObject(JS::HandleValue objVal, bool deep) const
bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& code) const
{
Request rq(this);
JS::RootedObject global(rq.cx, m->m_glob);
JS::RootedObject global(rq.cx, rq.glob);
utf16string codeUtf16(code.begin(), code.end());
uint lineNo = 1;
// CompileOptions does not copy the contents of the filename string pointer.
@@ -1036,12 +975,12 @@ std::string ScriptInterface::ToString(JS::MutableHandleValue obj, bool pretty) c
JS::RootedValue indentVal(rq.cx, JS::Int32Value(2));
// 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(GetJSRuntime(), nullptr);
bool ok = JS_Stringify(rq.cx, obj, nullptr, indentVal, &Stringifier::callback, &str);
// Restore error reporter
JS_SetErrorReporter(m->m_runtime->m_rt, er);
JS_SetErrorReporter(GetJSRuntime(), er);
if (ok)
return str.stream.str();
@@ -1077,12 +1016,12 @@ bool ScriptInterface::IsExceptionPending(const Request& rq)
return JS_IsExceptionPending(rq.cx) ? true : false;
}
JS::Value ScriptInterface::CloneValueFromOtherContext(const ScriptInterface& otherContext, JS::HandleValue val) const
JS::Value ScriptInterface::CloneValueFromOtherCompartment(const ScriptInterface& otherCompartment, JS::HandleValue val) const
{
PROFILE("CloneValueFromOtherContext");
PROFILE("CloneValueFromOtherCompartment");
Request rq(this);
JS::RootedValue out(rq.cx);
shared_ptr<StructuredClone> structuredClone = otherContext.WriteStructuredClone(val);
shared_ptr<StructuredClone> structuredClone = otherCompartment.WriteStructuredClone(val);
ReadStructuredClone(structuredClone, &out);
return out.get();
}