From 64bd264fc0f3a3665ebd40d252d1102797b39cda Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sat, 20 Nov 2010 00:45:58 +0000 Subject: [PATCH] Attempt to fix GUI script interaction with JITs. Simplify some other GUI script interface code. This was SVN commit r8657. --- source/gui/CGUI.cpp | 80 +++++++++++++++++-- source/gui/IGUIObject.cpp | 26 ++---- .../gui/scripting/JSInterface_IGUIObject.cpp | 4 +- 3 files changed, 81 insertions(+), 29 deletions(-) diff --git a/source/gui/CGUI.cpp b/source/gui/CGUI.cpp index b51947b4ed..322de937d5 100644 --- a/source/gui/CGUI.cpp +++ b/source/gui/CGUI.cpp @@ -343,20 +343,84 @@ void CGUI::SendEventToAll(const CStr& EventName) //------------------------------------------------------------------- // Constructor / Destructor //------------------------------------------------------------------- + +// To isolate the vars declared by each GUI page, we need to create +// a pseudo-global object to declare them in. In particular, it must +// have no parent object, so it must be declared with JS_NewGlobalObject. +// But GUI scripts should have access to the real global's properties +// (Array, undefined, getGUIObjectByName, etc), so we add a custom resolver +// that defers to the real global object when necessary. + +static JSBool GetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, jsval* vp) +{ + return JS_GetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, vp); +} + +static JSBool SetGlobalProperty(JSContext* cx, JSObject* UNUSED(obj), jsid id, jsval* vp) +{ + return JS_SetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, vp); +} + +static JSBool ResolveGlobalProperty(JSContext* cx, JSObject* obj, jsid id, uintN flags, JSObject** objp) +{ + // This gets called when the property can't be resolved in the page_global object. + + // Warning: The interaction between this resolution stuff and the JITs appears + // to be quite fragile, and I don't quite understand what the constraints are. + // If changing it, be careful to test with each JIT to make sure it works. + // (This code is somewhat based on GPSEE's module system.) + + // Declarations and assignments shouldn't affect the real global + if (flags & (JSRESOLVE_DECLARING | JSRESOLVE_ASSIGNING)) + { + // Can't be resolved - return NULL + *objp = NULL; + return JS_TRUE; + } + + // Check whether the real global object defined this property + uintN attrs; + JSBool found; + if (!JS_GetPropertyAttrsGetterAndSetterById(cx, g_ScriptingHost.GetGlobalObject(), id, &attrs, &found, NULL, NULL)) + return JS_FALSE; + + if (!found) + { + // Not found on real global, so can't be resolved - return NULL + *objp = NULL; + return JS_TRUE; + } + + // Retrieve the property value from the global + jsval v; + if (!JS_GetPropertyById(cx, g_ScriptingHost.GetGlobalObject(), id, &v)) + return JS_FALSE; + + // Add the global's property value onto this object, with getter/setter that will + // update the global's copy instead of this copy, and then return this object + if (!JS_DefinePropertyById(cx, obj, id, v, GetGlobalProperty, SetGlobalProperty, attrs)) + return JS_FALSE; + + *objp = obj; + return JS_TRUE; +} + +static JSClass page_global_class = { + "page_global", JSCLASS_GLOBAL_FLAGS | JSCLASS_NEW_RESOLVE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, (JSResolveOp)ResolveGlobalProperty, JS_ConvertStub, JS_FinalizeStub, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + CGUI::CGUI() : m_MouseButtons(0), m_FocusedObject(NULL), m_InternalNameNumber(0) { m_BaseObject = new CGUIDummyObject; m_BaseObject->SetGUI(this); - // Construct the root object for all GUI JavaScript things. - // (We need an object with no parent, so functions defined by scripts get - // put onto this object and not its parent. In the current version of SpiderMonkey - // that means it must be a global object. Then we adjust its prototype so it - // can still read standard properties from the context's standard global object.) - m_ScriptObject = JS_NewGlobalObject(g_ScriptingHost.getContext(), g_ScriptingHost.GetScriptInterface().GetGlobalClass()); + // Construct the root object for all GUI JavaScript things + m_ScriptObject = JS_NewGlobalObject(g_ScriptingHost.getContext(), &page_global_class); JS_AddObjectRoot(g_ScriptingHost.getContext(), &m_ScriptObject); - - JS_SetPrototype(g_ScriptingHost.getContext(), m_ScriptObject, g_ScriptingHost.GetGlobalObject()); } CGUI::~CGUI() diff --git a/source/gui/IGUIObject.cpp b/source/gui/IGUIObject.cpp index cabf002e91..ce48352b75 100644 --- a/source/gui/IGUIObject.cpp +++ b/source/gui/IGUIObject.cpp @@ -26,7 +26,7 @@ IGUIObject #include "gui/scripting/JSInterface_IGUIObject.h" #include "gui/scripting/JSInterface_GUITypes.h" -#include "scriptinterface/ScriptVal.h" +#include "scriptinterface/ScriptInterface.h" #include "ps/CLogger.h" #define LOG_CATEGORY L"gui" @@ -504,27 +504,17 @@ void IGUIObject::ScriptEvent(const CStr& Action) if (it == m_ScriptHandlers.end()) return; - // The IGUIObject needs to be stored inside the script's object - jsval guiObject = PRIVATE_TO_JSVAL(this); - - // Make a 'this', allowing access to the IGUIObject - JSObject* jsGuiObject = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_IGUIObject::JSI_class, m_pGUI->m_ScriptObject, NULL, 1, &guiObject); - debug_assert(jsGuiObject); // TODO: Handle errors - - // TODO: why don't we use GetJSObject here? - // Set up the 'mouse' parameter - jsval mouseParams[3]; - mouseParams[0] = INT_TO_JSVAL(m_pGUI->m_MousePos.x); - mouseParams[1] = INT_TO_JSVAL(m_pGUI->m_MousePos.y); - mouseParams[2] = INT_TO_JSVAL(m_pGUI->m_MouseButtons); - JSObject* mouseObj = JS_ConstructObjectWithArguments(g_ScriptingHost.getContext(), &JSI_GUIMouse::JSI_class, m_pGUI->m_ScriptObject, NULL, 3, mouseParams); - debug_assert(mouseObj); // TODO: Handle errors + CScriptVal mouse; + g_ScriptingHost.GetScriptInterface().Eval("({})", mouse); + g_ScriptingHost.GetScriptInterface().SetProperty(mouse.get(), "x", m_pGUI->m_MousePos.x, false); + g_ScriptingHost.GetScriptInterface().SetProperty(mouse.get(), "y", m_pGUI->m_MousePos.y, false); + g_ScriptingHost.GetScriptInterface().SetProperty(mouse.get(), "buttons", m_pGUI->m_MouseButtons, false); - jsval paramData[] = { OBJECT_TO_JSVAL(mouseObj) }; + jsval paramData[] = { mouse.get() }; jsval result; - JSBool ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), jsGuiObject, OBJECT_TO_JSVAL(*it->second), ARRAY_SIZE(paramData), paramData, &result); + JSBool ok = JS_CallFunctionValue(g_ScriptingHost.getContext(), GetJSObject(), OBJECT_TO_JSVAL(*it->second), ARRAY_SIZE(paramData), paramData, &result); if (!ok) { JS_ReportError(g_ScriptingHost.getContext(), "Errors executing script action \"%s\"", Action.c_str()); diff --git a/source/gui/scripting/JSInterface_IGUIObject.cpp b/source/gui/scripting/JSInterface_IGUIObject.cpp index da0b72ee57..f4590a9f07 100644 --- a/source/gui/scripting/JSInterface_IGUIObject.cpp +++ b/source/gui/scripting/JSInterface_IGUIObject.cpp @@ -99,9 +99,7 @@ JSBool JSI_IGUIObject::getProperty(JSContext* cx, JSObject* obj, jsid id, jsval* if (parent) { // If the object isn't parentless, return a new object - JSObject* entity = JS_NewObject(cx, &JSI_IGUIObject::JSI_class, NULL, NULL); - JS_SetPrivate(cx, entity, parent); - *vp = OBJECT_TO_JSVAL(entity); + *vp = OBJECT_TO_JSVAL(parent->GetJSObject()); } else {