| 81 | } |
| 82 | |
| 83 | static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId id, bool checkPropertyShadowsMethod) { |
| 84 | // look through the methods for dispatch |
| 85 | for (size_t index = 0;; index++) { |
| 86 | const char *methodName = JSObjectProxyType.tp_methods[index].ml_name; |
| 87 | if (methodName == NULL || !PyUnicode_Check(key)) { |
| 88 | JS::RootedValue value(GLOBAL_CX); |
| 89 | JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); |
| 90 | // if value is a JSFunction, bind `this` to self |
| 91 | /* (Caleb Aikens) its potentially problematic to bind it like this since if the function |
| 92 | * ever gets assigned to another object like so: |
| 93 | * |
| 94 | * jsObjA.func = jsObjB.func |
| 95 | * jsObjA.func() # `this` will be jsObjB not jsObjA |
| 96 | * |
| 97 | * It will be bound to the wrong object, however I can't find a better way to do this, |
| 98 | * and even pyodide works this way weirdly enough: |
| 99 | * https://github.com/pyodide/pyodide/blob/ee863a7f7907dfb6ee4948bde6908453c9d7ac43/src/core/jsproxy.c#L388 |
| 100 | * |
| 101 | * if the user wants to get an unbound JS function to bind later, they will have to get it without accessing it through |
| 102 | * a JSObjectProxy (such as via pythonmonkey.eval or as the result of some other function) |
| 103 | */ |
| 104 | if (value.isObject()) { |
| 105 | JS::RootedObject valueObject(GLOBAL_CX); |
| 106 | JS_ValueToObject(GLOBAL_CX, value, &valueObject); |
| 107 | js::ESClass cls; |
| 108 | JS::GetBuiltinClass(GLOBAL_CX, valueObject, &cls); |
| 109 | if (cls == js::ESClass::Function) { |
| 110 | JS::Rooted<JS::ValueArray<1>> args(GLOBAL_CX); |
| 111 | args[0].setObject(*((*(self->jsObject)).get())); |
| 112 | JS::RootedValue boundFunction(GLOBAL_CX); |
| 113 | if (!JS_CallFunctionName(GLOBAL_CX, valueObject, "bind", args, &boundFunction)) { |
| 114 | PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); |
| 115 | return NULL; |
| 116 | } |
| 117 | value.set(boundFunction); |
| 118 | } |
| 119 | } |
| 120 | else if (value.isUndefined() && PyUnicode_Check(key)) { |
| 121 | if (strcmp("__class__", PyUnicode_AsUTF8(key)) == 0) { |
| 122 | return PyObject_GenericGetAttr((PyObject *)self, key); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | return pyTypeFactory(GLOBAL_CX, value); |
| 127 | } |
| 128 | else { |
| 129 | if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { |
| 130 | if (checkPropertyShadowsMethod) { |
| 131 | // just make sure no property is shadowing a method by name |
| 132 | JS::RootedValue value(GLOBAL_CX); |
| 133 | JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); |
| 134 | if (!value.isUndefined()) { |
| 135 | return pyTypeFactory(GLOBAL_CX, value); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | return PyObject_GenericGetAttr((PyObject *)self, key); |
| 140 | } |
no test coverage detected