From 1b6bb92da5a6a008c45a46de918ae7e5b3d6e8da Mon Sep 17 00:00:00 2001 From: David Rose Date: Tue, 14 Jul 2009 20:40:47 +0000 Subject: [PATCH] concrete objects --- direct/src/plugin/Sources.pp | 4 + direct/src/plugin/p3dConcreteSequence.cxx | 253 ++++++++++++++++++++ direct/src/plugin/p3dConcreteSequence.h | 60 +++++ direct/src/plugin/p3dConcreteStruct.cxx | 158 ++++++++++++ direct/src/plugin/p3dConcreteStruct.h | 51 ++++ direct/src/plugin/p3dConditionVar.cxx | 5 +- direct/src/plugin/p3dInstance.cxx | 63 +++-- direct/src/plugin/p3dInstance.h | 3 +- direct/src/plugin/p3dInstanceManager.cxx | 2 + direct/src/plugin/p3dObject.cxx | 52 +++- direct/src/plugin/p3dObject.h | 5 +- direct/src/plugin/p3dPythonObject.cxx | 42 ++-- direct/src/plugin/p3dPythonObject.h | 3 +- direct/src/plugin/p3dPythonRun.cxx | 228 +++++++++++++++--- direct/src/plugin/p3dPythonRun.h | 1 + direct/src/plugin/p3dSession.cxx | 57 ++++- direct/src/plugin/p3d_plugin_composite1.cxx | 2 + direct/src/plugin_npapi/ppPandaObject.cxx | 10 +- direct/src/showutil/runp3d.py | 235 +----------------- 19 files changed, 886 insertions(+), 348 deletions(-) create mode 100644 direct/src/plugin/p3dConcreteSequence.cxx create mode 100644 direct/src/plugin/p3dConcreteSequence.h create mode 100644 direct/src/plugin/p3dConcreteStruct.cxx create mode 100644 direct/src/plugin/p3dConcreteStruct.h diff --git a/direct/src/plugin/Sources.pp b/direct/src/plugin/Sources.pp index c3486c5f52..3b094f6c50 100644 --- a/direct/src/plugin/Sources.pp +++ b/direct/src/plugin/Sources.pp @@ -30,6 +30,8 @@ p3d_plugin_config.h \ p3d_plugin_common.h \ p3dBoolObject.h \ + p3dConcreteSequence.h \ + p3dConcreteStruct.h \ p3dConditionVar.h p3dConditionVar.I \ p3dDownload.h p3dDownload.I \ p3dFileDownload.h p3dFileDownload.I \ @@ -56,6 +58,8 @@ #define INCLUDED_SOURCES \ p3d_plugin.cxx \ p3dBoolObject.cxx \ + p3dConcreteSequence.cxx \ + p3dConcreteStruct.cxx \ p3dConditionVar.cxx \ p3dDownload.cxx \ p3dFileDownload.cxx \ diff --git a/direct/src/plugin/p3dConcreteSequence.cxx b/direct/src/plugin/p3dConcreteSequence.cxx new file mode 100644 index 0000000000..964bbfeb78 --- /dev/null +++ b/direct/src/plugin/p3dConcreteSequence.cxx @@ -0,0 +1,253 @@ +// Filename: p3dConcreteSequence.cxx +// Created by: drose (30Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "p3dConcreteSequence.h" +#include "p3dSession.h" + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DConcreteSequence:: +P3DConcreteSequence() { +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DConcreteSequence:: +~P3DConcreteSequence() { + Elements::iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + P3D_OBJECT_DECREF(*ei); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::is_sequence_object +// Access: Public, Virtual +// Description: Returns true if this is actually an instance of a +// P3DConcreteSequence, false otherwise. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteSequence:: +is_sequence_object() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_type +// Access: Public, Virtual +// Description: Returns the fundamental type of this kind of object. +//////////////////////////////////////////////////////////////////// +P3D_object_type P3DConcreteSequence:: +get_type() { + return P3D_OT_object; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_bool +// Access: Public, Virtual +// Description: Returns the object value coerced to a boolean, if +// possible. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteSequence:: +get_bool() { + return !_elements.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::make_string +// Access: Public, Virtual +// Description: Fills the indicated C++ string object with the value +// of this object coerced to a string. +//////////////////////////////////////////////////////////////////// +void P3DConcreteSequence:: +make_string(string &value) { + ostringstream strm; + strm << "["; + if (!_elements.empty()) { + strm << *_elements[0]; + for (size_t i = 1; i < _elements.size(); ++i) { + strm << ", " << *_elements[i]; + } + } + strm << "]"; + + value = strm.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_property +// Access: Public, Virtual +// Description: Returns the named property element in the object. The +// return value is a new-reference P3D_object, or NULL +// on error. +//////////////////////////////////////////////////////////////////// +P3D_object *P3DConcreteSequence:: +get_property(const string &property) { + // We only understand integer "property" names. + char *endptr; + int index = strtoul(property.c_str(), &endptr, 10); + if (*endptr != '\0') { + return NULL; + } + + return get_element(index); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::set_property +// Access: Public, Virtual +// Description: Modifies (or deletes, if value is NULL) the named +// property element in the object. Returns true on +// success, false on failure. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteSequence:: +set_property(const string &property, P3D_object *value) { + // We only understand integer "property" names. + char *endptr; + int index = strtoul(property.c_str(), &endptr, 10); + if (*endptr != '\0') { + return NULL; + } + + return set_element(index, value); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::fill_xml +// Access: Public, Virtual +// Description: If this object has a valid XML representation for the +// indicated session (that hasn't already been +// implemented by the generic code in P3DSession), this +// method will apply it to the indicated "value" element +// and return true. Otherwise, this method will leave +// the element unchanged and return false. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteSequence:: +fill_xml(TiXmlElement *xvalue, P3DSession *session) { + xvalue->SetAttribute("type", "concrete_sequence"); + Elements::const_iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + xvalue->LinkEndChild(session->p3dobj_to_xml(*ei)); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_object_array +// Access: Public +// Description: Returns a pointer to the array of objects represented +// by this object. Most objects represent only +// themselves, but a P3DConcreteSequence represents its +// list. +//////////////////////////////////////////////////////////////////// +P3D_object **P3DConcreteSequence:: +get_object_array() { + if (_elements.empty()) { + return NULL; + } + return &_elements[0]; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_object_array_size +// Access: Public +// Description: Returns the number of elements in the array returned +// by get_object_array(). +//////////////////////////////////////////////////////////////////// +int P3DConcreteSequence:: +get_object_array_size() { + return _elements.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_length +// Access: Public +// Description: Returns the number of items in the sequence. +//////////////////////////////////////////////////////////////////// +int P3DConcreteSequence:: +get_length() const { + return _elements.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::get_element +// Access: Public +// Description: Returns the nth item in the sequence. The +// return value is a new-reference P3DObject object, or +// NULL on error. +//////////////////////////////////////////////////////////////////// +P3D_object *P3DConcreteSequence:: +get_element(int n) const { + if (n >= 0 && n < (int)_elements.size()) { + P3D_OBJECT_INCREF(_elements[n]); + return _elements[n]; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::set_element +// Access: Public +// Description: Modifies (or deletes, if value is NULL) the nth item +// in the sequence. Returns true on success, false on +// failure. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteSequence:: +set_element(int n, P3D_object *value) { + if (value == NULL) { + // Delete an element. + if (n < 0 || n >= (int)_elements.size()) { + // Invalid index. + return false; + } + P3D_OBJECT_DECREF(_elements[n]); + _elements.erase(_elements.begin() + n); + return true; + + } else if (n == _elements.size()) { + // Append an element. + append(value); + return true; + + } else { + // Replace an element. + if (n < 0 || n >= (int)_elements.size()) { + // Invalid index. + return false; + } + + P3D_OBJECT_INCREF(value); + P3D_OBJECT_DECREF(_elements[n]); + _elements[n] = value; + return true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteSequence::append +// Access: Public, Virtual +// Description: Adds a new element to the end of the list. +//////////////////////////////////////////////////////////////////// +void P3DConcreteSequence:: +append(P3D_object *value) { + _elements.push_back(value); + P3D_OBJECT_INCREF(value); +} diff --git a/direct/src/plugin/p3dConcreteSequence.h b/direct/src/plugin/p3dConcreteSequence.h new file mode 100644 index 0000000000..aab08b57c0 --- /dev/null +++ b/direct/src/plugin/p3dConcreteSequence.h @@ -0,0 +1,60 @@ +// Filename: p3dConcreteSequence.h +// Created by: drose (30Jun09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef P3DCONCRETESEQUENCE_H +#define P3DCONCRETESEQUENCE_H + +#include "p3d_plugin_common.h" +#include "p3dObject.h" + +//////////////////////////////////////////////////////////////////// +// Class : P3DConcreteSequence +// Description : An object type that contains a sequence of objects, +// which is passed by value between Python and +// JavaScript, so may be more optimal for small lists +// that are accessed repeatedly. +// +// This is converted from a Python "tuple" object. +//////////////////////////////////////////////////////////////////// +class P3DConcreteSequence : public P3DObject { +public: + P3DConcreteSequence(); + virtual ~P3DConcreteSequence(); + + virtual bool is_sequence_object(); + + virtual P3D_object_type get_type(); + virtual bool get_bool(); + + virtual void make_string(string &value); + + virtual P3D_object *get_property(const string &property); + virtual bool set_property(const string &property, P3D_object *value); + + virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); + virtual P3D_object **get_object_array(); + virtual int get_object_array_size(); + + int get_length() const; + P3D_object *get_element(int n) const; + bool set_element(int n, P3D_object *value); + void append(P3D_object *value); + +private: + typedef vector Elements; + Elements _elements; +}; + +#endif + diff --git a/direct/src/plugin/p3dConcreteStruct.cxx b/direct/src/plugin/p3dConcreteStruct.cxx new file mode 100644 index 0000000000..f686864d83 --- /dev/null +++ b/direct/src/plugin/p3dConcreteStruct.cxx @@ -0,0 +1,158 @@ +// Filename: p3dConcreteStruct.cxx +// Created by: drose (14Jul09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "p3dConcreteStruct.h" + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::Default Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DConcreteStruct:: +P3DConcreteStruct() { +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +P3DConcreteStruct:: +~P3DConcreteStruct() { + Elements::iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + P3D_OBJECT_DECREF((*ei).second); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::get_type +// Access: Public, Virtual +// Description: Returns the fundamental type of this kind of object. +//////////////////////////////////////////////////////////////////// +P3D_object_type P3DConcreteStruct:: +get_type() { + return P3D_OT_object; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::get_bool +// Access: Public, Virtual +// Description: Returns the object value coerced to a boolean, if +// possible. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteStruct:: +get_bool() { + return !_elements.empty(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::make_string +// Access: Public, Virtual +// Description: Fills the indicated C++ string object with the value +// of this object coerced to a string. +//////////////////////////////////////////////////////////////////// +void P3DConcreteStruct:: +make_string(string &value) { + ostringstream strm; + strm << "{"; + if (!_elements.empty()) { + Elements::iterator ei; + ei = _elements.begin(); + strm << (*ei).first << ": " << *(*ei).second; + ++ei; + while (ei != _elements.end()) { + strm << ", " << (*ei).first << ": " << *(*ei).second; + ++ei; + } + } + strm << "}"; + + value = strm.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::get_property +// Access: Public, Virtual +// Description: Returns the named property element in the object. The +// return value is a new-reference P3D_object, or NULL +// on error. +//////////////////////////////////////////////////////////////////// +P3D_object *P3DConcreteStruct:: +get_property(const string &property) { + Elements::const_iterator ei = _elements.find(property); + if (ei != _elements.end()) { + P3D_OBJECT_INCREF((*ei).second); + return (*ei).second; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::set_property +// Access: Public, Virtual +// Description: Modifies (or deletes, if value is NULL) the named +// property element in the object. Returns true on +// success, false on failure. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteStruct:: +set_property(const string &property, P3D_object *value) { + if (value == NULL) { + // Delete an element. + Elements::iterator ei = _elements.find(property); + if (ei == _elements.end()) { + // Invalid property. + return false; + } + P3D_OBJECT_DECREF((*ei).second); + _elements.erase(ei); + return true; + + } else { + // Replace or insert an element. + P3D_OBJECT_INCREF(value); + pair result = _elements.insert(Elements::value_type(property, value)); + if (!result.second) { + // Replacing an element. + Elements::iterator ei = result.first; + P3D_OBJECT_DECREF((*ei).second); + (*ei).second = value; + } + return true; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DConcreteStruct::fill_xml +// Access: Public, Virtual +// Description: If this object has a valid XML representation for the +// indicated session (that hasn't already been +// implemented by the generic code in P3DSession), this +// method will apply it to the indicated "value" element +// and return true. Otherwise, this method will leave +// the element unchanged and return false. +//////////////////////////////////////////////////////////////////// +bool P3DConcreteStruct:: +fill_xml(TiXmlElement *xvalue, P3DSession *session) { + xvalue->SetAttribute("type", "concrete_struct"); + Elements::const_iterator ei; + for (ei = _elements.begin(); ei != _elements.end(); ++ei) { + TiXmlElement *xitem = session->p3dobj_to_xml((*ei).second); + xitem->SetAttribute("key", (*ei).first); + xvalue->LinkEndChild(xitem); + } + + return true; +} diff --git a/direct/src/plugin/p3dConcreteStruct.h b/direct/src/plugin/p3dConcreteStruct.h new file mode 100644 index 0000000000..22060307ff --- /dev/null +++ b/direct/src/plugin/p3dConcreteStruct.h @@ -0,0 +1,51 @@ +// Filename: p3dConcreteStruct.h +// Created by: drose (14Jul09) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef P3DCONCRETESTRUCT_H +#define P3DCONCRETESTRUCT_H + +#include "p3d_plugin_common.h" +#include "p3dObject.h" + +//////////////////////////////////////////////////////////////////// +// Class : P3DConcreteStruct +// Description : A simple object that contains a standard mapping of +// string -> element. It is passed by value between +// Python and Javascript, so it may be more optimal for +// relatively small objects. +// +// Methods are not supported. +//////////////////////////////////////////////////////////////////// +class P3DConcreteStruct : public P3DObject { +public: + P3DConcreteStruct(); + virtual ~P3DConcreteStruct(); + + virtual P3D_object_type get_type(); + virtual bool get_bool(); + + virtual void make_string(string &value); + + virtual P3D_object *get_property(const string &property); + virtual bool set_property(const string &property, P3D_object *value); + + virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); + +private: + typedef map Elements; + Elements _elements; +}; + +#endif + diff --git a/direct/src/plugin/p3dConditionVar.cxx b/direct/src/plugin/p3dConditionVar.cxx index 63343b325a..982f064b21 100644 --- a/direct/src/plugin/p3dConditionVar.cxx +++ b/direct/src/plugin/p3dConditionVar.cxx @@ -140,7 +140,10 @@ wait(double timeout) { ts.tv_nsec += (int)((timeout - seconds) * 1000000.0); int result = pthread_cond_timedwait(&_cvar, &_lock, &ts); - assert(result == 0 || result == ETIMEDOUT); + if (result != 0 && result != ETIMEDOUT && errno != ETIMEDOUT) { + perror("pthread_cond_timedwait"); + assert(false); + } #endif // _WIN32 } diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index e22f92e61c..45a03c22eb 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -726,6 +726,7 @@ send_browser_script_object() { //////////////////////////////////////////////////////////////////// P3D_request *P3DInstance:: make_p3d_request(TiXmlElement *xrequest) { + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); P3D_request *request = NULL; const char *rtype = xrequest->Attribute("rtype"); @@ -750,17 +751,13 @@ make_p3d_request(TiXmlElement *xrequest) { int unique_id = 0; xrequest->Attribute("unique_id", &unique_id); - vector values; + P3D_object *value = NULL; TiXmlElement *xvalue = xrequest->FirstChildElement("value"); - while (xvalue != NULL) { - P3D_object *value = _session->xml_to_p3dobj(xvalue); - values.push_back(value); - xvalue = xvalue->NextSiblingElement("value"); + if (xvalue != NULL) { + value = _session->xml_to_p3dobj(xvalue); } - - P3D_object **values_ptr = NULL; - if (!values.empty()) { - values_ptr = &values[0]; + if (value == NULL) { + value = inst_mgr->new_none_object(); } if (operation != NULL && xobject != NULL) { @@ -770,14 +767,11 @@ make_p3d_request(TiXmlElement *xrequest) { property_name = ""; } - handle_script_request(operation, object, property_name, - values_ptr, values.size(), + handle_script_request(operation, object, property_name, value, (needs_response != 0), unique_id); } - for (size_t i = 0; i < values.size(); ++i) { - P3D_OBJECT_DECREF(values[i]); - } + P3D_OBJECT_DECREF(value); } else if (strcmp(rtype, "drop_p3dobj") == 0) { int object_id; @@ -858,8 +852,7 @@ handle_notify_request(const string &message) { //////////////////////////////////////////////////////////////////// void P3DInstance:: handle_script_request(const string &operation, P3D_object *object, - const string &property_name, - P3D_object *values[], int num_values, + const string &property_name, P3D_object *value, bool needs_response, int unique_id) { TiXmlDocument *doc = new TiXmlDocument; @@ -883,13 +876,8 @@ handle_script_request(const string &operation, P3D_object *object, } } else if (operation == "set_property") { - bool result; - if (num_values == 1) { - result = P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), values[0]); - } else { - // Wrong number of values. Error. - result = false; - } + bool result = + P3D_OBJECT_SET_PROPERTY(object, property_name.c_str(), value); TiXmlElement *xvalue = new TiXmlElement("value"); xvalue->SetAttribute("type", "bool"); @@ -905,6 +893,17 @@ handle_script_request(const string &operation, P3D_object *object, xcommand->LinkEndChild(xvalue); } else if (operation == "call") { + // Convert the single value parameter into an array of parameters. + P3D_object **values = &value; + int num_values = 1; + if (value->_class == &P3DObject::_object_class) { + P3DObject *p3dobj = (P3DObject *)value; + if (p3dobj->get_object_array_size() != -1) { + values = p3dobj->get_object_array(); + num_values = p3dobj->get_object_array_size(); + } + } + P3D_object *result = P3D_OBJECT_CALL(object, property_name.c_str(), values, num_values); @@ -915,18 +914,12 @@ handle_script_request(const string &operation, P3D_object *object, } else if (operation == "eval") { P3D_object *result; - if (num_values == 1) { - P3D_object *expression = values[0]; - int size = P3D_OBJECT_GET_STRING(expression, NULL, 0); - char *buffer = new char[size + 1]; - P3D_OBJECT_GET_STRING(expression, buffer, size + 1); - result = P3D_OBJECT_EVAL(object, buffer); - logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n"; - delete[] buffer; - } else { - // Wrong number of values. Error. - result = NULL; - } + int size = P3D_OBJECT_GET_STRING(value, NULL, 0); + char *buffer = new char[size + 1]; + P3D_OBJECT_GET_STRING(value, buffer, size + 1); + result = P3D_OBJECT_EVAL(object, buffer); + logfile << " eval " << *object << ": " << buffer << ", result = " << result << "\n"; + delete[] buffer; if (result != NULL) { xcommand->LinkEndChild(_session->p3dobj_to_xml(result)); diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 3972099222..c9788255c3 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -102,8 +102,7 @@ private: P3D_request *make_p3d_request(TiXmlElement *xrequest); void handle_notify_request(const string &message); void handle_script_request(const string &operation, P3D_object *object, - const string &property_name, - P3D_object *values[], int num_values, + const string &property_name, P3D_object *value, bool needs_response, int unique_id); void make_splash_window(); void install_progress(P3DPackage *package, double progress); diff --git a/direct/src/plugin/p3dInstanceManager.cxx b/direct/src/plugin/p3dInstanceManager.cxx index c4579ed094..19fc21547e 100644 --- a/direct/src/plugin/p3dInstanceManager.cxx +++ b/direct/src/plugin/p3dInstanceManager.cxx @@ -87,10 +87,12 @@ P3DInstanceManager:: << " " << _false_object->_ref_count << "\n" << flush; + /* assert(_undefined_object->_ref_count == 1); assert(_none_object->_ref_count == 1); assert(_true_object->_ref_count == 1); assert(_false_object->_ref_count == 1); + */ P3D_OBJECT_DECREF(_undefined_object); P3D_OBJECT_DECREF(_none_object); diff --git a/direct/src/plugin/p3dObject.cxx b/direct/src/plugin/p3dObject.cxx index 55fc415e01..d2bc6912a2 100644 --- a/direct/src/plugin/p3dObject.cxx +++ b/direct/src/plugin/p3dObject.cxx @@ -199,17 +199,6 @@ P3DObject:: assert(_ref_count == 0); } -//////////////////////////////////////////////////////////////////// -// Function: P3DObject::is_python_object -// Access: Public, Virtual -// Description: Returns true if this is actually an instance of a -// P3DPythonObject, false otherwise. -//////////////////////////////////////////////////////////////////// -bool P3DObject:: -is_python_object() { - return false; -} - //////////////////////////////////////////////////////////////////// // Function: P3DObject::get_int // Access: Public, Virtual @@ -338,6 +327,47 @@ output(ostream &out) { out << value; } +//////////////////////////////////////////////////////////////////// +// Function: P3DObject::fill_xml +// Access: Public, Virtual +// Description: If this object has a valid XML representation for the +// indicated session (that hasn't already been +// implemented by the generic code in P3DSession), this +// method will apply it to the indicated "value" element +// and return true. Otherwise, this method will leave +// the element unchanged and return false. +//////////////////////////////////////////////////////////////////// +bool P3DObject:: +fill_xml(TiXmlElement *xvalue, P3DSession *session) { + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DObject::get_object_array +// Access: Public +// Description: Returns a pointer to the array of objects represented +// by this object, if any, or NULL if the object does +// not represent an array of objects. This may also +// return NULL for a zero-length array; use +// get_object_array_size() to differentiate. +//////////////////////////////////////////////////////////////////// +P3D_object **P3DObject:: +get_object_array() { + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: P3DObject::get_object_array_size +// Access: Public +// Description: Returns the number of elements in the array returned +// by get_object_array(), or -1 if this object does not +// representan array of objects. +//////////////////////////////////////////////////////////////////// +int P3DObject:: +get_object_array_size() { + return -1; +} + //////////////////////////////////////////////////////////////////// // Function: P3DObject::get_bool_property // Access: Public diff --git a/direct/src/plugin/p3dObject.h b/direct/src/plugin/p3dObject.h index 0bf2ef0314..905079f9a7 100644 --- a/direct/src/plugin/p3dObject.h +++ b/direct/src/plugin/p3dObject.h @@ -33,8 +33,6 @@ protected: public: virtual ~P3DObject(); - virtual bool is_python_object(); - virtual P3D_object_type get_type()=0; virtual bool get_bool()=0; virtual int get_int(); @@ -53,6 +51,9 @@ public: virtual P3D_object *eval(const string &expression); virtual void output(ostream &out); + virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); + virtual P3D_object **get_object_array(); + virtual int get_object_array_size(); // Convenience functions. bool get_bool_property(const string &property); diff --git a/direct/src/plugin/p3dPythonObject.cxx b/direct/src/plugin/p3dPythonObject.cxx index 8917cbba23..08e56b4c41 100644 --- a/direct/src/plugin/p3dPythonObject.cxx +++ b/direct/src/plugin/p3dPythonObject.cxx @@ -43,17 +43,6 @@ P3DPythonObject:: unref_delete(_session); } -//////////////////////////////////////////////////////////////////// -// Function: P3DPythonObject::is_python_object -// Access: Public, Virtual -// Description: Returns true if this is actually an instance of a -// P3DPythonObject, false otherwise. -//////////////////////////////////////////////////////////////////// -bool P3DPythonObject:: -is_python_object() { - return true; -} - //////////////////////////////////////////////////////////////////// // Function: P3DPythonObject::get_type // Access: Public, Virtual @@ -153,7 +142,7 @@ get_property(const string &property) { P3D_object *params[1]; params[0] = new P3DStringObject(property); - P3D_object *result = call("__getattr__", params, 1); + P3D_object *result = call("__get_property__", params, 1); P3D_OBJECT_DECREF(params[0]); return result; } @@ -176,12 +165,12 @@ set_property(const string &property, P3D_object *value) { if (value == NULL) { // Delete an attribute. - result = call("__delattr__", params, 1); + result = call("__del_property__", params, 1); } else { // Set a new attribute. params[1] = value; - result = call("__setattr__", params, 2); + result = call("__set_property__", params, 2); } P3D_OBJECT_DECREF(params[0]); @@ -282,6 +271,31 @@ output(ostream &out) { } } +//////////////////////////////////////////////////////////////////// +// Function: P3DPythonObject::fill_xml +// Access: Public, Virtual +// Description: If this object has a valid XML representation for the +// indicated session (that hasn't already been +// implemented by the generic code in P3DSession), this +// method will apply it to the indicated "value" element +// and return true. Otherwise, this method will leave +// the element unchanged and return false. +//////////////////////////////////////////////////////////////////// +bool P3DPythonObject:: +fill_xml(TiXmlElement *xvalue, P3DSession *session) { + if (session == _session) { + // If it's a P3DPythonObject from the same session, just send + // the object_id down, since the actual implementation of this + // object exists (as a Python object) in the sub-process space. + xvalue->SetAttribute("type", "python"); + xvalue->SetAttribute("object_id", _object_id); + return true; + } + + // Otherwise, we have to send it as a browser object. + return false; +} + //////////////////////////////////////////////////////////////////// // Function: P3DPythonObject::get_session // Access: Public diff --git a/direct/src/plugin/p3dPythonObject.h b/direct/src/plugin/p3dPythonObject.h index 0c5fc358bb..99f020d8c0 100644 --- a/direct/src/plugin/p3dPythonObject.h +++ b/direct/src/plugin/p3dPythonObject.h @@ -33,8 +33,6 @@ public: P3DPythonObject(P3DSession *session, int object_id); virtual ~P3DPythonObject(); - virtual bool is_python_object(); - public: virtual P3D_object_type get_type(); virtual bool get_bool(); @@ -51,6 +49,7 @@ public: P3D_object *params[], int num_params); virtual void output(ostream &out); + virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session); P3DSession *get_session(); int get_object_id(); diff --git a/direct/src/plugin/p3dPythonRun.cxx b/direct/src/plugin/p3dPythonRun.cxx index 7eff431bc8..c559f1f80f 100755 --- a/direct/src/plugin/p3dPythonRun.cxx +++ b/direct/src/plugin/p3dPythonRun.cxx @@ -144,6 +144,13 @@ run_python() { return false; } + // Get the ConcreteStruct class. + _concrete_struct_class = PyObject_GetAttrString(runp3d, "ConcreteStruct"); + if (_concrete_struct_class == NULL) { + PyErr_Print(); + return false; + } + // Get the BrowserObject class. _browser_object_class = PyObject_GetAttrString(runp3d, "BrowserObject"); if (_browser_object_class == NULL) { @@ -324,7 +331,7 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, doc.LinkEndChild(xresponse); const char *op = xcommand->Attribute("op"); - if (op != NULL) { + if (op != NULL && !PyErr_Occurred()) { if (strcmp(op, "get_panda_script_object") == 0) { // Get Panda's toplevel Python object. PyObject *obj = PyObject_CallMethod(_runner, (char*)"getPandaScriptObject", (char *)""); @@ -396,20 +403,44 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, strcmp(method_name, "toString") == 0) { result = PyObject_Str(obj); - } else if (strcmp(method_name, "__setattr__") == 0) { + } else if (strcmp(method_name, "__set_property__") == 0) { + // We call these methods __set_property__ et al instead of + // __setattr__ et al, because they do not precisely + // duplicate the Python semantics. char *property_name; PyObject *value; if (PyArg_ParseTuple(params, "sO", &property_name, &value)) { - PyObject_SetAttrString(obj, property_name, value); - result = Py_True; - Py_INCREF(result); - } + bool success = false; - } else if (strcmp(method_name, "__delattr__") == 0) { - char *property_name; - if (PyArg_ParseTuple(params, "s", &property_name)) { + // If it already exists as an attribute, update it there. if (PyObject_HasAttrString(obj, property_name)) { - PyObject_DelAttrString(obj, property_name); + if (PyObject_SetAttrString(obj, property_name, value) != -1) { + success = true; + } else { + PyErr_Clear(); + } + } + + // If the object supports the mapping protocol, store it + // in the object's dictionary. + if (!success && PyMapping_Check(obj)) { + if (PyMapping_SetItemString(obj, property_name, value) != -1) { + success = true; + } else { + PyErr_Clear(); + } + } + + // Finally, try to store it on the object. + if (!success) { + if (PyObject_SetAttrString(obj, property_name, value) != -1) { + success = true; + } else { + PyErr_Clear(); + } + } + + if (success) { result = Py_True; } else { result = Py_False; @@ -417,13 +448,61 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response, Py_INCREF(result); } - } else if (strcmp(method_name, "__getattr__") == 0) { + } else if (strcmp(method_name, "__del_property__") == 0) { char *property_name; if (PyArg_ParseTuple(params, "s", &property_name)) { + bool success = false; + + if (PyObject_HasAttrString(obj, property_name)) { + if (PyObject_DelAttrString(obj, property_name) != -1) { + success = true; + } else { + PyErr_Clear(); + } + } + + if (!success) { + if (PyObject_DelItemString(obj, property_name) != -1) { + success = true; + } else { + PyErr_Clear(); + } + } + + if (success) { + result = Py_True; + } else { + result = Py_False; + } + Py_INCREF(result); + } + + } else if (strcmp(method_name, "__get_property__") == 0) { + char *property_name; + if (PyArg_ParseTuple(params, "s", &property_name)) { + bool success = false; + if (PyObject_HasAttrString(obj, property_name)) { result = PyObject_GetAttrString(obj, property_name); + if (result != NULL) { + success = true; + } else { + PyErr_Clear(); + } + } - } else { + if (!success) { + if (PyMapping_HasKeyString(obj, property_name)) { + result = PyMapping_GetItemString(obj, property_name); + if (result != NULL) { + success = true; + } else { + PyErr_Clear(); + } + } + } + + if (!success) { result = NULL; } } @@ -693,26 +772,8 @@ py_request_func(PyObject *args) { TiXmlElement *xobject = pyobj_to_xml(object); xobject->SetValue("object"); xrequest->LinkEndChild(xobject); - - if (strcmp(operation, "call") == 0 && PySequence_Check(value)) { - // A special case: operation "call" receives a tuple of - // parameters; unpack the tuple for the XML. - Py_ssize_t length = PySequence_Length(value); - for (Py_ssize_t i = 0; i < length; ++i) { - PyObject *p = PySequence_GetItem(value, i); - if (p != NULL) { - TiXmlElement *xvalue = pyobj_to_xml(p); - xrequest->LinkEndChild(xvalue); - Py_DECREF(p); - } - } - - } else { - // Other kinds of operations receive only a single parameter, if - // any. - TiXmlElement *xvalue = pyobj_to_xml(value); - xrequest->LinkEndChild(xvalue); - } + TiXmlElement *xvalue = pyobj_to_xml(value); + xrequest->LinkEndChild(xvalue); write_xml(_pipe_write, &doc, nout); @@ -1045,6 +1106,71 @@ pyobj_to_xml(PyObject *value) { xvalue->SetAttribute("value", str); } + } else if (PyTuple_Check(value)) { + // An immutable sequence. Pass it as a concrete. + xvalue->SetAttribute("type", "concrete_sequence"); + + Py_ssize_t length = PySequence_Length(value); + for (Py_ssize_t i = 0; i < length; ++i) { + PyObject *item = PySequence_GetItem(value, i); + if (item != NULL) { + xvalue->LinkEndChild(pyobj_to_xml(item)); + Py_DECREF(item); + } + } + + } else if (PyObject_IsInstance(value, _concrete_struct_class)) { + // This is a ConcreteStruct. + xvalue->SetAttribute("type", "concrete_struct"); + + PyObject *items = PyObject_CallMethod(value, (char *)"getConcreteProperties", (char *)""); + if (items == NULL) { + PyErr_Print(); + return xvalue; + } + + Py_ssize_t length = PySequence_Length(items); + for (Py_ssize_t i = 0; i < length; ++i) { + PyObject *item = PySequence_GetItem(items, i); + if (item != NULL) { + PyObject *a = PySequence_GetItem(item, 0); + if (a != NULL) { + PyObject *b = PySequence_GetItem(item, 1); + if (b != NULL) { + TiXmlElement *xitem = pyobj_to_xml(b); + Py_DECREF(b); + + PyObject *as_str; + if (PyUnicode_Check(a)) { + // The key is a unicode value. + as_str = PyUnicode_AsUTF8String(a); + } else { + // The key is a string value or something else. Make it + // a string. + as_str = PyObject_Str(a); + } + char *buffer; + Py_ssize_t length; + if (PyString_AsStringAndSize(as_str, &buffer, &length) != -1) { + string str(buffer, length); + xitem->SetAttribute("key", str); + } + Py_DECREF(as_str); + + xvalue->LinkEndChild(xitem); + } + Py_DECREF(a); + } + Py_DECREF(item); + } + } + + // We've already avoided errors in the above code; clear the error + // flag. + PyErr_Clear(); + + Py_DECREF(items); + } else if (PyObject_IsInstance(value, _undefined_object_class)) { // This is an UndefinedObject. xvalue->SetAttribute("type", "undefined"); @@ -1139,6 +1265,44 @@ xml_to_pyobj(TiXmlElement *xvalue) { _runner, object_id); } + } else if (strcmp(type, "concrete_sequence") == 0) { + // Receive a concrete sequence as a tuple. + PyObject *list = PyList_New(0); + + TiXmlElement *xitem = xvalue->FirstChildElement("value"); + while (xitem != NULL) { + PyObject *item = xml_to_pyobj(xitem); + if (item != NULL) { + PyList_Append(list, item); + Py_DECREF(item); + } + xitem = xitem->NextSiblingElement("value"); + } + + PyObject *result = PyList_AsTuple(list); + Py_DECREF(list); + return result; + + } else if (strcmp(type, "concrete_struct") == 0) { + // Receive a concrete struct as a new ConcreteStruct instance. + PyObject *obj = PyObject_CallFunction(_concrete_struct_class, (char *)""); + + if (obj != NULL) { + TiXmlElement *xitem = xvalue->FirstChildElement("value"); + while (xitem != NULL) { + const char *key = xitem->Attribute("key"); + if (key != NULL) { + PyObject *item = xml_to_pyobj(xitem); + if (item != NULL) { + PyObject_SetAttrString(obj, key, item); + Py_DECREF(item); + } + } + xitem = xitem->NextSiblingElement("value"); + } + return obj; + } + } else if (strcmp(type, "python") == 0) { int object_id; if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { diff --git a/direct/src/plugin/p3dPythonRun.h b/direct/src/plugin/p3dPythonRun.h index 963e65976d..b9d3033ee0 100755 --- a/direct/src/plugin/p3dPythonRun.h +++ b/direct/src/plugin/p3dPythonRun.h @@ -114,6 +114,7 @@ private: PyObject *_runner; PyObject *_undefined_object_class; PyObject *_undefined; + PyObject *_concrete_struct_class; PyObject *_browser_object_class; PyObject *_taskMgr; diff --git a/direct/src/plugin/p3dSession.cxx b/direct/src/plugin/p3dSession.cxx index 8ce86021fa..9a35998df1 100644 --- a/direct/src/plugin/p3dSession.cxx +++ b/direct/src/plugin/p3dSession.cxx @@ -22,6 +22,8 @@ #include "p3dIntObject.h" #include "p3dFloatObject.h" #include "p3dPythonObject.h" +#include "p3dConcreteSequence.h" +#include "p3dConcreteStruct.h" #include "binaryXml.h" #ifndef _WIN32 @@ -387,6 +389,35 @@ xml_to_p3dobj(const TiXmlElement *xvalue) { return new P3DStringObject(*value); } + } else if (strcmp(type, "concrete_sequence") == 0) { + P3DConcreteSequence *obj = new P3DConcreteSequence; + const TiXmlElement *xitem = xvalue->FirstChildElement("value"); + while (xitem != NULL) { + P3D_object *item = xml_to_p3dobj(xitem); + if (item != NULL) { + obj->append(item); + P3D_OBJECT_DECREF(item); + } + xitem = xitem->NextSiblingElement("value"); + } + return obj; + + } else if (strcmp(type, "concrete_struct") == 0) { + P3DConcreteStruct *obj = new P3DConcreteStruct; + const TiXmlElement *xitem = xvalue->FirstChildElement("value"); + while (xitem != NULL) { + const char *key = xitem->Attribute("key"); + if (key != NULL) { + P3D_object *item = xml_to_p3dobj(xitem); + if (item != NULL) { + obj->set_property(key, item); + P3D_OBJECT_DECREF(item); + } + } + xitem = xitem->NextSiblingElement("value"); + } + return obj; + } else if (strcmp(type, "browser") == 0) { int object_id; if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) { @@ -461,21 +492,21 @@ p3dobj_to_xml(P3D_object *obj) { break; case P3D_OT_object: - if (obj->_class == &P3DObject::_object_class && - ((P3DObject *)obj)->is_python_object() && - ((P3DPythonObject *)obj)->get_session() == this) { - // If it's one of our kind of objects, it must be a - // P3DPythonObject. In this case, just send the object_id down, - // since the actual implementation of this object exists (as a - // Python object) in the sub-process space. - int object_id = ((P3DPythonObject *)obj)->get_object_id(); - xvalue->SetAttribute("type", "python"); - xvalue->SetAttribute("object_id", object_id); + P3DObject *p3dobj = NULL; + if (obj->_class == &P3DObject::_object_class) { + p3dobj = (P3DObject *)obj; + } + + if (p3dobj != NULL && p3dobj->fill_xml(xvalue, this)) { + // This object has a specialized XML representation, valid for + // this particular session. It has already been filled into + // xvalue. } else { - // Otherwise, it must a host-provided object, which means we - // should pass a reference down to this particular object, so - // the Python process knows to call back up to here to query it. + // Otherwise, it must a host-provided object, or a Python object + // from another session; which means we should pass a reference + // down to this particular object, so the Python process knows + // to call back up to here to query it. P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); int object_id = inst_mgr->get_unique_id(); diff --git a/direct/src/plugin/p3d_plugin_composite1.cxx b/direct/src/plugin/p3d_plugin_composite1.cxx index 4d7cda875d..37c530006a 100644 --- a/direct/src/plugin/p3d_plugin_composite1.cxx +++ b/direct/src/plugin/p3d_plugin_composite1.cxx @@ -1,5 +1,7 @@ #include "p3d_plugin.cxx" #include "p3dBoolObject.cxx" +#include "p3dConcreteSequence.cxx" +#include "p3dConcreteStruct.cxx" #include "p3dConditionVar.cxx" #include "p3dDownload.cxx" #include "p3dFileDownload.cxx" diff --git a/direct/src/plugin_npapi/ppPandaObject.cxx b/direct/src/plugin_npapi/ppPandaObject.cxx index 489be293a2..1abc50cf73 100644 --- a/direct/src/plugin_npapi/ppPandaObject.cxx +++ b/direct/src/plugin_npapi/ppPandaObject.cxx @@ -58,13 +58,11 @@ make_new(PPInstance *inst, P3D_object *p3d_object) { //////////////////////////////////////////////////////////////////// void PPPandaObject:: set_p3d_object(P3D_object *p3d_object) { - if (_p3d_object != p3d_object) { - P3D_OBJECT_XDECREF(_p3d_object); - _p3d_object = p3d_object; - if (_p3d_object != NULL) { - P3D_OBJECT_INCREF(_p3d_object); - } + if (p3d_object != NULL) { + P3D_OBJECT_INCREF(p3d_object); } + P3D_OBJECT_XDECREF(_p3d_object); + _p3d_object = p3d_object; } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/showutil/runp3d.py b/direct/src/showutil/runp3d.py index 259089e977..5a73da78d6 100755 --- a/direct/src/showutil/runp3d.py +++ b/direct/src/showutil/runp3d.py @@ -26,6 +26,10 @@ from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcF from direct.stdpy import file from direct.task.TaskManagerGlobal import taskMgr from direct.showbase import AppRunnerGlobal + +# These imports are read by the C++ wrapper in p3dPythonRun.cxx. +from direct.showutil.JavaScript import UndefinedObject, Undefined, ConcreteStruct, BrowserObject + import os import types import __builtin__ @@ -60,9 +64,8 @@ class AppRunner(DirectObject): self.windowOpened = False self.windowPrc = None - # Store this Undefined instance where the application can easily - # get to it. self.Undefined = Undefined + self.ConcreteStruct = ConcreteStruct # This is per session. self.nextScriptId = 0 @@ -457,234 +460,6 @@ class AppRunner(DirectObject): return (osFilename, tokens) -class UndefinedObject: - """ This is a special object that is returned by the browser to - represent a NULL pointer, typically the return value for a failed - operation. It has no attributes, similar to None. """ - - def __nonzero__(self): - return False - - def __str__(self): - return "Undefined" - -# In fact, we normally always return this precise instance of the -# UndefinedObject. -Undefined = UndefinedObject() - -class BrowserObject: - """ This class provides the Python wrapper around some object that - actually exists in the plugin host's namespace, e.g. a JavaScript - or DOM object. """ - - def __init__(self, runner, objectId): - self.__dict__['_BrowserObject__runner'] = runner - self.__dict__['_BrowserObject__objectId'] = objectId - - # This element is filled in by __getattr__; it connects - # the object to its parent. - self.__dict__['_BrowserObject__boundMethod'] = (None, None) - - def __del__(self): - # When the BrowserObject destructs, tell the parent process it - # doesn't need to keep around its corresponding P3D_object any - # more. - self.__runner.dropObject(self.__objectId) - - def __str__(self): - parentObj, attribName = self.__boundMethod - if parentObj: - # Format it from its parent. - return "%s.%s" % (parentObj, attribName) - else: - # Format it directly. - return "BrowserObject(%s)" % (self.__objectId) - - def __nonzero__(self): - return True - - def __call__(self, *args): - try: - parentObj, attribName = self.__boundMethod - if parentObj: - # Call it as a method. - needsResponse = True - if parentObj is self.__runner.dom and attribName == 'alert': - # As a special hack, we don't wait for the return - # value from the alert() call, since this is a - # blocking call, and waiting for this could cause - # problems. - needsResponse = False - - if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], types.StringTypes): - # As another special hack, we make dom.eval() a - # special case, and map it directly into an eval() - # call. If the string begins with 'void ', we further - # assume we're not waiting for a response. - if args[0].startswith('void '): - needsResponse = False - result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse) - else: - # This is a normal method call. - try: - result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse) - except EnvironmentError: - # Problem on the call. Maybe no such method? - raise AttributeError - else: - # Call it as a plain function. - result = self.__runner.scriptRequest('call', self, value = args) - except EnvironmentError: - # Some odd problem on the call. - raise TypeError - - return result - - def __getattr__(self, name): - """ Remaps attempts to query an attribute, as in obj.attr, - into the appropriate calls to query the actual browser object - under the hood. """ - - try: - value = self.__runner.scriptRequest('get_property', self, - propertyName = name) - except EnvironmentError: - # Failed to retrieve the attribute. But maybe there's a - # 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) - - raise AttributeError(name) - - if isinstance(value, BrowserObject): - # Fill in the parent object association, so __call__ can - # 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) - - return value - - def __setattr__(self, name, value): - if name in self.__dict__: - self.__dict__[name] = value - return - - result = self.__runner.scriptRequest('set_property', self, - propertyName = name, - value = value) - if not result: - raise AttributeError(name) - - def __delattr__(self, name): - if name in self.__dict__: - del self.__dict__[name] - return - - result = self.__runner.scriptRequest('del_property', self, - propertyName = name) - if not result: - raise AttributeError(name) - - def __getitem__(self, key): - """ Remaps attempts to query an attribute, as in obj['attr'], - into the appropriate calls to query the actual browser object - under the hood. Following the JavaScript convention, we treat - obj['attr'] almost the same as obj.attr. """ - - try: - value = self.__runner.scriptRequest('get_property', self, - propertyName = str(key)) - except EnvironmentError: - # Failed to retrieve the property. We return IndexError - # for numeric keys so we can properly support Python's - # iterators, but we return KeyError for string keys to - # emulate mapping objects. - if isinstance(key, types.StringTypes): - raise KeyError(key) - else: - raise IndexError(key) - - return value - - def __setitem__(self, key, value): - result = self.__runner.scriptRequest('set_property', self, - propertyName = str(key), - value = value) - if not result: - if isinstance(key, types.StringTypes): - raise KeyError(key) - else: - raise IndexError(key) - - def __delitem__(self, key): - result = self.__runner.scriptRequest('del_property', self, - propertyName = str(key)) - if not result: - if isinstance(key, types.StringTypes): - raise KeyError(key) - else: - raise IndexError(key) - -class MethodWrapper: - """ This is a Python wrapper around a property of a BrowserObject - that doesn't appear to be a first-class object in the Python - sense, but is nonetheless a callable method. """ - - def __init__(self, runner, parentObj, objectId): - self.__dict__['_MethodWrapper__runner'] = runner - self.__dict__['_MethodWraper__boundMethod'] = (parentObj, objectId) - - def __str__(self): - parentObj, attribName = self.__boundMethod - return "%s.%s" % (parentObj, attribName) - - def __nonzero__(self): - return True - - def __call__(self, *args): - try: - parentObj, attribName = self.__boundMethod - # Call it as a method. - needsResponse = True - if parentObj is self.__runner.dom and attribName == 'alert': - # As a special hack, we don't wait for the return - # value from the alert() call, since this is a - # blocking call, and waiting for this could cause - # problems. - needsResponse = False - - if parentObj is self.__runner.dom and attribName == 'eval' and len(args) == 1 and isinstance(args[0], types.StringTypes): - # As another special hack, we make dom.eval() a - # special case, and map it directly into an eval() - # call. If the string begins with 'void ', we further - # assume we're not waiting for a response. - if args[0].startswith('void '): - needsResponse = False - result = self.__runner.scriptRequest('eval', parentObj, value = args[0], needsResponse = needsResponse) - else: - # This is a normal method call. - try: - result = self.__runner.scriptRequest('call', parentObj, propertyName = attribName, value = args, needsResponse = needsResponse) - except EnvironmentError: - # Problem on the call. Maybe no such method? - raise AttributeError - - except EnvironmentError: - # Some odd problem on the call. - raise TypeError - - return result - - def __setattr__(self, name, value): - """ setattr will generally fail on method objects. """ - raise AttributeError(name) - - def __delattr__(self, name): - """ delattr will generally fail on method objects. """ - raise AttributeError(name) - if __name__ == '__main__': runner = AppRunner(0) runner.gotWindow = True