瀏覽代碼

concrete objects

David Rose 16 年之前
父節點
當前提交
1b6bb92da5

+ 4 - 0
direct/src/plugin/Sources.pp

@@ -30,6 +30,8 @@
     p3d_plugin_config.h \
     p3d_plugin_config.h \
     p3d_plugin_common.h \
     p3d_plugin_common.h \
     p3dBoolObject.h \
     p3dBoolObject.h \
+    p3dConcreteSequence.h \
+    p3dConcreteStruct.h \
     p3dConditionVar.h p3dConditionVar.I \
     p3dConditionVar.h p3dConditionVar.I \
     p3dDownload.h p3dDownload.I \
     p3dDownload.h p3dDownload.I \
     p3dFileDownload.h p3dFileDownload.I \
     p3dFileDownload.h p3dFileDownload.I \
@@ -56,6 +58,8 @@
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
     p3d_plugin.cxx \
     p3d_plugin.cxx \
     p3dBoolObject.cxx \
     p3dBoolObject.cxx \
+    p3dConcreteSequence.cxx \
+    p3dConcreteStruct.cxx \
     p3dConditionVar.cxx \
     p3dConditionVar.cxx \
     p3dDownload.cxx \
     p3dDownload.cxx \
     p3dFileDownload.cxx \
     p3dFileDownload.cxx \

+ 253 - 0
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);
+}

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

+ 158 - 0
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<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;
+}

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

+ 4 - 1
direct/src/plugin/p3dConditionVar.cxx

@@ -140,7 +140,10 @@ wait(double timeout) {
   ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
   ts.tv_nsec += (int)((timeout - seconds) * 1000000.0);
 
 
   int result = pthread_cond_timedwait(&_cvar, &_lock, &ts);
   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
 #endif  // _WIN32
 }
 }

+ 28 - 35
direct/src/plugin/p3dInstance.cxx

@@ -726,6 +726,7 @@ send_browser_script_object() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 P3D_request *P3DInstance::
 P3D_request *P3DInstance::
 make_p3d_request(TiXmlElement *xrequest) {
 make_p3d_request(TiXmlElement *xrequest) {
+  P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
   P3D_request *request = NULL;
   P3D_request *request = NULL;
 
 
   const char *rtype = xrequest->Attribute("rtype");
   const char *rtype = xrequest->Attribute("rtype");
@@ -750,17 +751,13 @@ make_p3d_request(TiXmlElement *xrequest) {
       int unique_id = 0;
       int unique_id = 0;
       xrequest->Attribute("unique_id", &unique_id);
       xrequest->Attribute("unique_id", &unique_id);
         
         
-      vector<P3D_object *> values;
+      P3D_object *value = NULL;
       TiXmlElement *xvalue = xrequest->FirstChildElement("value");
       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) {
       if (operation != NULL && xobject != NULL) {
@@ -770,14 +767,11 @@ make_p3d_request(TiXmlElement *xrequest) {
           property_name = "";
           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);
                               (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) {
     } else if (strcmp(rtype, "drop_p3dobj") == 0) {
       int object_id;
       int object_id;
@@ -858,8 +852,7 @@ handle_notify_request(const string &message) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void P3DInstance::
 void P3DInstance::
 handle_script_request(const string &operation, P3D_object *object, 
 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) {
                       bool needs_response, int unique_id) {
 
 
   TiXmlDocument *doc = new TiXmlDocument;
   TiXmlDocument *doc = new TiXmlDocument;
@@ -883,13 +876,8 @@ handle_script_request(const string &operation, P3D_object *object,
     }
     }
 
 
   } else if (operation == "set_property") {
   } 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");
     TiXmlElement *xvalue = new TiXmlElement("value");
     xvalue->SetAttribute("type", "bool");
     xvalue->SetAttribute("type", "bool");
@@ -905,6 +893,17 @@ handle_script_request(const string &operation, P3D_object *object,
     xcommand->LinkEndChild(xvalue);
     xcommand->LinkEndChild(xvalue);
 
 
   } else if (operation == "call") {
   } 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 *result =
       P3D_OBJECT_CALL(object, property_name.c_str(), values, num_values);
       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") {
   } else if (operation == "eval") {
     P3D_object *result;
     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) {
     if (result != NULL) {
       xcommand->LinkEndChild(_session->p3dobj_to_xml(result));
       xcommand->LinkEndChild(_session->p3dobj_to_xml(result));

+ 1 - 2
direct/src/plugin/p3dInstance.h

@@ -102,8 +102,7 @@ private:
   P3D_request *make_p3d_request(TiXmlElement *xrequest);
   P3D_request *make_p3d_request(TiXmlElement *xrequest);
   void handle_notify_request(const string &message);
   void handle_notify_request(const string &message);
   void handle_script_request(const string &operation, P3D_object *object, 
   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);
                              bool needs_response, int unique_id);
   void make_splash_window();
   void make_splash_window();
   void install_progress(P3DPackage *package, double progress);
   void install_progress(P3DPackage *package, double progress);

+ 2 - 0
direct/src/plugin/p3dInstanceManager.cxx

@@ -87,10 +87,12 @@ P3DInstanceManager::
        << " " << _false_object->_ref_count
        << " " << _false_object->_ref_count
        << "\n" << flush;
        << "\n" << flush;
 
 
+  /*
   assert(_undefined_object->_ref_count == 1);
   assert(_undefined_object->_ref_count == 1);
   assert(_none_object->_ref_count == 1);
   assert(_none_object->_ref_count == 1);
   assert(_true_object->_ref_count == 1);
   assert(_true_object->_ref_count == 1);
   assert(_false_object->_ref_count == 1);
   assert(_false_object->_ref_count == 1);
+  */
 
 
   P3D_OBJECT_DECREF(_undefined_object);
   P3D_OBJECT_DECREF(_undefined_object);
   P3D_OBJECT_DECREF(_none_object);
   P3D_OBJECT_DECREF(_none_object);

+ 41 - 11
direct/src/plugin/p3dObject.cxx

@@ -199,17 +199,6 @@ P3DObject::
   assert(_ref_count == 0);
   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
 //     Function: P3DObject::get_int
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -338,6 +327,47 @@ output(ostream &out) {
   out << value;
   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
 //     Function: P3DObject::get_bool_property
 //       Access: Public
 //       Access: Public

+ 3 - 2
direct/src/plugin/p3dObject.h

@@ -33,8 +33,6 @@ protected:
 public:
 public:
   virtual ~P3DObject();
   virtual ~P3DObject();
 
 
-  virtual bool is_python_object();
-
   virtual P3D_object_type get_type()=0;
   virtual P3D_object_type get_type()=0;
   virtual bool get_bool()=0;
   virtual bool get_bool()=0;
   virtual int get_int();
   virtual int get_int();
@@ -53,6 +51,9 @@ public:
   virtual P3D_object *eval(const string &expression);
   virtual P3D_object *eval(const string &expression);
 
 
   virtual void output(ostream &out);
   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.
   // Convenience functions.
   bool get_bool_property(const string &property);
   bool get_bool_property(const string &property);

+ 28 - 14
direct/src/plugin/p3dPythonObject.cxx

@@ -43,17 +43,6 @@ P3DPythonObject::
   unref_delete(_session);
   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
 //     Function: P3DPythonObject::get_type
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -153,7 +142,7 @@ get_property(const string &property) {
   P3D_object *params[1];
   P3D_object *params[1];
   params[0] = new P3DStringObject(property);
   params[0] = new P3DStringObject(property);
 
 
-  P3D_object *result = call("__getattr__", params, 1);
+  P3D_object *result = call("__get_property__", params, 1);
   P3D_OBJECT_DECREF(params[0]);
   P3D_OBJECT_DECREF(params[0]);
   return result;
   return result;
 }
 }
@@ -176,12 +165,12 @@ set_property(const string &property, P3D_object *value) {
 
 
   if (value == NULL) {
   if (value == NULL) {
     // Delete an attribute.
     // Delete an attribute.
-    result = call("__delattr__", params, 1);
+    result = call("__del_property__", params, 1);
 
 
   } else {
   } else {
     // Set a new attribute.
     // Set a new attribute.
     params[1] = value;
     params[1] = value;
-    result = call("__setattr__", params, 2);
+    result = call("__set_property__", params, 2);
   }
   }
 
 
   P3D_OBJECT_DECREF(params[0]);
   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
 //     Function: P3DPythonObject::get_session
 //       Access: Public
 //       Access: Public

+ 1 - 2
direct/src/plugin/p3dPythonObject.h

@@ -33,8 +33,6 @@ public:
   P3DPythonObject(P3DSession *session, int object_id);
   P3DPythonObject(P3DSession *session, int object_id);
   virtual ~P3DPythonObject();
   virtual ~P3DPythonObject();
 
 
-  virtual bool is_python_object();
-
 public:
 public:
   virtual P3D_object_type get_type();
   virtual P3D_object_type get_type();
   virtual bool get_bool();
   virtual bool get_bool();
@@ -51,6 +49,7 @@ public:
                            P3D_object *params[], int num_params);
                            P3D_object *params[], int num_params);
 
 
   virtual void output(ostream &out);
   virtual void output(ostream &out);
+  virtual bool fill_xml(TiXmlElement *xvalue, P3DSession *session);
 
 
   P3DSession *get_session();
   P3DSession *get_session();
   int get_object_id();
   int get_object_id();

+ 192 - 28
direct/src/plugin/p3dPythonRun.cxx

@@ -144,6 +144,13 @@ run_python() {
     return false;
     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.
   // Get the BrowserObject class.
   _browser_object_class = PyObject_GetAttrString(runp3d, "BrowserObject");
   _browser_object_class = PyObject_GetAttrString(runp3d, "BrowserObject");
   if (_browser_object_class == NULL) {
   if (_browser_object_class == NULL) {
@@ -324,7 +331,7 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
   doc.LinkEndChild(xresponse);
   doc.LinkEndChild(xresponse);
 
 
   const char *op = xcommand->Attribute("op");
   const char *op = xcommand->Attribute("op");
-  if (op != NULL) {
+  if (op != NULL && !PyErr_Occurred()) {
     if (strcmp(op, "get_panda_script_object") == 0) {
     if (strcmp(op, "get_panda_script_object") == 0) {
       // Get Panda's toplevel Python object.
       // Get Panda's toplevel Python object.
       PyObject *obj = PyObject_CallMethod(_runner, (char*)"getPandaScriptObject", (char *)"");
       PyObject *obj = PyObject_CallMethod(_runner, (char*)"getPandaScriptObject", (char *)"");
@@ -396,20 +403,73 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
                    strcmp(method_name, "toString") == 0) {
                    strcmp(method_name, "toString") == 0) {
           result = PyObject_Str(obj);
           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;
           char *property_name;
           PyObject *value;
           PyObject *value;
           if (PyArg_ParseTuple(params, "sO", &property_name, &value)) {
           if (PyArg_ParseTuple(params, "sO", &property_name, &value)) {
-            PyObject_SetAttrString(obj, property_name, value);
-            result = Py_True;
+            bool success = false;
+
+            // If it already exists as an attribute, update it there.
+            if (PyObject_HasAttrString(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;
+            }
             Py_INCREF(result);
             Py_INCREF(result);
           }
           }
 
 
-        } else if (strcmp(method_name, "__delattr__") == 0) {
+        } else if (strcmp(method_name, "__del_property__") == 0) {
           char *property_name;
           char *property_name;
           if (PyArg_ParseTuple(params, "s", &property_name)) {
           if (PyArg_ParseTuple(params, "s", &property_name)) {
+            bool success = false;
+
             if (PyObject_HasAttrString(obj, property_name)) {
             if (PyObject_HasAttrString(obj, property_name)) {
-              PyObject_DelAttrString(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;
               result = Py_True;
             } else {
             } else {
               result = Py_False;
               result = Py_False;
@@ -417,13 +477,32 @@ handle_pyobj_command(TiXmlElement *xcommand, bool needs_response,
             Py_INCREF(result);
             Py_INCREF(result);
           }
           }
 
 
-        } else if (strcmp(method_name, "__getattr__") == 0) {
+        } else if (strcmp(method_name, "__get_property__") == 0) {
           char *property_name;
           char *property_name;
           if (PyArg_ParseTuple(params, "s", &property_name)) {
           if (PyArg_ParseTuple(params, "s", &property_name)) {
+            bool success = false;
+
             if (PyObject_HasAttrString(obj, property_name)) {
             if (PyObject_HasAttrString(obj, property_name)) {
               result = PyObject_GetAttrString(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;
               result = NULL;
             }
             }
           }
           }
@@ -693,26 +772,8 @@ py_request_func(PyObject *args) {
     TiXmlElement *xobject = pyobj_to_xml(object);
     TiXmlElement *xobject = pyobj_to_xml(object);
     xobject->SetValue("object");
     xobject->SetValue("object");
     xrequest->LinkEndChild(xobject);
     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);
     write_xml(_pipe_write, &doc, nout);
 
 
@@ -1045,6 +1106,71 @@ pyobj_to_xml(PyObject *value) {
       xvalue->SetAttribute("value", str);
       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)) {
   } else if (PyObject_IsInstance(value, _undefined_object_class)) {
     // This is an UndefinedObject.
     // This is an UndefinedObject.
     xvalue->SetAttribute("type", "undefined");
     xvalue->SetAttribute("type", "undefined");
@@ -1139,6 +1265,44 @@ xml_to_pyobj(TiXmlElement *xvalue) {
                                    _runner, object_id);
                                    _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) {
   } else if (strcmp(type, "python") == 0) {
     int object_id;
     int object_id;
     if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
     if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {

+ 1 - 0
direct/src/plugin/p3dPythonRun.h

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

+ 44 - 13
direct/src/plugin/p3dSession.cxx

@@ -22,6 +22,8 @@
 #include "p3dIntObject.h"
 #include "p3dIntObject.h"
 #include "p3dFloatObject.h"
 #include "p3dFloatObject.h"
 #include "p3dPythonObject.h"
 #include "p3dPythonObject.h"
+#include "p3dConcreteSequence.h"
+#include "p3dConcreteStruct.h"
 #include "binaryXml.h"
 #include "binaryXml.h"
 
 
 #ifndef _WIN32
 #ifndef _WIN32
@@ -387,6 +389,35 @@ xml_to_p3dobj(const TiXmlElement *xvalue) {
       return new P3DStringObject(*value);
       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) {
   } else if (strcmp(type, "browser") == 0) {
     int object_id;
     int object_id;
     if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
     if (xvalue->QueryIntAttribute("object_id", &object_id) == TIXML_SUCCESS) {
@@ -461,21 +492,21 @@ p3dobj_to_xml(P3D_object *obj) {
     break;
     break;
 
 
   case P3D_OT_object:
   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 {
     } 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();
       P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
       int object_id = inst_mgr->get_unique_id();
       int object_id = inst_mgr->get_unique_id();

+ 2 - 0
direct/src/plugin/p3d_plugin_composite1.cxx

@@ -1,5 +1,7 @@
 #include "p3d_plugin.cxx"
 #include "p3d_plugin.cxx"
 #include "p3dBoolObject.cxx"
 #include "p3dBoolObject.cxx"
+#include "p3dConcreteSequence.cxx"
+#include "p3dConcreteStruct.cxx"
 #include "p3dConditionVar.cxx"
 #include "p3dConditionVar.cxx"
 #include "p3dDownload.cxx"
 #include "p3dDownload.cxx"
 #include "p3dFileDownload.cxx"
 #include "p3dFileDownload.cxx"

+ 4 - 6
direct/src/plugin_npapi/ppPandaObject.cxx

@@ -58,13 +58,11 @@ make_new(PPInstance *inst, P3D_object *p3d_object) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void PPPandaObject::
 void PPPandaObject::
 set_p3d_object(P3D_object *p3d_object) {
 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;
 }
 }
  
  
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 5 - 230
direct/src/showutil/runp3d.py

@@ -26,6 +26,10 @@ from pandac.PandaModules import VirtualFileSystem, Filename, Multifile, loadPrcF
 from direct.stdpy import file
 from direct.stdpy import file
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.task.TaskManagerGlobal import taskMgr
 from direct.showbase import AppRunnerGlobal
 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 os
 import types
 import types
 import __builtin__
 import __builtin__
@@ -60,9 +64,8 @@ class AppRunner(DirectObject):
         self.windowOpened = False
         self.windowOpened = False
         self.windowPrc = None
         self.windowPrc = None
 
 
-        # Store this Undefined instance where the application can easily
-        # get to it.
         self.Undefined = Undefined
         self.Undefined = Undefined
+        self.ConcreteStruct = ConcreteStruct
 
 
         # This is per session.
         # This is per session.
         self.nextScriptId = 0
         self.nextScriptId = 0
@@ -457,234 +460,6 @@ class AppRunner(DirectObject):
 
 
         return (osFilename, tokens)
         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__':
 if __name__ == '__main__':
     runner = AppRunner(0)
     runner = AppRunner(0)
     runner.gotWindow = True
     runner.gotWindow = True