diff --git a/include/JSObjectIterProxy.hh b/include/JSObjectIterProxy.hh index e2246ce0..06533659 100644 --- a/include/JSObjectIterProxy.hh +++ b/include/JSObjectIterProxy.hh @@ -38,6 +38,7 @@ typedef struct { typedef struct { dictiterobject it; + JS::PersistentRootedObject *iteratorSymbol; } JSObjectIterProxy; /** diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index 0163e713..f624a174 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -28,106 +28,164 @@ void JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_dealloc(JSObjectIterProxy *self) { - delete self->it.props; - PyObject_GC_UnTrack(self); - Py_XDECREF(self->it.di_dict); - PyObject_GC_Del(self); + //printf("JSObjectIterProxy_dealloc 1 , pointer is %p, other is %p\n", self->it.props, self->iteratorSymbol); + if (self->it.props) { + // printf("JSObjectIterProxy_dealloc 2\n"); + delete self->it.props; + // printf("JSObjectIterProxy_dealloc 3\n"); + PyObject_GC_UnTrack(self); + // printf("JSObjectIterProxy_dealloc 4\n"); + Py_XDECREF(self->it.di_dict); + // printf("JSObjectIterProxy_dealloc 5\n"); + PyObject_GC_Del(self); + // printf("JSObjectIterProxy_dealloc 6\n"); + } + else if (self->iteratorSymbol) { + // printf("JSObjectIterProxy_dealloc 7\n"); + self->iteratorSymbol->set(nullptr); + // printf("JSObjectIterProxy_dealloc 8\n"); + delete self->iteratorSymbol; + // printf("JSObjectIterProxy_dealloc 9\n"); + } } int JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_traverse(JSObjectIterProxy *self, visitproc visit, void *arg) { - Py_VISIT(self->it.di_dict); + if (self->it.props) { + Py_VISIT(self->it.di_dict); + } return 0; } int JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_clear(JSObjectIterProxy *self) { - Py_CLEAR(self->it.di_dict); + if (self->it.props) { + Py_CLEAR(self->it.di_dict); + } return 0; } PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_iter(JSObjectIterProxy *self) { + //printf("JSObjectIterProxy_iter\n"); Py_INCREF(&self->it); return (PyObject *)&self->it; } PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObjectIterProxy *self) { - PyDictObject *dict = self->it.di_dict; - if (dict == NULL) { - return NULL; - } + // printf("JSObjectIterProxy_nextkey, self is %p\n", self); - if (self->it.reversed) { - if (self->it.it_index >= 0) { - JS::HandleId id = (*(self->it.props))[(self->it.it_index)--]; - PyObject *key = idToKey(GLOBAL_CX, id); - PyObject *value; - - if (self->it.kind != KIND_KEYS) { - JS::RootedValue jsVal(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, *(((JSObjectProxy *)(self->it.di_dict))->jsObject), id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, jsVal); - } - - PyObject *ret; - if (self->it.kind == KIND_ITEMS) { - ret = PyTuple_Pack(2, key, value); - } - else if (self->it.kind == KIND_VALUES) { - ret = value; - } - else { - ret = key; - } - - Py_INCREF(ret); - if (self->it.kind != KIND_KEYS) { - Py_DECREF(value); - } - - return ret; + if (self->it.props) { + PyDictObject *dict = self->it.di_dict; + if (dict == NULL) { + return NULL; } - } else { - if (self->it.it_index < JSObjectProxyMethodDefinitions::JSObjectProxy_length((JSObjectProxy *)dict)) { - JS::HandleId id = (*(self->it.props))[(self->it.it_index)++]; - PyObject *key = idToKey(GLOBAL_CX, id); - PyObject *value; - - if (self->it.kind != KIND_KEYS) { - JS::RootedValue jsVal(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, *(((JSObjectProxy *)(self->it.di_dict))->jsObject), id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, jsVal); - } - PyObject *ret; - if (self->it.kind == KIND_ITEMS) { - ret = PyTuple_Pack(2, key, value); - } - else if (self->it.kind == KIND_VALUES) { - ret = value; + if (self->it.reversed) { + if (self->it.it_index >= 0) { + JS::HandleId id = (*(self->it.props))[(self->it.it_index)--]; + PyObject *key = idToKey(GLOBAL_CX, id); + PyObject *value; + + if (self->it.kind != KIND_KEYS) { + JS::RootedValue jsVal(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, *(((JSObjectProxy *)(self->it.di_dict))->jsObject), id, &jsVal); + value = pyTypeFactory(GLOBAL_CX, jsVal); + } + + PyObject *ret; + if (self->it.kind == KIND_ITEMS) { + ret = PyTuple_Pack(2, key, value); + } + else if (self->it.kind == KIND_VALUES) { + ret = value; + } + else { + ret = key; + } + + Py_INCREF(ret); + if (self->it.kind != KIND_KEYS) { + Py_DECREF(value); + } + + return ret; } - else { - ret = key; + } else { + if (self->it.it_index < JSObjectProxyMethodDefinitions::JSObjectProxy_length((JSObjectProxy *)dict)) { + JS::HandleId id = (*(self->it.props))[(self->it.it_index)++]; + PyObject *key = idToKey(GLOBAL_CX, id); + PyObject *value; + + if (self->it.kind != KIND_KEYS) { + JS::RootedValue jsVal(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, *(((JSObjectProxy *)(self->it.di_dict))->jsObject), id, &jsVal); + value = pyTypeFactory(GLOBAL_CX, jsVal); + } + + PyObject *ret; + if (self->it.kind == KIND_ITEMS) { + ret = PyTuple_Pack(2, key, value); + } + else if (self->it.kind == KIND_VALUES) { + ret = value; + } + else { + ret = key; + } + + Py_INCREF(ret); + if (self->it.kind != KIND_KEYS) { + Py_DECREF(value); + } + + return ret; } + } + + self->it.di_dict = NULL; + Py_DECREF(dict); + return NULL; + } + else { + JS::RootedObject iteratorNextObject(GLOBAL_CX, self->iteratorSymbol->get()); + JS::RootedValue nextObject(GLOBAL_CX); + + if (!JS_CallFunctionName(GLOBAL_CX, iteratorNextObject, "next", JS::HandleValueArray::empty(), &nextObject)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectIterProxyType.tp_name); + return nullptr; + } + + JS::RootedObject rootedNextObject(GLOBAL_CX, nextObject.toObjectOrNull()); + + JS::RootedValue nextObjectValue(GLOBAL_CX); + + if (!JS_GetProperty(GLOBAL_CX, rootedNextObject, "done", &nextObjectValue)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectIterProxyType.tp_name); + return nullptr; + } - Py_INCREF(ret); - if (self->it.kind != KIND_KEYS) { - Py_DECREF(value); + if (!nextObjectValue.toBoolean()) { + // get value + if (!JS_GetProperty(GLOBAL_CX, rootedNextObject, "value", &nextObjectValue)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectIterProxyType.tp_name); + return nullptr; } - return ret; + return pyTypeFactory(GLOBAL_CX, nextObjectValue); + } + else { + return nullptr; // done with the iteration } } - - self->it.di_dict = NULL; - Py_DECREF(dict); - return NULL; } PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_len(JSObjectIterProxy *self) { - Py_ssize_t len; - if (self->it.di_dict) { - len = JSObjectProxyMethodDefinitions::JSObjectProxy_length((JSObjectProxy *)self->it.di_dict) - self->it.it_index; - if (len >= 0) { - return PyLong_FromSsize_t(len); + // printf("JSObjectIterProxy_len self is %p\n", self); + if (self->it.props) { + Py_ssize_t len; + if (self->it.di_dict) { + len = JSObjectProxyMethodDefinitions::JSObjectProxy_length((JSObjectProxy *)self->it.di_dict) - self->it.it_index; + if (len >= 0) { + return PyLong_FromSsize_t(len); + } } } return PyLong_FromLong(0); diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 13b3e609..32917f89 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -284,26 +284,89 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr } PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter(JSObjectProxy *self) { + printf("JSObjectProxy_iter\n"); + // key iteration JSObjectIterProxy *iterator = PyObject_GC_New(JSObjectIterProxy, &JSObjectIterProxyType); if (iterator == NULL) { return NULL; } - iterator->it.it_index = 0; - iterator->it.reversed = false; - iterator->it.kind = KIND_KEYS; - Py_INCREF(self); - iterator->it.di_dict = (PyDictObject *)self; - iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); - // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, iterator->it.props)) { - return NULL; + + // check if Symbol.iterator is defined + JS::RootedValue symbolValue(GLOBAL_CX, JS::SymbolValue(JS::GetWellKnownSymbol(GLOBAL_CX, JS::SymbolCode::iterator))); + JS::RootedId id(GLOBAL_CX); + + printf("JSObjectProxy_iter 2\n"); + + if (JS_ValueToId(GLOBAL_CX, symbolValue, &id) && id.isSymbol()) { + + printf("JSObjectProxy_iter 3\n"); + + JS::RootedValue iteratorVal(GLOBAL_CX); + if (JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &iteratorVal) < 0) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); + return NULL; + } + + printf("JSObjectProxy_iter 4\n"); + + if (!iteratorVal.isUndefined()) { + printf("JSObjectProxy_iter 5\n"); + JS::RootedValue iteratorObject(GLOBAL_CX); + if (!JS_CallFunctionValue(GLOBAL_CX, nullptr, iteratorVal, JS::HandleValueArray::empty(), &iteratorObject)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); + return NULL; + } + + printf("JSObjectProxy_iter 6\n"); + + iterator->iteratorSymbol = new JS::PersistentRootedObject(GLOBAL_CX); + iterator->iteratorSymbol->set(iteratorObject.toObjectOrNull()); + + iterator->it.props = nullptr; + } + else { + printf("JSObjectProxy_iter 7\n"); + iterator->it.it_index = 0; + iterator->it.reversed = false; + iterator->it.kind = KIND_KEYS; + Py_INCREF(self); + iterator->it.di_dict = (PyDictObject *)self; + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); + + // Get **enumerable** own properties + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, iterator->it.props)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); + return NULL; + } + + iterator->iteratorSymbol = nullptr; + } + } + else { + printf("JSObjectProxy_iter 8\n"); + iterator->it.it_index = 0; + iterator->it.reversed = false; + iterator->it.kind = KIND_KEYS; + Py_INCREF(self); + iterator->it.di_dict = (PyDictObject *)self; + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); + + // Get **enumerable** own properties + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, iterator->it.props)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); + return NULL; + } + + iterator->iteratorSymbol = nullptr; } + PyObject_GC_Track(iterator); return (PyObject *)iterator; } PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter_next(JSObjectProxy *self) { + // printf("JSObjectProxy_iter_next\n"); PyObject *key = PyUnicode_FromString("next"); JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index 72d2decf..b0b478fe 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index aae9ad94..cd543e35 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index e49b6f81..2b3a2dff 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -37,7 +37,6 @@ #include #include #include -#include #include #include diff --git a/tests/python/test_dicts.py b/tests/python/test_dicts.py index 99959926..2e706f50 100644 --- a/tests/python/test_dicts.py +++ b/tests/python/test_dicts.py @@ -387,3 +387,42 @@ def test___class__attribute(): def test___none__attribute(): a = pm.eval("({'0': 1, '1': 2})") assert a[2] is None + + +# iterate with Symbol.iterator +def test_proxy_symbol_iterator(): + obj = pm.eval(""" + class ResultHandle extends Array +{ + values() { + return [9, 12, 15]; + } + + [Symbol.iterator]() { + python.print('Symbol.iterator init'); + let index = 0; + let values = this.values(); // use available values + return { + next: () => ({ + value: values[index++], + done: index > values.length + }) + }; + } + + +} + + new ResultHandle(); + """) + result = [] + for i in obj: + result.append(i) + assert result == [9, 12, 15] + + + + + + +