Removes g_ScriptingHost and implements global to compartment 1 to 1 relation.

Each GUI Page gets its own compartment and all ScriptInterfaces in the
same thread should now use the same JS Runtime.
This is required for the SpiderMonkey upgrade.
Check the ticket for details.

Closes #2241
Refs #1886
Refs #1966

This was SVN commit r14496.
This commit is contained in:
Yves
2014-01-04 10:14:53 +00:00
parent 743644f5ce
commit 4b1297b328
99 changed files with 1733 additions and 2313 deletions
+2 -2
View File
@@ -40,7 +40,7 @@
// Define RegisterFunction<TR, T0..., f>
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( void* T0_TAIL(z,i) )> \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CxPrivate* T0_TAIL(z,i) )> \
void RegisterFunction(const char* name) { \
Register(name, call<R, T0_HEAD(z,i) fptr>, nargs<0 T0_TAIL(z,i)>()); \
}
@@ -50,7 +50,7 @@ BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
// JSFastNative-compatible function that wraps the function identified in the template argument list
// (Definition comes later, since it depends on some things we haven't defined yet)
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( void* T0_TAIL(z,i) )> \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CxPrivate* T0_TAIL(z,i) )> \
static JSBool call(JSContext* cx, uintN argc, jsval* vp);
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
+3 -3
View File
@@ -27,7 +27,7 @@ struct ScriptInterface_NativeWrapper {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename F> \
static void call(JSContext* cx, jsval& rval, F fptr T0_A0(z,i)) { \
rval = ScriptInterface::ToJSVal<R>(cx, fptr(ScriptInterface::GetCallbackData(cx) A0_TAIL(z,i))); \
rval = ScriptInterface::ToJSVal<R>(cx, fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx) A0_TAIL(z,i))); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
@@ -40,7 +40,7 @@ struct ScriptInterface_NativeWrapper<void> {
#define OVERLOADS(z, i, data) \
template<TYPENAME_T0_HEAD(z,i) typename F> \
static void call(JSContext* cx, jsval& /*rval*/, F fptr T0_A0(z,i)) { \
fptr(ScriptInterface::GetCallbackData(cx) A0_TAIL(z,i)); \
fptr(ScriptInterface::GetScriptInterfaceAndCBData(cx) A0_TAIL(z,i)); \
}
BOOST_PP_REPEAT(SCRIPT_INTERFACE_MAX_ARGS, OVERLOADS, ~)
#undef OVERLOADS
@@ -90,7 +90,7 @@ struct ScriptInterface_NativeMethodWrapper<void, TC> {
// JSFastNative-compatible function that wraps the function identified in the template argument list
#define OVERLOADS(z, i, data) \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( void* T0_TAIL(z,i) )> \
template <typename R, TYPENAME_T0_HEAD(z,i) R (*fptr) ( ScriptInterface::CxPrivate* T0_TAIL(z,i) )> \
JSBool ScriptInterface::call(JSContext* cx, uintN argc, jsval* vp) { \
UNUSED2(argc); \
SCRIPT_PROFILE \
+64 -34
View File
@@ -69,7 +69,7 @@ class ScriptRuntime
{
public:
ScriptRuntime(int runtimeSize) :
m_rooter(NULL), m_compartmentGlobal(NULL)
m_rooter(NULL)
{
m_rt = JS_NewRuntime(runtimeSize);
ENSURE(m_rt); // TODO: error handling
@@ -98,8 +98,6 @@ public:
JSRuntime* m_rt;
AutoGCRooter* m_rooter;
JSObject* m_compartmentGlobal;
private:
@@ -241,7 +239,6 @@ struct ScriptInterface_impl
JSContext* m_cx;
JSObject* m_glob; // global scope object
JSObject* m_nativeScope; // native function scope object
JSCrossCompartmentCall* m_call;
};
namespace
@@ -507,24 +504,9 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
}
JS_SetOptions(m_cx, options);
JS_SetVersion(m_cx, JSVERSION_LATEST);
// Threadsafe SpiderMonkey requires that we have a request before doing anything much
JS_BeginRequest(m_cx);
// We only want a single compartment per runtime
if (m_runtime->m_compartmentGlobal)
{
m_call = JS_EnterCrossCompartmentCall(m_cx, m_runtime->m_compartmentGlobal);
m_glob = JS_NewGlobalObject(m_cx, &global_class);
}
else
{
m_call = NULL;
m_glob = JS_NewCompartmentAndGlobalObject(m_cx, &global_class, NULL);
m_runtime->m_compartmentGlobal = m_glob;
}
m_glob = JS_NewCompartmentAndGlobalObject(m_cx, &global_class, NULL);
ok = JS_InitStandardClasses(m_cx, m_glob);
ENSURE(ok);
@@ -547,9 +529,6 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, const sh
ScriptInterface_impl::~ScriptInterface_impl()
{
if (m_call)
JS_LeaveCrossCompartmentCall(m_call);
JS_EndRequest(m_cx);
JS_DestroyContext(m_cx);
}
@@ -594,6 +573,9 @@ ScriptInterface::ScriptInterface(const char* nativeScopeName, const char* debugN
else
g_DebuggingServer->RegisterScriptinterface(debugName, this);
}
m_CxPrivate.pScriptInterface = this;
JS_SetContextPrivate(m->m_cx, (void*)&m_CxPrivate);
}
ScriptInterface::~ScriptInterface()
@@ -614,14 +596,15 @@ void ScriptInterface::ShutDown()
JS_ShutDown();
}
void ScriptInterface::SetCallbackData(void* cbdata)
void ScriptInterface::SetCallbackData(void* pCBData)
{
JS_SetContextPrivate(m->m_cx, cbdata);
m_CxPrivate.pCBData = pCBData;
}
void* ScriptInterface::GetCallbackData(JSContext* cx)
ScriptInterface::CxPrivate* ScriptInterface::GetScriptInterfaceAndCBData(JSContext* cx)
{
return JS_GetContextPrivate(cx);
CxPrivate* pCxPrivate = (CxPrivate*)JS_GetContextPrivate(cx);
return pCxPrivate;
}
bool ScriptInterface::LoadGlobalScripts()
@@ -674,11 +657,16 @@ JSContext* ScriptInterface::GetContext() const
return m->m_cx;
}
JSRuntime* ScriptInterface::GetRuntime() const
JSRuntime* ScriptInterface::GetJSRuntime() const
{
return m->m_runtime->m_rt;
}
shared_ptr<ScriptRuntime> ScriptInterface::GetRuntime() const
{
return m->m_runtime;
}
AutoGCRooter* ScriptInterface::ReplaceAutoGCRooter(AutoGCRooter* rooter)
{
AutoGCRooter* ret = m->m_runtime->m_rooter;
@@ -734,6 +722,47 @@ jsval ScriptInterface::NewObjectFromConstructor(jsval ctor)
return OBJECT_TO_JSVAL(obj);
}
void ScriptInterface::DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
{
std::string typeName = clasp->name;
if (m_CustomObjectTypes.find(typeName) != m_CustomObjectTypes.end())
{
// This type already exists
throw PSERROR_Scripting_DefineType_AlreadyExists();
}
JSObject * obj = JS_InitClass( m->m_cx, JSVAL_TO_OBJECT(GetGlobalObject()), 0,
clasp,
constructor, minArgs, // Constructor, min args
ps, fs, // Properties, methods
static_ps, static_fs); // Constructor properties, methods
if (obj == NULL)
throw PSERROR_Scripting_DefineType_CreationFailed();
CustomType type;
type.m_Object = obj;
type.m_Class = clasp;
type.m_Constructor = constructor;
m_CustomObjectTypes[typeName] = type;
}
JSObject* ScriptInterface::CreateCustomObject(const std::string & typeName)
{
std::map < std::string, CustomType > ::iterator it = m_CustomObjectTypes.find(typeName);
if (it == m_CustomObjectTypes.end())
throw PSERROR_Scripting_TypeDoesNotExist();
JSFunction* ctor = JS_NewFunction(m->m_cx, (*it).second.m_Constructor, 0, 0,
NULL, "ctor_fun");
return JS_New(m->m_cx, JS_GetFunctionObject(ctor), 0, NULL);
}
bool ScriptInterface::CallFunctionVoid(jsval val, const char* name)
{
jsval jsRet;
@@ -951,11 +980,11 @@ bool ScriptInterface::LoadScript(const VfsPath& filename, const std::string& cod
return ok ? true : false;
}
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::string& code)
bool ScriptInterface::LoadGlobalScript(const VfsPath& filename, const std::wstring& code)
{
// Compile the code in strict mode, to encourage better coding practices and
// to possibly help SpiderMonkey with optimisations
std::wstring codeStrict = L"\"use strict\";\n" + wstring_from_utf8(code);
std::wstring codeStrict = L"\"use strict\";\n" + code;
utf16string codeUtf16(codeStrict.begin(), codeStrict.end());
uintN lineNo = 0; // put the automatic 'use strict' on line 0, so the real code starts at line 1
@@ -1187,9 +1216,9 @@ void ScriptInterface::DumpHeap()
#if MOZJS_DEBUG_ABI
JS_DumpHeap(m->m_cx, stderr, NULL, 0, NULL, (size_t)-1, NULL);
#endif
fprintf(stderr, "# Bytes allocated: %u\n", JS_GetGCParameter(GetRuntime(), JSGC_BYTES));
fprintf(stderr, "# Bytes allocated: %u\n", JS_GetGCParameter(GetJSRuntime(), JSGC_BYTES));
JS_GC(m->m_cx);
fprintf(stderr, "# Bytes allocated after GC: %u\n", JS_GetGCParameter(GetRuntime(), JSGC_BYTES));
fprintf(stderr, "# Bytes allocated after GC: %u\n", JS_GetGCParameter(GetJSRuntime(), JSGC_BYTES));
}
void ScriptInterface::MaybeGC()
@@ -1337,9 +1366,10 @@ shared_ptr<ScriptInterface::StructuredClone> ScriptInterface::WriteStructuredClo
uint64* data = NULL;
size_t nbytes = 0;
if (!JS_WriteStructuredClone(m->m_cx, v, &data, &nbytes, NULL, NULL))
{
debug_warn(L"Writing a structured clone with JS_WriteStructuredClone failed!");
return shared_ptr<StructuredClone>();
// TODO: should we have better error handling?
// Currently we'll probably continue and then crash in ReadStructuredClone
}
shared_ptr<StructuredClone> ret (new StructuredClone);
ret->m_Context = m->m_cx;
+44 -4
View File
@@ -27,6 +27,25 @@
#include "js/jsapi.h"
#include "ps/Errors.h"
ERROR_GROUP(Scripting);
ERROR_TYPE(Scripting, SetupFailed);
ERROR_SUBGROUP(Scripting, LoadFile);
ERROR_TYPE(Scripting_LoadFile, OpenFailed);
ERROR_TYPE(Scripting_LoadFile, EvalErrors);
ERROR_TYPE(Scripting, ConversionFailed);
ERROR_TYPE(Scripting, CallFunctionFailed);
ERROR_TYPE(Scripting, RegisterFunctionFailed);
ERROR_TYPE(Scripting, DefineConstantFailed);
ERROR_TYPE(Scripting, CreateObjectFailed);
ERROR_TYPE(Scripting, TypeDoesNotExist);
ERROR_SUBGROUP(Scripting, DefineType);
ERROR_TYPE(Scripting_DefineType, AlreadyExists);
ERROR_TYPE(Scripting_DefineType, CreationFailed);
#include "lib/file/vfs/vfs_path.h"
#include "ps/Profile.h"
#include "ps/utf16string.h"
@@ -47,6 +66,8 @@ struct ScriptInterface_impl;
class ScriptRuntime;
extern shared_ptr<ScriptRuntime> g_ScriptRuntime;
class CDebuggingServer;
/**
@@ -87,11 +108,18 @@ public:
*/
static void ShutDown();
void SetCallbackData(void* cbdata);
static void* GetCallbackData(JSContext* cx);
struct CxPrivate
{
ScriptInterface* pScriptInterface; // the ScriptInterface object the current context belongs to
void* pCBData; // meant to be used as the "this" object for callback functions
} m_CxPrivate;
void SetCallbackData(void* pCBData);
static CxPrivate* GetScriptInterfaceAndCBData(JSContext* cx);
JSContext* GetContext() const;
JSRuntime* GetRuntime() const;
JSRuntime* GetJSRuntime() const;
shared_ptr<ScriptRuntime> GetRuntime() const;
/**
* Load global scripts that most script contexts need,
@@ -170,6 +198,9 @@ public:
template<typename T0, typename T1, typename T2, typename T3, typename R>
bool CallFunction(jsval val, const char* name, const T0& a0, const T1& a1, const T2& a2, const T3& a3, R& ret);
JSObject* CreateCustomObject(const std::string & typeName);
void DefineCustomObjectType(JSClass *clasp, JSNative constructor, uint minArgs, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs);
jsval GetGlobalObject();
JSClass* GetGlobalClass();
@@ -268,7 +299,7 @@ public:
* @param code JS code to execute
* @return true on successful compilation and execution; false otherwise
*/
bool LoadGlobalScript(const VfsPath& filename, const std::string& code);
bool LoadGlobalScript(const VfsPath& filename, const std::wstring& code);
/**
* Load and execute the given script in the global scope.
@@ -342,8 +373,17 @@ private:
static JSClass* GetClass(JSContext* cx, JSObject* obj);
static void* GetPrivate(JSContext* cx, JSObject* obj);
class CustomType
{
public:
JSObject * m_Object;
JSClass * m_Class;
JSNative m_Constructor;
};
void Register(const char* name, JSNative fptr, size_t nargs);
std::auto_ptr<ScriptInterface_impl> m;
std::map<std::string, CustomType> m_CustomObjectTypes;
// The nasty macro/template bits are split into a separate file so you don't have to look at them
public:
+4 -4
View File
@@ -86,28 +86,28 @@ CStr CScriptStatsTable::GetCellText(size_t row, size_t col)
{
if (col == 0)
return "max nominal heap bytes";
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_MAX_BYTES);
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_MAX_BYTES);
return CStr::FromUInt(n);
}
case Row_MaxMallocBytes:
{
if (col == 0)
return "max JS_malloc bytes";
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_MAX_MALLOC_BYTES);
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_MAX_MALLOC_BYTES);
return CStr::FromUInt(n);
}
case Row_Bytes:
{
if (col == 0)
return "allocated bytes";
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_BYTES);
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_BYTES);
return CStr::FromUInt(n);
}
case Row_NumberGC:
{
if (col == 0)
return "number of GCs";
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetRuntime(), JSGC_NUMBER);
uint32_t n = JS_GetGCParameter(m_ScriptInterfaces.at(col-1).first->GetJSRuntime(), JSGC_NUMBER);
return CStr::FromUInt(n);
}
default:
+15 -15
View File
@@ -289,10 +289,10 @@ CThreadDebugger::~CThreadDebugger()
ReturnActiveBreakPoints(NULL);
// Remove all the hooks because they store a pointer to this object
JS_SetExecuteHook(m->m_pScriptInterface->GetRuntime(), NULL, NULL);
JS_SetCallHook(m->m_pScriptInterface->GetRuntime(), NULL, NULL);
JS_SetNewScriptHook(m->m_pScriptInterface->GetRuntime(), NULL, NULL);
JS_SetDestroyScriptHook(m->m_pScriptInterface->GetRuntime(), NULL, NULL);
JS_SetExecuteHook(m->m_pScriptInterface->GetJSRuntime(), NULL, NULL);
JS_SetCallHook(m->m_pScriptInterface->GetJSRuntime(), NULL, NULL);
JS_SetNewScriptHook(m->m_pScriptInterface->GetJSRuntime(), NULL, NULL);
JS_SetDestroyScriptHook(m->m_pScriptInterface->GetJSRuntime(), NULL, NULL);
}
void CThreadDebugger::ReturnActiveBreakPoints(jsbytecode* pBytecode)
@@ -329,16 +329,16 @@ void CThreadDebugger::Initialize(uint id, std::string name, ScriptInterface* pSc
m->m_Name = name;
m->m_pScriptInterface = pScriptInterface;
m->m_pDebuggingServer = pDebuggingServer;
JS_SetExecuteHook(m->m_pScriptInterface->GetRuntime(), CallHook_, (void*)this);
JS_SetCallHook(m->m_pScriptInterface->GetRuntime(), CallHook_, (void*)this);
JS_SetNewScriptHook(m->m_pScriptInterface->GetRuntime(), NewScriptHook_, (void*)this);
JS_SetDestroyScriptHook(m->m_pScriptInterface->GetRuntime(), DestroyScriptHook_, (void*)this);
JS_SetThrowHook(m->m_pScriptInterface->GetRuntime(), ThrowHandler_, (void*)this);
JS_SetExecuteHook(m->m_pScriptInterface->GetJSRuntime(), CallHook_, (void*)this);
JS_SetCallHook(m->m_pScriptInterface->GetJSRuntime(), CallHook_, (void*)this);
JS_SetNewScriptHook(m->m_pScriptInterface->GetJSRuntime(), NewScriptHook_, (void*)this);
JS_SetDestroyScriptHook(m->m_pScriptInterface->GetJSRuntime(), DestroyScriptHook_, (void*)this);
JS_SetThrowHook(m->m_pScriptInterface->GetJSRuntime(), ThrowHandler_, (void*)this);
if (m->m_pDebuggingServer->GetSettingSimultaneousThreadBreak())
{
// Setup a handler to check for break-requests from the DebuggingServer regularly
JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), CheckForBreakRequestHandler_, (void*)this);
JS_SetInterrupt(m->m_pScriptInterface->GetJSRuntime(), CheckForBreakRequestHandler_, (void*)this);
}
}
@@ -456,7 +456,7 @@ JSTrapStatus CThreadDebugger::BreakHandler(JSContext* cx, JSScript* script, jsby
if (breakSrc == BREAK_SRC_INTERRUP)
{
JS_ClearInterrupt(m->m_pScriptInterface->GetRuntime(), NULL, NULL);
JS_ClearInterrupt(m->m_pScriptInterface->GetJSRuntime(), NULL, NULL);
JS_SetSingleStepMode(cx, script, false);
}
@@ -496,17 +496,17 @@ JSTrapStatus CThreadDebugger::BreakHandler(JSContext* cx, JSScript* script, jsby
{
if (nextDbgCmd == DBG_CMD_SINGLESTEP)
{
JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepHandler_, this);
JS_SetInterrupt(m->m_pScriptInterface->GetJSRuntime(), StepHandler_, this);
break;
}
else if (nextDbgCmd == DBG_CMD_STEPINTO)
{
JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepIntoHandler_, this);
JS_SetInterrupt(m->m_pScriptInterface->GetJSRuntime(), StepIntoHandler_, this);
break;
}
else if (nextDbgCmd == DBG_CMD_STEPOUT)
{
JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), StepOutHandler_, this);
JS_SetInterrupt(m->m_pScriptInterface->GetJSRuntime(), StepOutHandler_, this);
break;
}
}
@@ -518,7 +518,7 @@ JSTrapStatus CThreadDebugger::BreakHandler(JSContext* cx, JSScript* script, jsby
else
{
// Setup a handler to check for break-requests from the DebuggingServer regularly
JS_SetInterrupt(m->m_pScriptInterface->GetRuntime(), CheckForBreakRequestHandler_, this);
JS_SetInterrupt(m->m_pScriptInterface->GetJSRuntime(), CheckForBreakRequestHandler_, this);
}
break;
}