diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index 06930a13c6..ce7c6821c1 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -900,7 +900,8 @@ handle_script_request(const string &operation, P3D_object *object, } P3D_object *result = - P3D_OBJECT_CALL(object, property_name.c_str(), values, num_values); + P3D_OBJECT_CALL(object, property_name.c_str(), needs_response, + values, num_values); if (result != NULL) { xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); diff --git a/direct/src/plugin/p3dObject.cxx b/direct/src/plugin/p3dObject.cxx index d2bc6912a2..a6b2fa645c 100644 --- a/direct/src/plugin/p3dObject.cxx +++ b/direct/src/plugin/p3dObject.cxx @@ -76,11 +76,12 @@ object_has_method(P3D_object *object, const char *method_name) { static P3D_object * object_call(P3D_object *object, const char *method_name, + bool needs_response, P3D_object *params[], int num_params) { if (method_name == NULL) { method_name = ""; } - return ((P3DObject *)object)->call(method_name, params, num_params); + return ((P3DObject *)object)->call(method_name, needs_response, params, num_params); } static P3D_object * @@ -165,7 +166,7 @@ generic_has_method(P3D_object *object, const char *method_name) { static P3D_object * generic_call(P3D_object *object, const char *method_name, - P3D_object *params[], int num_params) { + bool needs_response, P3D_object *params[], int num_params) { return NULL; } @@ -294,11 +295,17 @@ has_method(const string &method_name) { // Access: Public, Virtual // Description: Invokes the named method on the object, passing the // indicated parameters. If the method name is empty, -// invokes the object itself. Returns the return value -// on success, NULL on error. +// invokes the object itself. +// +// If needs_response is true, the return value is a +// new-reference P3D_object on success, or NULL on +// failure. If needs_response is false, the return +// value is always NULL, and there is no way to +// determine success or failure. //////////////////////////////////////////////////////////////////// P3D_object *P3DObject:: -call(const string &method_name, P3D_object *params[], int num_params) { +call(const string &method_name, bool needs_response, + P3D_object *params[], int num_params) { return NULL; } diff --git a/direct/src/plugin/p3dObject.h b/direct/src/plugin/p3dObject.h index 905079f9a7..09a1218346 100644 --- a/direct/src/plugin/p3dObject.h +++ b/direct/src/plugin/p3dObject.h @@ -46,7 +46,7 @@ public: virtual bool set_property(const string &property, P3D_object *value); virtual bool has_method(const string &method_name); - virtual P3D_object *call(const string &method_name, + virtual P3D_object *call(const string &method_name, bool needs_response, P3D_object *params[], int num_params); virtual P3D_object *eval(const string &expression); diff --git a/direct/src/plugin/p3dPythonObject.cxx b/direct/src/plugin/p3dPythonObject.cxx index abe949d138..f2136d596e 100644 --- a/direct/src/plugin/p3dPythonObject.cxx +++ b/direct/src/plugin/p3dPythonObject.cxx @@ -61,7 +61,7 @@ bool P3DPythonObject:: get_bool() { bool bresult = 0; - P3D_object *result = call("__bool__", NULL, 0); + P3D_object *result = call("__bool__", true, NULL, 0); if (result != NULL) { bresult = P3D_OBJECT_GET_BOOL(result); P3D_OBJECT_DECREF(result); @@ -80,7 +80,7 @@ int P3DPythonObject:: get_int() { int iresult = 0; - P3D_object *result = call("__int__", NULL, 0); + P3D_object *result = call("__int__", true, NULL, 0); if (result != NULL) { iresult = P3D_OBJECT_GET_INT(result); P3D_OBJECT_DECREF(result); @@ -99,7 +99,7 @@ double P3DPythonObject:: get_float() { double fresult = 0.0; - P3D_object *result = call("__float__", NULL, 0); + P3D_object *result = call("__float__", true, NULL, 0); if (result != NULL) { fresult = P3D_OBJECT_GET_FLOAT(result); P3D_OBJECT_DECREF(result); @@ -116,7 +116,7 @@ get_float() { //////////////////////////////////////////////////////////////////// void P3DPythonObject:: make_string(string &value) { - P3D_object *result = call("__str__", NULL, 0); + P3D_object *result = call("__str__", true, NULL, 0); if (result != NULL) { int size = P3D_OBJECT_GET_STRING(result, NULL, 0); char *buffer = new char[size]; @@ -140,7 +140,7 @@ get_property(const string &property) { P3D_object *params[1]; params[0] = new P3DStringObject(property); - P3D_object *result = call("__get_property__", params, 1); + P3D_object *result = call("__get_property__", true, params, 1); P3D_OBJECT_DECREF(params[0]); return result; } @@ -163,12 +163,12 @@ set_property(const string &property, P3D_object *value) { if (value == NULL) { // Delete an attribute. - result = call("__del_property__", params, 1); + result = call("__del_property__", true, params, 1); } else { // Set a new attribute. params[1] = value; - result = call("__set_property__", params, 2); + result = call("__set_property__", true, params, 2); } P3D_OBJECT_DECREF(params[0]); @@ -202,7 +202,7 @@ has_method(const string &method_name) { P3D_object *params[1]; params[0] = new P3DStringObject(method_name); - P3D_object *result = call("__has_method__", params, 1); + P3D_object *result = call("__has_method__", true, params, 1); P3D_OBJECT_DECREF(params[0]); if (result != NULL) { @@ -223,11 +223,17 @@ has_method(const string &method_name) { // Access: Public, Virtual // Description: Invokes the named method on the object, passing the // indicated parameters. If the method name is empty, -// invokes the object itself. Returns the return value -// on success, NULL on error. +// invokes the object itself. +// +// If needs_response is true, the return value is a +// new-reference P3D_object on success, or NULL on +// failure. If needs_response is false, the return +// value is always NULL, and there is no way to +// determine success or failure. //////////////////////////////////////////////////////////////////// P3D_object *P3DPythonObject:: -call(const string &method_name, P3D_object *params[], int num_params) { +call(const string &method_name, bool needs_response, + P3D_object *params[], int num_params) { TiXmlDocument *doc = new TiXmlDocument; TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", ""); TiXmlElement *xcommand = new TiXmlElement("command"); @@ -248,6 +254,16 @@ call(const string &method_name, P3D_object *params[], int num_params) { doc->LinkEndChild(decl); doc->LinkEndChild(xcommand); + + // If no response is requested, send the command out in a vacuum, + // and return NULL. + if (!needs_response) { + _session->send_command(doc); + return NULL; + } + + // If a response is requested, we have to send the command and wait + // for it. TiXmlDocument *response = _session->command_and_response(doc); P3D_object *result = NULL; @@ -274,7 +290,7 @@ call(const string &method_name, P3D_object *params[], int num_params) { //////////////////////////////////////////////////////////////////// void P3DPythonObject:: output(ostream &out) { - P3D_object *result = call("__repr__", NULL, 0); + P3D_object *result = call("__repr__", true, NULL, 0); out << "Python " << _object_id; if (result != NULL) { out << ": " << *result; diff --git a/direct/src/plugin/p3dPythonObject.h b/direct/src/plugin/p3dPythonObject.h index 2b4cbe91d7..a05d4ec08c 100644 --- a/direct/src/plugin/p3dPythonObject.h +++ b/direct/src/plugin/p3dPythonObject.h @@ -45,7 +45,7 @@ public: virtual bool set_property(const string &property, P3D_object *value); virtual bool has_method(const string &method_name); - virtual P3D_object *call(const string &method_name, + virtual P3D_object *call(const string &method_name, bool needs_response, P3D_object *params[], int num_params); virtual void output(ostream &out); diff --git a/direct/src/plugin/p3dToplevelObject.cxx b/direct/src/plugin/p3dToplevelObject.cxx index b064078875..4368a4b6eb 100644 --- a/direct/src/plugin/p3dToplevelObject.cxx +++ b/direct/src/plugin/p3dToplevelObject.cxx @@ -189,17 +189,24 @@ has_method(const string &method_name) { // Access: Public, Virtual // Description: Invokes the named method on the object, passing the // indicated parameters. If the method name is empty, -// invokes the object itself. Returns the return value -// on success, NULL on error. +// invokes the object itself. +// +// If needs_response is true, the return value is a +// new-reference P3D_object on success, or NULL on +// failure. If needs_response is false, the return +// value is always NULL, and there is no way to +// determine success or failure. //////////////////////////////////////////////////////////////////// P3D_object *P3DToplevelObject:: -call(const string &method_name, P3D_object *params[], int num_params) { +call(const string &method_name, bool needs_response, + P3D_object *params[], int num_params) { if (_pyobj == NULL) { // No methods until we get our pyobj. return NULL; } - return P3D_OBJECT_CALL(_pyobj, method_name.c_str(), params, num_params); + return P3D_OBJECT_CALL(_pyobj, method_name.c_str(), needs_response, + params, num_params); } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/plugin/p3dToplevelObject.h b/direct/src/plugin/p3dToplevelObject.h index d188a1dcc6..ee77f16de9 100644 --- a/direct/src/plugin/p3dToplevelObject.h +++ b/direct/src/plugin/p3dToplevelObject.h @@ -52,7 +52,7 @@ public: virtual bool set_property(const string &property, P3D_object *value); virtual bool has_method(const string &method_name); - virtual P3D_object *call(const string &method_name, + virtual P3D_object *call(const string &method_name, bool needs_response, P3D_object *params[], int num_params); virtual void output(ostream &out); diff --git a/direct/src/plugin/p3d_plugin.cxx b/direct/src/plugin/p3d_plugin.cxx index 9e93ef3f7e..da616d62f5 100644 --- a/direct/src/plugin/p3d_plugin.cxx +++ b/direct/src/plugin/p3d_plugin.cxx @@ -205,10 +205,12 @@ P3D_object_has_method(P3D_object *object, const char *method_name) { P3D_object * P3D_object_call(P3D_object *object, const char *method_name, + bool needs_response, P3D_object *params[], int num_params) { assert(P3DInstanceManager::get_global_ptr()->is_initialized()); ACQUIRE_LOCK(_api_lock); - P3D_object *result = P3D_OBJECT_CALL(object, method_name, params, num_params); + P3D_object *result = P3D_OBJECT_CALL(object, method_name, needs_response, + params, num_params); RELEASE_LOCK(_api_lock); return result; } diff --git a/direct/src/plugin/p3d_plugin.h b/direct/src/plugin/p3d_plugin.h index cac161cca0..2697c1b979 100644 --- a/direct/src/plugin/p3d_plugin.h +++ b/direct/src/plugin/p3d_plugin.h @@ -406,10 +406,13 @@ P3D_object_has_method_method(P3D_object *object, const char *method_name); these objects' reference counts is not transferred with the call (you must still DECREF these objects afterwards). - The return value is a new-reference P3D_object on success, or NULL - on failure. */ + If needs_response is true, the return value is a new-reference + P3D_object on success, or NULL on failure. If needs_response is + false, the return value is always NULL, and there is no way to + determine success or failure. */ typedef P3D_object * P3D_object_call_method(P3D_object *object, const char *method_name, + bool needs_response, P3D_object *params[], int num_params); /* Evaluates an arbitrary JavaScript expression in the context of the @@ -468,7 +471,7 @@ struct _P3D_object { #define P3D_OBJECT_SET_PROPERTY(object, property, value) ((object)->_class->_set_property((object), (property), (value))) #define P3D_OBJECT_HAS_METHOD(object, method_name) ((object)->_class->_has_method((object), (method_name))) -#define P3D_OBJECT_CALL(object, method_name, params, num_params) ((object)->_class->_call((object), (method_name), (params), (num_params))) +#define P3D_OBJECT_CALL(object, method_name, needs_response, params, num_params) ((object)->_class->_call((object), (method_name), (needs_response), (params), (num_params))) #define P3D_OBJECT_EVAL(object, expression) ((object)->_class->_eval((object), (expression))) /* These macros are provided to manipulate the reference count of the @@ -506,6 +509,7 @@ typedef bool P3D_object_has_method_func(P3D_object *object, const char *method_name); typedef P3D_object * P3D_object_call_func(P3D_object *object, const char *method_name, + bool needs_response, P3D_object *params[], int num_params); typedef P3D_object * P3D_object_eval_func(P3D_object *object, const char *expression); diff --git a/direct/src/plugin_npapi/ppBrowserObject.cxx b/direct/src/plugin_npapi/ppBrowserObject.cxx index 7f99a34004..3beee31098 100644 --- a/direct/src/plugin_npapi/ppBrowserObject.cxx +++ b/direct/src/plugin_npapi/ppBrowserObject.cxx @@ -43,12 +43,20 @@ object_set_property(P3D_object *object, const char *property, } static P3D_object * -object_call(P3D_object *object, const char *method_name, +object_call(P3D_object *object, const char *method_name, + bool needs_response, P3D_object *params[], int num_params) { if (method_name == NULL) { method_name = ""; } - return ((const PPBrowserObject *)object)->call(method_name, params, num_params); + P3D_object *response = ((const PPBrowserObject *)object)->call(method_name, params, num_params); + if (!needs_response) { + // No response was expected. Throw away the response we received, + // so we can be consistent with defined semantics. + P3D_OBJECT_XDECREF(response); + response = NULL; + } + return response; } static P3D_object * diff --git a/direct/src/plugin_npapi/ppPandaObject.cxx b/direct/src/plugin_npapi/ppPandaObject.cxx index 3d079085d4..4bab577d75 100644 --- a/direct/src/plugin_npapi/ppPandaObject.cxx +++ b/direct/src/plugin_npapi/ppPandaObject.cxx @@ -143,7 +143,7 @@ invoke(NPIdentifier name, const NPVariant *args, uint32_t argCount, } P3D_object *value = P3D_OBJECT_CALL(_p3d_object, method_name.c_str(), - p3dargs, argCount); + true, p3dargs, argCount); for (i = 0; i < argCount; ++i) { P3D_OBJECT_DECREF(p3dargs[i]); } @@ -181,7 +181,7 @@ invoke_default(const NPVariant *args, uint32_t argCount, p3dargs[i] = _instance->variant_to_p3dobj(&args[i]); } - P3D_object *value = P3D_OBJECT_CALL(_p3d_object, "", + P3D_object *value = P3D_OBJECT_CALL(_p3d_object, "", true, p3dargs, argCount); for (i = 0; i < argCount; ++i) { P3D_OBJECT_DECREF(p3dargs[i]); diff --git a/direct/src/showutil/JavaScript.py b/direct/src/showutil/JavaScript.py index 50b14d26b6..84b2f50790 100644 --- a/direct/src/showutil/JavaScript.py +++ b/direct/src/showutil/JavaScript.py @@ -6,7 +6,7 @@ code that runs in a browser via the web plugin. """ class UndefinedObject: """ This is a special object that is returned by the browser to represent an "undefined" value, typically the value for an - uninitialize variable or undefined property. It has no + uninitialized variable or undefined property. It has no attributes, similar to None, but it is a slightly different concept in JavaScript. """ @@ -48,7 +48,11 @@ class BrowserObject: # This element is filled in by __getattr__; it connects # the object to its parent. - self.__dict__['_BrowserObject__boundMethod'] = (None, None) + self.__dict__['_BrowserObject__childObject'] = (None, None) + + # This is a cache of method names to MethodWrapper objects in + # the parent object. + self.__dict__['_BrowserObject__methods'] = {} def __del__(self): # When the BrowserObject destructs, tell the parent process it @@ -56,8 +60,23 @@ class BrowserObject: # more. self.__runner.dropObject(self.__objectId) + def __cacheMethod(self, methodName): + """ Stores a pointer to the named method on this object, so + that the next time __getattr__ is called, it can retrieve the + method wrapper without having to query the browser. This + cache assumes that callable methods don't generally come and + go on and object. + + The return value is the MethodWrapper object. """ + + method = self.__methods.get(methodName, None) + if method is None: + method = MethodWrapper(self.__runner, self, methodName) + self.__methods[methodName] = method + return method + def __str__(self): - parentObj, attribName = self.__boundMethod + parentObj, attribName = self.__childObject if parentObj: # Format it from its parent. return "%s.%s" % (parentObj, attribName) @@ -77,7 +96,7 @@ class BrowserObject: raise ArgumentError, 'Keyword arguments not supported' try: - parentObj, attribName = self.__boundMethod + parentObj, attribName = self.__childObject if parentObj: # Call it as a method. if parentObj is self.__runner.dom and attribName == 'alert': @@ -102,6 +121,12 @@ class BrowserObject: except EnvironmentError: # Problem on the call. Maybe no such method? raise AttributeError + + # Hey, the method call appears to have succeeded. + # Cache the method object on the parent so we won't + # have to look up the method wrapper again next time. + parentObj.__cacheMethod(attribName) + else: # Call it as a plain function. result = self.__runner.scriptRequest('call', self, value = args, needsResponse = needsResponse) @@ -116,6 +141,13 @@ class BrowserObject: into the appropriate calls to query the actual browser object under the hood. """ + # First check to see if there's a cached method wrapper from a + # previous call. + method = self.__methods.get(name, None) + if method: + return method + + # No cache. Go query the browser for the desired value. try: value = self.__runner.scriptRequest('get_property', self, propertyName = name) @@ -124,7 +156,7 @@ class BrowserObject: # method instead? if self.__runner.scriptRequest('has_method', self, propertyName = name): # Yes, so create a method wrapper for it. - return MethodWrapper(self.__runner, self, name) + return self.__cacheMethod(name) raise AttributeError(name) @@ -133,7 +165,7 @@ class BrowserObject: # properly call a method. (Javascript needs to know the # method container at the time of the call, and doesn't # store it on the function object.) - value.__dict__['_BrowserObject__boundMethod'] = (self, name) + value.__dict__['_BrowserObject__childObject'] = (self, name) return value @@ -205,10 +237,10 @@ class MethodWrapper: def __init__(self, runner, parentObj, objectId): self.__dict__['_MethodWrapper__runner'] = runner - self.__dict__['_MethodWraper__boundMethod'] = (parentObj, objectId) + self.__dict__['_MethodWrapper__childObject'] = (parentObj, objectId) def __str__(self): - parentObj, attribName = self.__boundMethod + parentObj, attribName = self.__childObject return "%s.%s" % (parentObj, attribName) def __nonzero__(self): @@ -223,7 +255,7 @@ class MethodWrapper: raise ArgumentError, 'Keyword arguments not supported' try: - parentObj, attribName = self.__boundMethod + parentObj, attribName = self.__childObject # Call it as a method. if parentObj is self.__runner.dom and attribName == 'alert': # As a special hack, we don't wait for the return