concrete objects

This commit is contained in:
David Rose 2009-07-14 20:40:47 +00:00
parent 3a902bdaf3
commit 1b6bb92da5
19 changed files with 886 additions and 348 deletions

View File

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

View File

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

View File

@ -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<P3D_object *> Elements;
Elements _elements;
};
#endif

View File

@ -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<Elements::iterator, bool> 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;
}

View File

@ -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<string, P3D_object *> Elements;
Elements _elements;
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -114,6 +114,7 @@ private:
PyObject *_runner;
PyObject *_undefined_object_class;
PyObject *_undefined;
PyObject *_concrete_struct_class;
PyObject *_browser_object_class;
PyObject *_taskMgr;

View File

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

View File

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

View File

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

View File

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