Skip to content

Commit

Permalink
Implement 'in' operation for Objects
Browse files Browse the repository at this point in the history
add sq_contains as part of tp_as_sequence
  • Loading branch information
philippedistributive committed Nov 23, 2023
1 parent 749fc65 commit cdfeb51
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 21 deletions.
23 changes: 14 additions & 9 deletions include/JSObjectProxy.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand All @@ -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
*
Expand All @@ -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<PyObject *, PyObject *> &visited);

/**
Expand Down Expand Up @@ -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
*/
Expand Down
29 changes: 20 additions & 9 deletions src/JSObjectProxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions src/modules/pythonmonkey/pythonmonkey.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
};

Expand Down

0 comments on commit cdfeb51

Please sign in to comment.