changeset 22:988a8998c75f

JS objects reflected into Python are now identity-preserving, though the implementation for this is pretty bad right now.
author Atul Varma <varmaa@toolness.com>
date Sun, 28 Jun 2009 21:49:07 -0700
parents fc04e5f1c675
children 951ad1b15587
files object.c object.h runtime.c runtime.h test_pymonkey.py
diffstat 5 files changed, 90 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/object.c	Sun Jun 28 20:40:18 2009 -0700
+++ b/object.c	Sun Jun 28 21:49:07 2009 -0700
@@ -1,5 +1,6 @@
 #include "object.h"
 #include "runtime.h"
+#include "utils.h"
 
 JSClass PYM_JS_ObjectClass = {
   "PymonkeyObject", JSCLASS_GLOBAL_FLAGS,
@@ -11,16 +12,26 @@
 static void
 PYM_JSObjectDealloc(PYM_JSObject *self)
 {
-  // JS_RemoveRoot() always returns JS_TRUE, so don't
-  // bother checking its return value.
+  if (self->weakrefList)
+    PyObject_ClearWeakRefs((PyObject *) self);
 
   if (self->obj) {
+    if (PyDict_DelItem(self->runtime->objects,
+                       self->uniqueId) == -1)
+      PySys_WriteStderr("WARNING: PyDict_DelItem() failed.\n");
+    Py_DECREF(self->uniqueId);
+    self->uniqueId = NULL;
+
+    // JS_RemoveRoot() always returns JS_TRUE, so don't
+    // bother checking its return value.
     JS_RemoveRootRT(self->runtime->rt, &self->obj);
     self->obj = NULL;
   }
 
-  Py_DECREF(self->runtime);
-  self->runtime = NULL;
+  if (self->runtime) {
+    Py_DECREF(self->runtime);
+    self->runtime = NULL;
+  }
 }
 
 PyTypeObject PYM_JSObjectType = {
@@ -51,7 +62,8 @@
   0,		               /* tp_traverse */
   0,		               /* tp_clear */
   0,		               /* tp_richcompare */
-  0,		               /* tp_weaklistoffset */
+  /* tp_weaklistoffset */
+  offsetof(PYM_JSObject, weakrefList),
   0,		               /* tp_iter */
   0,		               /* tp_iternext */
   0,                           /* tp_methods */
@@ -69,15 +81,56 @@
 
 PYM_JSObject *PYM_newJSObject(PYM_JSContextObject *context,
                               JSObject *obj) {
+  jsid uniqueId;
+  if (!JS_GetObjectId(context->cx, obj, &uniqueId)) {
+    PyErr_SetString(PYM_error, "JS_GetObjectId() failed");
+    return NULL;
+  }
+  PyObject *pyUniqueId = PyLong_FromLong(uniqueId);
+  if (pyUniqueId == NULL)
+    return NULL;
+
+  PYM_JSRuntimeObject *runtime = context->runtime;
+  PyObject *cachedObject = PyDict_GetItem(runtime->objects,
+                                          pyUniqueId);
+  if (cachedObject) {
+    cachedObject = PyWeakref_GetObject(cachedObject);
+    Py_INCREF(cachedObject);
+    Py_DECREF(pyUniqueId);
+    return (PYM_JSObject *) cachedObject;
+  }
+
   PYM_JSObject *object = PyObject_New(PYM_JSObject,
                                       &PYM_JSObjectType);
-  if (object == NULL)
+  if (object == NULL) {
+    Py_DECREF(pyUniqueId);
     return NULL;
+  }
+
+  object->runtime = NULL;
+  object->obj = NULL;
+  object->uniqueId = NULL;
+  object->weakrefList = NULL;
+
+  PyObject *weakref = PyWeakref_NewRef((PyObject *) object, NULL);
+  if (weakref == NULL) {
+    Py_DECREF(object);
+    Py_DECREF(pyUniqueId);
+    return NULL;
+  }
+
+  if (PyDict_SetItem(runtime->objects, pyUniqueId, weakref) == -1) {
+    Py_DECREF(weakref);
+    Py_DECREF(object);
+    Py_DECREF(pyUniqueId);
+    return NULL;
+  }
 
   object->runtime = context->runtime;
   Py_INCREF(object->runtime);
 
   object->obj = obj;
+  object->uniqueId = pyUniqueId;
 
   JS_AddNamedRootRT(object->runtime->rt, &object->obj,
                     "Pymonkey-Generated Object");
--- a/object.h	Sun Jun 28 20:40:18 2009 -0700
+++ b/object.h	Sun Jun 28 21:49:07 2009 -0700
@@ -12,6 +12,8 @@
   PyObject_HEAD
   PYM_JSRuntimeObject *runtime;
   JSObject *obj;
+  PyObject *uniqueId;
+  PyObject *weakrefList;
 } PYM_JSObject;
 
 extern PyTypeObject PYM_JSObjectType;
--- a/runtime.c	Sun Jun 28 20:40:18 2009 -0700
+++ b/runtime.c	Sun Jun 28 21:49:07 2009 -0700
@@ -10,12 +10,22 @@
 
   self = (PYM_JSRuntimeObject *) type->tp_alloc(type, 0);
   if (self != NULL) {
-    self->rt = JS_NewRuntime(8L * 1024L * 1024L);
-    if (!self->rt) {
-      PyErr_SetString(PYM_error, "JS_NewRuntime() failed");
+    self->rt = NULL;
+
+    self->objects = PyDict_New();
+    if (self->objects == NULL) {
       type->tp_dealloc((PyObject *) self);
       self = NULL;
     }
+
+    if (self != NULL) {
+      self->rt = JS_NewRuntime(8L * 1024L * 1024L);
+      if (!self->rt) {
+        PyErr_SetString(PYM_error, "JS_NewRuntime() failed");
+        type->tp_dealloc((PyObject *) self);
+        self = NULL;
+      }
+    }
   }
 
   return (PyObject *) self;
@@ -24,6 +34,11 @@
 static void
 PYM_JSRuntimeDealloc(PYM_JSRuntimeObject *self)
 {
+  if (self->objects) {
+    Py_DECREF(self->objects);
+    self->objects = NULL;
+  }
+
   if (self->rt) {
     JS_DestroyRuntime(self->rt);
     self->rt = NULL;
--- a/runtime.h	Sun Jun 28 20:40:18 2009 -0700
+++ b/runtime.h	Sun Jun 28 21:49:07 2009 -0700
@@ -7,6 +7,7 @@
 typedef struct {
   PyObject_HEAD
   JSRuntime *rt;
+  PyObject *objects;
 } PYM_JSRuntimeObject;
 
 extern PyTypeObject PYM_JSRuntimeType;
--- a/test_pymonkey.py	Sun Jun 28 20:40:18 2009 -0700
+++ b/test_pymonkey.py	Sun Jun 28 21:49:07 2009 -0700
@@ -9,6 +9,16 @@
         cx.init_standard_classes(obj)
         return cx.evaluate_script(obj, code, '<string>', 1)
 
+    def testObjectIsIdentityPreserving(self):
+        cx = pymonkey.Runtime().new_context()
+        obj = cx.new_object()
+        cx.init_standard_classes(obj)
+        cx.evaluate_script(obj, 'foo = {bar: 1}', '<string>', 1)
+        self.assertTrue(isinstance(cx.get_property(obj, "foo"),
+                                   pymonkey.Object))
+        self.assertTrue(cx.get_property(obj, "foo") is
+                        cx.get_property(obj, "foo"))
+
     def testObjectGetattrWorks(self):
         cx = pymonkey.Runtime().new_context()
         obj = cx.new_object()