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_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));

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
////////////////////////////////////////////////////////////////////

View File

@ -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);

View File

@ -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;
}

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
(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);

View File

@ -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 *

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(),
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]);

View File

@ -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