diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index d66ac0d5..763e9160 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -74,6 +74,15 @@ public: */ static PyObject *JSObjectProxy_get(JSObjectProxy *self, PyObject *key); + /** + * @brief Getter method (.sq_contains), returns whether a key exists, used by the in operator + * + * @param self - The JSObjectProxy + * @param key - The key for the value in the JSObjectProxy + * @return int 1 if `key` is in dict, 0 if not, and -1 on error + */ + static int JSObjectProxy_contains(JSObjectProxy *self, PyObject *key); + /** * @brief Assign method (.mp_ass_subscript), assigns a key-value pair if value is non-NULL, or deletes a key-value pair if value is NULL * @@ -84,15 +93,6 @@ public: */ static int JSObjectProxy_assign(JSObjectProxy *self, PyObject *key, PyObject *value); - /** - * @brief Helper function for various JSObjectProxy methods, sets a key-value pair on a JSObject given a python string key and a JS::Value value - * - * @param jsObject - The underlying backing store JSObject for the JSObjectProxy - * @param key - The key to be assigned or deleted - * @param value - The JS::Value to be assigned - */ - static void JSObjectProxy_set_helper(JS::HandleObject jsObject, PyObject *key, JS::HandleValue value); - /** * @brief Comparison method (.tp_richcompare), returns appropriate boolean given a comparison operator and other pyobject * @@ -111,6 +111,7 @@ public: * @param visited * @return bool - Whether the compared objects are equal or not */ + // private static bool JSObjectProxy_richcompare_helper(JSObjectProxy *self, PyObject *other, std::unordered_map &visited); /** @@ -141,6 +142,10 @@ static PyMappingMethods JSObjectProxy_mapping_methods = { .mp_ass_subscript = (objobjargproc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign }; +static PySequenceMethods JSObjectProxy_sequence_methods = { + .sq_contains = (objobjproc)JSObjectProxyMethodDefinitions::JSObjectProxy_contains +}; + /** * @brief Struct for the JSObjectProxyType, used by all JSObjectProxy objects */ diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 00392ba7..f2bb30bf 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -82,7 +82,23 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - return pyTypeFactory(GLOBAL_CX, global, value)->getPyObject(); + PyType *pyType = pyTypeFactory(GLOBAL_CX, global, value); + delete value; + delete global; + return pyType->getPyObject(); +} + +int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, PyObject *key) +{ + JS::RootedId id(GLOBAL_CX); + if (!keyToId(key, &id)) { + // TODO (Caleb Aikens): raise exception here + return -1; // key is not a str or int + } + + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); + return value.isUndefined() ? 0 : 1; } int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, PyObject *key, PyObject *value) @@ -104,15 +120,9 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, Py return 0; } -void JSObjectProxyMethodDefinitions::JSObjectProxy_set_helper(JS::HandleObject jsObject, PyObject *key, JS::HandleValue value) -{ - JS::RootedId id(GLOBAL_CX); - keyToId(key, &id); - JS_SetPropertyById(GLOBAL_CX, jsObject, id, value); -} - PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare(JSObjectProxy *self, PyObject *other, int op) { + if (op != Py_EQ && op != Py_NE) { Py_RETURN_NOTIMPLEMENTED; } @@ -163,7 +173,8 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr } // iterate recursively through members of self and check for equality - for (size_t i = 0; i < props.length(); i++) + size_t length = props.length(); + for (size_t i = 0; i < length; i++) { JS::HandleId id = props[i]; JS::RootedValue *key = new JS::RootedValue(GLOBAL_CX); diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index af98ec1d..3b85ec2f 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -74,6 +74,7 @@ PyTypeObject JSObjectProxyType = { .tp_basicsize = sizeof(JSObjectProxy), .tp_dealloc = (destructor)JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc, .tp_repr = (reprfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_repr, + .tp_as_sequence = &JSObjectProxy_sequence_methods, .tp_as_mapping = &JSObjectProxy_mapping_methods, .tp_getattro = (getattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get, .tp_setattro = (setattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign, @@ -312,9 +313,9 @@ PyMethodDef PythonMonkeyMethods[] = { struct PyModuleDef pythonmonkey = { PyModuleDef_HEAD_INIT, - "pythonmonkey", /* name of module */ - "A module for python to JS interoperability", /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ + "pythonmonkey", /* name of module */ + "A module for Python to JavaScript interoperability", /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */ PythonMonkeyMethods };