more needs_response changes

This commit is contained in:
David Rose 2009-07-19 18:15:52 +00:00
parent e545c9193e
commit 07e714bb16
12 changed files with 119 additions and 42 deletions

View File

@ -900,7 +900,8 @@ handle_script_request(const string &operation, P3D_object *object,
} }
P3D_object *result = 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) { if (result != NULL) {
xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); xcommand->LinkEndChild(_session->p3dobj_to_xml(result));

View File

@ -76,11 +76,12 @@ object_has_method(P3D_object *object, const char *method_name) {
static P3D_object * 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) { P3D_object *params[], int num_params) {
if (method_name == NULL) { if (method_name == NULL) {
method_name = ""; 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 * static P3D_object *
@ -165,7 +166,7 @@ generic_has_method(P3D_object *object, const char *method_name) {
static P3D_object * static P3D_object *
generic_call(P3D_object *object, const char *method_name, 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; return NULL;
} }
@ -294,11 +295,17 @@ has_method(const string &method_name) {
// Access: Public, Virtual // Access: Public, Virtual
// Description: Invokes the named method on the object, passing the // Description: Invokes the named method on the object, passing the
// indicated parameters. If the method name is empty, // indicated parameters. If the method name is empty,
// invokes the object itself. Returns the return value // invokes the object itself.
// on success, NULL on error. //
// 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:: 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; return NULL;
} }

View File

@ -46,7 +46,7 @@ public:
virtual bool set_property(const string &property, P3D_object *value); virtual bool set_property(const string &property, P3D_object *value);
virtual bool has_method(const string &method_name); 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); P3D_object *params[], int num_params);
virtual P3D_object *eval(const string &expression); virtual P3D_object *eval(const string &expression);

View File

@ -61,7 +61,7 @@ bool P3DPythonObject::
get_bool() { get_bool() {
bool bresult = 0; bool bresult = 0;
P3D_object *result = call("__bool__", NULL, 0); P3D_object *result = call("__bool__", true, NULL, 0);
if (result != NULL) { if (result != NULL) {
bresult = P3D_OBJECT_GET_BOOL(result); bresult = P3D_OBJECT_GET_BOOL(result);
P3D_OBJECT_DECREF(result); P3D_OBJECT_DECREF(result);
@ -80,7 +80,7 @@ int P3DPythonObject::
get_int() { get_int() {
int iresult = 0; int iresult = 0;
P3D_object *result = call("__int__", NULL, 0); P3D_object *result = call("__int__", true, NULL, 0);
if (result != NULL) { if (result != NULL) {
iresult = P3D_OBJECT_GET_INT(result); iresult = P3D_OBJECT_GET_INT(result);
P3D_OBJECT_DECREF(result); P3D_OBJECT_DECREF(result);
@ -99,7 +99,7 @@ double P3DPythonObject::
get_float() { get_float() {
double fresult = 0.0; double fresult = 0.0;
P3D_object *result = call("__float__", NULL, 0); P3D_object *result = call("__float__", true, NULL, 0);
if (result != NULL) { if (result != NULL) {
fresult = P3D_OBJECT_GET_FLOAT(result); fresult = P3D_OBJECT_GET_FLOAT(result);
P3D_OBJECT_DECREF(result); P3D_OBJECT_DECREF(result);
@ -116,7 +116,7 @@ get_float() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DPythonObject:: void P3DPythonObject::
make_string(string &value) { make_string(string &value) {
P3D_object *result = call("__str__", NULL, 0); P3D_object *result = call("__str__", true, NULL, 0);
if (result != NULL) { if (result != NULL) {
int size = P3D_OBJECT_GET_STRING(result, NULL, 0); int size = P3D_OBJECT_GET_STRING(result, NULL, 0);
char *buffer = new char[size]; char *buffer = new char[size];
@ -140,7 +140,7 @@ get_property(const string &property) {
P3D_object *params[1]; P3D_object *params[1];
params[0] = new P3DStringObject(property); 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]); P3D_OBJECT_DECREF(params[0]);
return result; return result;
} }
@ -163,12 +163,12 @@ set_property(const string &property, P3D_object *value) {
if (value == NULL) { if (value == NULL) {
// Delete an attribute. // Delete an attribute.
result = call("__del_property__", params, 1); result = call("__del_property__", true, params, 1);
} else { } else {
// Set a new attribute. // Set a new attribute.
params[1] = value; params[1] = value;
result = call("__set_property__", params, 2); result = call("__set_property__", true, params, 2);
} }
P3D_OBJECT_DECREF(params[0]); P3D_OBJECT_DECREF(params[0]);
@ -202,7 +202,7 @@ has_method(const string &method_name) {
P3D_object *params[1]; P3D_object *params[1];
params[0] = new P3DStringObject(method_name); 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]); P3D_OBJECT_DECREF(params[0]);
if (result != NULL) { if (result != NULL) {
@ -223,11 +223,17 @@ has_method(const string &method_name) {
// Access: Public, Virtual // Access: Public, Virtual
// Description: Invokes the named method on the object, passing the // Description: Invokes the named method on the object, passing the
// indicated parameters. If the method name is empty, // indicated parameters. If the method name is empty,
// invokes the object itself. Returns the return value // invokes the object itself.
// on success, NULL on error. //
// 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:: 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; TiXmlDocument *doc = new TiXmlDocument;
TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", ""); TiXmlDeclaration *decl = new TiXmlDeclaration("1.0", "utf-8", "");
TiXmlElement *xcommand = new TiXmlElement("command"); 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(decl);
doc->LinkEndChild(xcommand); 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); TiXmlDocument *response = _session->command_and_response(doc);
P3D_object *result = NULL; P3D_object *result = NULL;
@ -274,7 +290,7 @@ call(const string &method_name, P3D_object *params[], int num_params) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void P3DPythonObject:: void P3DPythonObject::
output(ostream &out) { output(ostream &out) {
P3D_object *result = call("__repr__", NULL, 0); P3D_object *result = call("__repr__", true, NULL, 0);
out << "Python " << _object_id; out << "Python " << _object_id;
if (result != NULL) { if (result != NULL) {
out << ": " << *result; out << ": " << *result;

View File

@ -45,7 +45,7 @@ public:
virtual bool set_property(const string &property, P3D_object *value); virtual bool set_property(const string &property, P3D_object *value);
virtual bool has_method(const string &method_name); 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); P3D_object *params[], int num_params);
virtual void output(ostream &out); virtual void output(ostream &out);

View File

@ -189,17 +189,24 @@ has_method(const string &method_name) {
// Access: Public, Virtual // Access: Public, Virtual
// Description: Invokes the named method on the object, passing the // Description: Invokes the named method on the object, passing the
// indicated parameters. If the method name is empty, // indicated parameters. If the method name is empty,
// invokes the object itself. Returns the return value // invokes the object itself.
// on success, NULL on error. //
// 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:: 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) { if (_pyobj == NULL) {
// No methods until we get our pyobj. // No methods until we get our pyobj.
return NULL; 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);
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////

View File

@ -52,7 +52,7 @@ public:
virtual bool set_property(const string &property, P3D_object *value); virtual bool set_property(const string &property, P3D_object *value);
virtual bool has_method(const string &method_name); 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); P3D_object *params[], int num_params);
virtual void output(ostream &out); virtual void output(ostream &out);

View File

@ -205,10 +205,12 @@ P3D_object_has_method(P3D_object *object, const char *method_name) {
P3D_object * P3D_object *
P3D_object_call(P3D_object *object, const char *method_name, P3D_object_call(P3D_object *object, const char *method_name,
bool needs_response,
P3D_object *params[], int num_params) { P3D_object *params[], int num_params) {
assert(P3DInstanceManager::get_global_ptr()->is_initialized()); assert(P3DInstanceManager::get_global_ptr()->is_initialized());
ACQUIRE_LOCK(_api_lock); 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); RELEASE_LOCK(_api_lock);
return result; return result;
} }

View File

@ -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 these objects' reference counts is not transferred with the call
(you must still DECREF these objects afterwards). (you must still DECREF these objects afterwards).
The return value is a new-reference P3D_object on success, or NULL If needs_response is true, the return value is a new-reference
on failure. */ 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 * typedef P3D_object *
P3D_object_call_method(P3D_object *object, const char *method_name, P3D_object_call_method(P3D_object *object, const char *method_name,
bool needs_response,
P3D_object *params[], int num_params); P3D_object *params[], int num_params);
/* Evaluates an arbitrary JavaScript expression in the context of the /* 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_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_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))) #define P3D_OBJECT_EVAL(object, expression) ((object)->_class->_eval((object), (expression)))
/* These macros are provided to manipulate the reference count of the /* 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); P3D_object_has_method_func(P3D_object *object, const char *method_name);
typedef P3D_object * typedef P3D_object *
P3D_object_call_func(P3D_object *object, const char *method_name, P3D_object_call_func(P3D_object *object, const char *method_name,
bool needs_response,
P3D_object *params[], int num_params); P3D_object *params[], int num_params);
typedef P3D_object * typedef P3D_object *
P3D_object_eval_func(P3D_object *object, const char *expression); P3D_object_eval_func(P3D_object *object, const char *expression);

View File

@ -44,11 +44,19 @@ object_set_property(P3D_object *object, const char *property,
static P3D_object * 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) { P3D_object *params[], int num_params) {
if (method_name == NULL) { if (method_name == NULL) {
method_name = ""; 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 * static P3D_object *

View File

@ -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(), P3D_object *value = P3D_OBJECT_CALL(_p3d_object, method_name.c_str(),
p3dargs, argCount); true, p3dargs, argCount);
for (i = 0; i < argCount; ++i) { for (i = 0; i < argCount; ++i) {
P3D_OBJECT_DECREF(p3dargs[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]); 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); p3dargs, argCount);
for (i = 0; i < argCount; ++i) { for (i = 0; i < argCount; ++i) {
P3D_OBJECT_DECREF(p3dargs[i]); P3D_OBJECT_DECREF(p3dargs[i]);

View File

@ -6,7 +6,7 @@ code that runs in a browser via the web plugin. """
class UndefinedObject: class UndefinedObject:
""" This is a special object that is returned by the browser to """ This is a special object that is returned by the browser to
represent an "undefined" value, typically the value for an 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 attributes, similar to None, but it is a slightly different
concept in JavaScript. """ concept in JavaScript. """
@ -48,7 +48,11 @@ class BrowserObject:
# This element is filled in by __getattr__; it connects # This element is filled in by __getattr__; it connects
# the object to its parent. # 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): def __del__(self):
# When the BrowserObject destructs, tell the parent process it # When the BrowserObject destructs, tell the parent process it
@ -56,8 +60,23 @@ class BrowserObject:
# more. # more.
self.__runner.dropObject(self.__objectId) 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): def __str__(self):
parentObj, attribName = self.__boundMethod parentObj, attribName = self.__childObject
if parentObj: if parentObj:
# Format it from its parent. # Format it from its parent.
return "%s.%s" % (parentObj, attribName) return "%s.%s" % (parentObj, attribName)
@ -77,7 +96,7 @@ class BrowserObject:
raise ArgumentError, 'Keyword arguments not supported' raise ArgumentError, 'Keyword arguments not supported'
try: try:
parentObj, attribName = self.__boundMethod parentObj, attribName = self.__childObject
if parentObj: if parentObj:
# Call it as a method. # Call it as a method.
if parentObj is self.__runner.dom and attribName == 'alert': if parentObj is self.__runner.dom and attribName == 'alert':
@ -102,6 +121,12 @@ class BrowserObject:
except EnvironmentError: except EnvironmentError:
# Problem on the call. Maybe no such method? # Problem on the call. Maybe no such method?
raise AttributeError 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: else:
# Call it as a plain function. # Call it as a plain function.
result = self.__runner.scriptRequest('call', self, value = args, needsResponse = needsResponse) 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 into the appropriate calls to query the actual browser object
under the hood. """ 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: try:
value = self.__runner.scriptRequest('get_property', self, value = self.__runner.scriptRequest('get_property', self,
propertyName = name) propertyName = name)
@ -124,7 +156,7 @@ class BrowserObject:
# method instead? # method instead?
if self.__runner.scriptRequest('has_method', self, propertyName = name): if self.__runner.scriptRequest('has_method', self, propertyName = name):
# Yes, so create a method wrapper for it. # Yes, so create a method wrapper for it.
return MethodWrapper(self.__runner, self, name) return self.__cacheMethod(name)
raise AttributeError(name) raise AttributeError(name)
@ -133,7 +165,7 @@ class BrowserObject:
# properly call a method. (Javascript needs to know the # properly call a method. (Javascript needs to know the
# method container at the time of the call, and doesn't # method container at the time of the call, and doesn't
# store it on the function object.) # store it on the function object.)
value.__dict__['_BrowserObject__boundMethod'] = (self, name) value.__dict__['_BrowserObject__childObject'] = (self, name)
return value return value
@ -205,10 +237,10 @@ class MethodWrapper:
def __init__(self, runner, parentObj, objectId): def __init__(self, runner, parentObj, objectId):
self.__dict__['_MethodWrapper__runner'] = runner self.__dict__['_MethodWrapper__runner'] = runner
self.__dict__['_MethodWraper__boundMethod'] = (parentObj, objectId) self.__dict__['_MethodWrapper__childObject'] = (parentObj, objectId)
def __str__(self): def __str__(self):
parentObj, attribName = self.__boundMethod parentObj, attribName = self.__childObject
return "%s.%s" % (parentObj, attribName) return "%s.%s" % (parentObj, attribName)
def __nonzero__(self): def __nonzero__(self):
@ -223,7 +255,7 @@ class MethodWrapper:
raise ArgumentError, 'Keyword arguments not supported' raise ArgumentError, 'Keyword arguments not supported'
try: try:
parentObj, attribName = self.__boundMethod parentObj, attribName = self.__childObject
# Call it as a method. # Call it as a method.
if parentObj is self.__runner.dom and attribName == 'alert': if parentObj is self.__runner.dom and attribName == 'alert':
# As a special hack, we don't wait for the return # As a special hack, we don't wait for the return