From ede9026e1e91a4d0384149c0e05662f6d44bfb68 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 22 Nov 2023 14:40:42 -0500 Subject: [PATCH 001/170] chore(JSObjectProxy): add missing include guard --- include/JSObjectProxy.hh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index d66ac0d5..862ecc06 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -9,6 +9,9 @@ * */ +#ifndef PythonMonkey_JSObjectProxy_ +#define PythonMonkey_JSObjectProxy_ + #include #include @@ -145,3 +148,5 @@ static PyMappingMethods JSObjectProxy_mapping_methods = { * @brief Struct for the JSObjectProxyType, used by all JSObjectProxy objects */ extern PyTypeObject JSObjectProxyType; + +#endif \ No newline at end of file From 8e235b5c585c07ffed8629060416e181fa8e86b7 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 22 Nov 2023 14:49:44 -0500 Subject: [PATCH 002/170] feat(PyObjectProxyHandler): implement an opaque type wrapper --- include/PyProxyHandler.hh | 131 +++++++++++++++++++- src/JSObjectProxy.cc | 2 - src/PyProxyHandler.cc | 145 +++++++++++++++++++++-- src/jsTypeFactory.cc | 10 +- src/modules/pythonmonkey/pythonmonkey.cc | 2 + src/pyTypeFactory.cc | 7 +- 6 files changed, 274 insertions(+), 23 deletions(-) diff --git a/include/PyProxyHandler.hh b/include/PyProxyHandler.hh index 21188e26..f3480224 100644 --- a/include/PyProxyHandler.hh +++ b/include/PyProxyHandler.hh @@ -18,7 +18,7 @@ #include /** - * @brief base class for PyProxyHandler and PyListProxyHandler + * @brief base class for PyObjectProxyHandler, PyDictProxyHandler, and PyListProxyHandler */ struct PyBaseProxyHandler : public js::BaseProxyHandler { public: @@ -34,9 +34,134 @@ public: * @brief This struct is the ProxyHandler for JS Proxy Objects pythonmonkey creates to handle coercion from python dicts to JS Objects * */ -struct PyProxyHandler : public PyBaseProxyHandler { +struct PyDictProxyHandler : public PyBaseProxyHandler { public: - PyProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; + PyDictProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; + static const char family; + + /** + * @brief [[OwnPropertyKeys]] + * + * @param cx - pointer to JSContext + * @param proxy - The proxy object who's keys we output + * @param props - out-parameter of object IDs + * @return true - call succeeded + * @return false - call failed and an exception has been raised + */ + bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, + JS::MutableHandleIdVector props) const override; + /** + * @brief [[Delete]] + * + * @param cx - pointer to JSContext + * @param proxy - The proxy object who's property we wish to delete + * @param id - The key we wish to delete + * @param result - @TODO (Caleb Aikens) read up on JS::ObjectOpResult + * @return true - call succeeded + * @return false - call failed and an exception has been raised + */ + bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::ObjectOpResult &result) const override; + /** + * @brief [[HasProperty]] + * + * @param cx - pointer to JSContext + * @param proxy - The proxy object who's propery we wish to check + * @param id - key value of the property to check + * @param bp - out-paramter: true if object has property, false if not + * @return true - call succeeded + * @return false - call failed and an exception has been raised + */ + bool has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + bool *bp) const override; + /** + * @brief [[Get]] + * + * @param cx pointer to JSContext + * @param proxy - The proxy object who's property we wish to check + * @param receiver @TODO (Caleb Aikens) read ECMAScript docs about this + * @param id - Key of the property we wish to get + * @param vp - out-paramter for the gotten property + * @return true - call succeeded + * @return false - call failed and an exception has been raised + */ + bool get(JSContext *cx, JS::HandleObject proxy, + JS::HandleValue receiver, JS::HandleId id, + JS::MutableHandleValue vp) const override; + /** + * @brief [[Set]] + * + * @param cx pointer to JSContext + * @param proxy The proxy object who's property we wish to set + * @param id Key of the property we wish to set + * @param v Value that we wish to set the property to + * @param receiver @TODO (Caleb Aikens) read ECMAScript docs about this + * @param result @TODO (Caleb Aikens) read ECMAScript docs about this + * @return true call succeed + * @return false call failed and an exception has been raised + */ + bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::HandleValue v, JS::HandleValue receiver, + JS::ObjectOpResult &result) const override; + /** + * @brief [[Enumerate]] + * + * @param cx - pointer to JSContext + * @param proxy - The proxy object who's keys we output + * @param props - out-parameter of object IDs + * @return true - call succeeded + * @return false - call failed and an exception has been raised + */ + bool enumerate(JSContext *cx, JS::HandleObject proxy, + JS::MutableHandleIdVector props) const override; + + // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more + /** + * @brief @TODO (Caleb Aikens) read up on what this trap does exactly + * + * @param cx pointer to JSContext + * @param proxy The proxy object who's property we wish to check + * @param id Key of the property we wish to check + * @param bp out-paramter: true if object has property, false if not + * @return true call succeeded + * @return false call failed and an exception has been raised + */ + bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + bool *bp) const override; + /** + * @brief @TODO (Caleb Aikens) read up on what this trap does exactly + * + * @param cx - pointer to JSContext + * @param proxy - The proxy object who's keys we output + * @param props - out-parameter of object IDs + * @return true - call succeeded + * @return false - call failed and an exception has been raised + */ + bool getOwnEnumerablePropertyKeys( + JSContext *cx, JS::HandleObject proxy, + JS::MutableHandleIdVector props) const override; + /** + * @brief Handles python object reference count when JS Proxy object is finalized + * + * @param gcx pointer to JS::GCContext + * @param proxy the proxy object being finalized + */ + void finalize(JS::GCContext *gcx, JSObject *proxy) const override; + + bool getOwnPropertyDescriptor( + JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::MutableHandle> desc + ) const override; + + bool defineProperty(JSContext *cx, JS::HandleObject proxy, + JS::HandleId id, + JS::Handle desc, + JS::ObjectOpResult &result) const override; +}; + +struct PyObjectProxyHandler : public PyBaseProxyHandler { +public: + PyObjectProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; static const char family; /** diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 00392ba7..af6f6cfc 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -21,8 +21,6 @@ #include -JSContext *GLOBAL_CX; /**< pointer to PythonMonkey's JSContext */ - bool keyToId(PyObject *key, JS::MutableHandleId idp) { if (PyUnicode_Check(key)) { // key is str type JS::RootedString idString(GLOBAL_CX); diff --git a/src/PyProxyHandler.cc b/src/PyProxyHandler.cc index db4c9011..d1f7cbad 100644 --- a/src/PyProxyHandler.cc +++ b/src/PyProxyHandler.cc @@ -47,9 +47,9 @@ bool idToIndex(JSContext *cx, JS::HandleId id, Py_ssize_t *index) { } } -const char PyProxyHandler::family = 0; +const char PyDictProxyHandler::family = 0; -bool PyProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { +bool PyDictProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { PyObject *keys = PyDict_Keys(pyObject); size_t length = PyList_Size(keys); if (!props.reserve(length)) { @@ -68,7 +68,7 @@ bool PyProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS:: return true; } -bool PyProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, +bool PyDictProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult &result) const { PyObject *attrName = idToKey(cx, id); if (PyDict_DelItem(pyObject, attrName) < 0) { @@ -77,12 +77,12 @@ bool PyProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId return result.succeed(); } -bool PyProxyHandler::has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, +bool PyDictProxyHandler::has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const { return hasOwn(cx, proxy, id, bp); } -bool PyProxyHandler::get(JSContext *cx, JS::HandleObject proxy, +bool PyDictProxyHandler::get(JSContext *cx, JS::HandleObject proxy, JS::HandleValue receiver, JS::HandleId id, JS::MutableHandleValue vp) const { PyObject *attrName = idToKey(cx, id); @@ -95,7 +95,7 @@ bool PyProxyHandler::get(JSContext *cx, JS::HandleObject proxy, return true; } -bool PyProxyHandler::getOwnPropertyDescriptor( +bool PyDictProxyHandler::getOwnPropertyDescriptor( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle> desc ) const { @@ -114,7 +114,7 @@ bool PyProxyHandler::getOwnPropertyDescriptor( return true; } -bool PyProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, +bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, JS::HandleValue receiver, JS::ObjectOpResult &result) const { JS::RootedValue *rootedV = new JS::RootedValue(cx, v); @@ -126,28 +126,28 @@ bool PyProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, return result.succeed(); } -bool PyProxyHandler::enumerate(JSContext *cx, JS::HandleObject proxy, +bool PyDictProxyHandler::enumerate(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { return this->ownPropertyKeys(cx, proxy, props); } -bool PyProxyHandler::hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, +bool PyDictProxyHandler::hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const { PyObject *attrName = idToKey(cx, id); *bp = PyDict_Contains(pyObject, attrName) == 1; return true; } -bool PyProxyHandler::getOwnEnumerablePropertyKeys( +bool PyDictProxyHandler::getOwnEnumerablePropertyKeys( JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { return this->ownPropertyKeys(cx, proxy, props); } // @TODO (Caleb Aikens) implement this -void PyProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const {} +void PyDictProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const {} -bool PyProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, +bool PyDictProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, JS::ObjectOpResult &result) const { @@ -263,3 +263,124 @@ bool PyListProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::Hand } return result.succeed(); // report success } + +const char PyObjectProxyHandler::family = 0; + +bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { + PyObject *keys = PyObject_Dir(pyObject); + size_t keysLength = PyList_Size(keys); + + PyObject *nonDunderKeys = PyList_New(0); + for (size_t i = 0; i < keysLength; i++) { + PyObject *key = PyList_GetItem(keys, i); + PyObject *isDunder = PyObject_CallMethod(key, "startswith", "(s)", "__"); + if (Py_IsFalse(isDunder)) { // if key starts with "__", ignore it + PyList_Append(nonDunderKeys, key); + } + } + + size_t length = PyList_Size(nonDunderKeys); + + if (!props.reserve(length)) { + return false; // out of memory + } + + for (size_t i = 0; i < length; i++) { + PyObject *key = PyList_GetItem(nonDunderKeys, i); + JS::RootedId jsId(cx); + if (!keyToId(key, &jsId)) { + // TODO (Caleb Aikens): raise exception here + return false; // key is not a str or int + } + props.infallibleAppend(jsId); + } + return true; +} + +bool PyObjectProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::ObjectOpResult &result) const { + PyObject *attrName = idToKey(cx, id); + if (PyObject_SetAttr(pyObject, attrName, NULL) < 0) { + return result.failCantDelete(); // raises JS exception + } + return result.succeed(); +} + +bool PyObjectProxyHandler::has(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + bool *bp) const { + return hasOwn(cx, proxy, id, bp); +} + +bool PyObjectProxyHandler::get(JSContext *cx, JS::HandleObject proxy, + JS::HandleValue receiver, JS::HandleId id, + JS::MutableHandleValue vp) const { + PyObject *attrName = idToKey(cx, id); + if (!PyObject_HasAttr(pyObject, attrName)) { + vp.setUndefined(); // JS objects return undefined for nonpresent keys + return true; + } + + PyObject *p = PyObject_GetAttr(pyObject, attrName); + vp.set(jsTypeFactory(cx, p)); + return true; +} + +bool PyObjectProxyHandler::getOwnPropertyDescriptor( + JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::MutableHandle> desc +) const { + PyObject *attrName = idToKey(cx, id); + PyObject *item = PyObject_GetAttr(pyObject, attrName); + if (!item) { // NULL if the key is not present + desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys + } else { + desc.set(mozilla::Some( + JS::PropertyDescriptor::Data( + jsTypeFactory(cx, item), + {JS::PropertyAttribute::Writable, JS::PropertyAttribute::Enumerable} + ) + )); + } + return true; +} + +bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + JS::HandleValue v, JS::HandleValue receiver, + JS::ObjectOpResult &result) const { + JS::RootedValue *rootedV = new JS::RootedValue(cx, v); + PyObject *attrName = idToKey(cx, id); + JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + return result.failCantSetInterposed(); // raises JS exception + } + return result.succeed(); +} + +bool PyObjectProxyHandler::enumerate(JSContext *cx, JS::HandleObject proxy, + JS::MutableHandleIdVector props) const { + return this->ownPropertyKeys(cx, proxy, props); +} + +bool PyObjectProxyHandler::hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, + bool *bp) const { + PyObject *attrName = idToKey(cx, id); + *bp = PyObject_HasAttr(pyObject, attrName) == 1; + return true; +} + +bool PyObjectProxyHandler::getOwnEnumerablePropertyKeys( + JSContext *cx, JS::HandleObject proxy, + JS::MutableHandleIdVector props) const { + return this->ownPropertyKeys(cx, proxy, props); +} + +// @TODO (Caleb Aikens) implement this +void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const {} + +bool PyObjectProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, + JS::HandleId id, + JS::Handle desc, + JS::ObjectOpResult &result) const { + // Block direct `Object.defineProperty` since we already have the `set` method + return result.failInvalidDescriptor(); +} \ No newline at end of file diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 62dd2dde..7885fd06 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -168,7 +169,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { if (PyList_Check(object)) { proxy = js::NewProxyObject(cx, new PyListProxyHandler(object), v, NULL); } else { - proxy = js::NewProxyObject(cx, new PyProxyHandler(object), v, NULL); + proxy = js::NewProxyObject(cx, new PyDictProxyHandler(object), v, NULL); } returnType.setObject(*proxy); } @@ -186,9 +187,10 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { // memoizePyTypeAndGCThing(p, returnType); } else { - std::string errorString("pythonmonkey cannot yet convert python objects of type: "); - errorString += Py_TYPE(object)->tp_name; - PyErr_SetString(PyExc_TypeError, errorString.c_str()); + JS::RootedValue v(cx); + JSObject *proxy; + proxy = js::NewProxyObject(cx, new PyObjectProxyHandler(object), v, NULL); + returnType.setObject(*proxy); } return returnType; diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index af98ec1d..2e1decf3 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -44,6 +44,8 @@ #include #include +JSContext *GLOBAL_CX; + typedef std::unordered_map *>>::iterator PyToGCIterator; typedef struct { PyObject_HEAD diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index 5a4aec6e..c8c810a3 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -97,12 +97,15 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted JS::Rooted obj(cx); JS_ValueToObject(cx, *rval, &obj); if (JS::GetClass(obj)->isProxyObject()) { - if (js::GetProxyHandler(obj)->family() == &PyProxyHandler::family) { // this is one of our proxies for python dicts - return new DictType(((PyProxyHandler *)js::GetProxyHandler(obj))->pyObject); + if (js::GetProxyHandler(obj)->family() == &PyDictProxyHandler::family) { // this is one of our proxies for python dicts + return new DictType(((PyDictProxyHandler *)js::GetProxyHandler(obj))->pyObject); } if (js::GetProxyHandler(obj)->family() == &PyListProxyHandler::family) { // this is one of our proxies for python lists return new ListType(((PyListProxyHandler *)js::GetProxyHandler(obj))->pyObject); } + if (js::GetProxyHandler(obj)->family() == &PyObjectProxyHandler::family) { // this is one of our proxies for python objects + return new PyType(((PyObjectProxyHandler *)js::GetProxyHandler(obj))->pyObject); + } } js::ESClass cls; JS::GetBuiltinClass(cx, obj, &cls); From 677f64381dc050ef5f1eaeaa1afdfda29367c5a5 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 24 Nov 2023 11:29:25 -0500 Subject: [PATCH 003/170] feat(JSFunctionProxy): implement JSFunctionProxy, JSMethodProxy, and fix usage. This commit introduces some GC segfault issues which must be addressed before merging --- include/FuncType.hh | 3 ++ include/JSFunctionProxy.hh | 59 +++++++++++++++++++++ include/JSMethodProxy.hh | 62 ++++++++++++++++++++++ include/pyTypeFactory.hh | 9 ---- src/FuncType.cc | 10 ++++ src/JSFunctionProxy.cc | 55 ++++++++++++++++++++ src/JSMethodProxy.cc | 66 ++++++++++++++++++++++++ src/jsTypeFactory.cc | 30 +++++++---- src/modules/pythonmonkey/pythonmonkey.cc | 53 ++++++++++++++++++- src/pyTypeFactory.cc | 35 ++----------- 10 files changed, 328 insertions(+), 54 deletions(-) create mode 100644 include/JSFunctionProxy.hh create mode 100644 include/JSMethodProxy.hh create mode 100644 src/JSFunctionProxy.cc create mode 100644 src/JSMethodProxy.cc diff --git a/include/FuncType.hh b/include/FuncType.hh index d8ce62f6..af2d2dfb 100644 --- a/include/FuncType.hh +++ b/include/FuncType.hh @@ -14,6 +14,8 @@ #include "PyType.hh" #include "TypeEnum.hh" +#include + #include /** @@ -22,6 +24,7 @@ struct FuncType : public PyType { public: FuncType(PyObject *object); + FuncType(JSContext *cx, JS::HandleValue fval); const TYPE returnType = TYPE::FUNC; const char *getValue() const; }; diff --git a/include/JSFunctionProxy.hh b/include/JSFunctionProxy.hh new file mode 100644 index 00000000..732ddad2 --- /dev/null +++ b/include/JSFunctionProxy.hh @@ -0,0 +1,59 @@ +/** + * @file JSFunctionProxy.hh + * @author Caleb Aikens (caleb@distributive.network) + * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. + * @version 0.1 + * @date 2023-09-28 + * + * Copyright (c) 2023 Distributive Corp. + * + */ + +#ifndef PythonMonkey_JSFunctionProxy_ +#define PythonMonkey_JSFunctionProxy_ + +#include + +#include +/** + * @brief The typedef for the backing store that will be used by JSFunctionProxy objects. All it contains is a pointer to the JSFunction + * + */ +typedef struct { + PyObject_HEAD + JS::PersistentRootedObject *jsFunc; +} JSFunctionProxy; + +/** + * @brief This struct is a bundle of methods used by the JSFunctionProxy type + * + */ +struct JSFunctionProxyMethodDefinitions { +public: + /** + * @brief New method (.tp_new), creates a new instance of the JSFunctionProxy type, exposed as the __new()__ method in python + * + * @param type - The type of object to be created, will always be JSFunctionProxyType or a derived type + * @param args - arguments to the __new()__ method, not used + * @param kwds - keyword arguments to the __new()__ method, not used + * @return PyObject* - A new instance of JSFunctionProxy + */ + static PyObject *JSFunctionProxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + + /** + * @brief Call method (.tp_call), called when the JSFunctionProxy is called + * + * @param self - this callable, might be a free function or a method + * @param args - args to the function + * @param kwargs - keyword args to the function + * @return PyObject* - Result of the function call + */ + static PyObject *JSFunctionProxy_call(PyObject *self, PyObject *args, PyObject *kwargs); +}; + +/** + * @brief Struct for the JSFunctionProxyType, used by all JSFunctionProxy objects + */ +extern PyTypeObject JSFunctionProxyType; + +#endif \ No newline at end of file diff --git a/include/JSMethodProxy.hh b/include/JSMethodProxy.hh new file mode 100644 index 00000000..a364027e --- /dev/null +++ b/include/JSMethodProxy.hh @@ -0,0 +1,62 @@ +/** + * @file JSMethodProxy.hh + * @author Caleb Aikens (caleb@distributive.network) + * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. + * @version 0.1 + * @date 2023-11-14 + * + * Copyright (c) 2023 Distributive Corp. + * + */ + +#ifndef PythonMonkey_JSMethodProxy_ +#define PythonMonkey_JSMethodProxy_ + +#include "include/JSFunctionProxy.hh" + +#include + +#include +/** + * @brief The typedef for the backing store that will be used by JSMethodProxy objects. All it contains is a pointer to the JSFunction and a pointer to self + * + */ +typedef struct { + PyObject_HEAD + PyObject *self; + JS::PersistentRootedObject *jsFunc; +} JSMethodProxy; + +/** + * @brief This struct is a bundle of methods used by the JSMethodProxy type + * + */ +struct JSMethodProxyMethodDefinitions { +public: + /** + * @brief New method (.tp_new), creates a new instance of the JSMethodProxy type, exposed as the __new()__ method in python + * + * @param type - The type of object to be created, will always be JSMethodProxyType or a derived type + * @param args - arguments to the __new()__ method, expected to be a JSFunctionProxy, and an object to bind self to + * @param kwds - keyword arguments to the __new()__ method, not used + * @return PyObject* - A new instance of JSMethodProxy + */ + static PyObject *JSMethodProxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds); + + /** + * @brief Call method (.tp_call), called when the JSMethodProxy is called, properly handling `self` and `this` + * + * @param self - the JSMethodProxy being called + * @param args - args to the method + * @param kwargs - keyword args to the method + * @return PyObject* - Result of the method call + */ + static PyObject *JSMethodProxy_call(PyObject *self, PyObject *args, PyObject *kwargs); +}; + +/** + * @brief Struct for the JSMethodProxyType, used by all JSMethodProxy objects + */ +extern PyTypeObject JSMethodProxyType; + +#endif \ No newline at end of file diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index 68e87b74..3b79cbdd 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -42,13 +42,4 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted */ PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::Rooted *rval); -/** - * @brief Helper function for pyTypeFactory to create FuncTypes through PyCFunction_New - * - * @param JSFuncAddress - Pointer to a PyLongObject containing the memory address of JS::Value containing the JSFunction* - * @param args - Pointer to a PyTupleObject containing the arguments to the python function - * @return PyObject* - The result of the JSFunction called with args coerced to JS types, coerced back to a PyObject type, or NULL if coercion wasn't possible - */ -PyObject *callJSFunc(PyObject *JSFuncAddress, PyObject *args); - #endif \ No newline at end of file diff --git a/src/FuncType.cc b/src/FuncType.cc index 553bf650..b2d76d6b 100644 --- a/src/FuncType.cc +++ b/src/FuncType.cc @@ -1,11 +1,21 @@ #include "include/FuncType.hh" +#include "include/modules/pythonmonkey/pythonmonkey.hh" +#include "include/JSFunctionProxy.hh" #include "include/PyType.hh" +#include + #include FuncType::FuncType(PyObject *object) : PyType(object) {} +FuncType::FuncType(JSContext *cx, JS::HandleValue fval) { + JSFunctionProxy *proxy = (JSFunctionProxy *)PyObject_CallObject((PyObject *)&JSFunctionProxyType, NULL); + proxy->jsFunc->set(&fval.toObject()); + this->pyObject = (PyObject *)proxy; +} + const char *FuncType::getValue() const { return PyUnicode_AsUTF8(PyObject_GetAttrString(pyObject, "__name__")); } \ No newline at end of file diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc new file mode 100644 index 00000000..7ed8c061 --- /dev/null +++ b/src/JSFunctionProxy.cc @@ -0,0 +1,55 @@ +/** + * @file JSFunctionProxy.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. + * @version 0.1 + * @date 2023-09-28 + * + * Copyright (c) 2023 Distributive Corp. + * + */ + +#include "include/JSFunctionProxy.hh" + +#include "include/modules/pythonmonkey/pythonmonkey.hh" +#include "include/jsTypeFactory.hh" +#include "include/pyTypeFactory.hh" +#include "include/setSpiderMonkeyException.hh" + +#include + +#include + +PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { + JSFunctionProxy *self = (JSFunctionProxy *)subtype->tp_alloc(subtype, 0); + if (self) { + self->jsFunc = new JS::PersistentRootedObject(GLOBAL_CX); + } + return (PyObject *)self; +} + +PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, PyObject *args, PyObject *kwargs) { + JSContext *cx = GLOBAL_CX; + JS::RootedValue jsFunc(GLOBAL_CX, JS::ObjectValue(**((JSFunctionProxy *)self)->jsFunc)); + JSObject *o = jsFunc.toObjectOrNull(); + JS::RootedObject thisObj(GLOBAL_CX, JS::GetNonCCWObjectGlobal(o)); + + + JS::RootedVector jsArgsVector(cx); + for (size_t i = 0; i < PyTuple_Size(args); i++) { + JS::Value jsValue = jsTypeFactory(cx, PyTuple_GetItem(args, i)); + if (PyErr_Occurred()) { // Check if an exception has already been set in the flow of control + return NULL; // Fail-fast + } + jsArgsVector.append(jsValue); + } + + JS::HandleValueArray jsArgs(jsArgsVector); + JS::Rooted *jsReturnVal = new JS::Rooted(cx); + if (!JS_CallFunctionValue(cx, thisObj, jsFunc, jsArgs, jsReturnVal)) { + setSpiderMonkeyException(cx); + return NULL; + } + + return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); +} \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc new file mode 100644 index 00000000..939f0451 --- /dev/null +++ b/src/JSMethodProxy.cc @@ -0,0 +1,66 @@ +/** + * @file JSMethodProxy.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. + * @version 0.1 + * @date 2023-11-14 + * + * Copyright (c) 2023 Distributive Corp. + * + */ + +#include "include/JSMethodProxy.hh" + +#include "include/modules/pythonmonkey/pythonmonkey.hh" +#include "include/jsTypeFactory.hh" +#include "include/pyTypeFactory.hh" +#include "include/setSpiderMonkeyException.hh" + +#include + +#include + +PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { + JSFunctionProxy *jsFunctionProxy; + PyObject *im_self; + + if (!PyArg_ParseTuple(args, "O!O", &JSFunctionProxyType, &jsFunctionProxy, &im_self)) { + return NULL; + } + + JSMethodProxy *self = (JSMethodProxy *)subtype->tp_alloc(subtype, 0); + if (self) { + self->self = im_self; + self->jsFunc = new JS::PersistentRootedObject(GLOBAL_CX); + self->jsFunc->set(*(jsFunctionProxy->jsFunc)); + } + + return (PyObject *)self; +} + +PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyObject *args, PyObject *kwargs) { + JSContext *cx = GLOBAL_CX; + JS::RootedValue jsFunc(GLOBAL_CX, JS::ObjectValue(**((JSMethodProxy *)self)->jsFunc)); + JS::RootedValue selfValue(cx, jsTypeFactory(cx, ((JSMethodProxy *)self)->self)); + JS::RootedObject selfObject(cx); + JS_ValueToObject(cx, selfValue, &selfObject); + + JS::RootedVector jsArgsVector(cx); + for (size_t i = 0; i < PyTuple_Size(args); i++) { + JS::Value jsValue = jsTypeFactory(cx, PyTuple_GetItem(args, i)); + if (PyErr_Occurred()) { // Check if an exception has already been set in the flow of control + return NULL; // Fail-fast + } + jsArgsVector.append(jsValue); + } + + JS::HandleValueArray jsArgs(jsArgsVector); + JS::Rooted *jsReturnVal = new JS::Rooted(cx); + if (!JS_CallFunctionValue(cx, selfObject, jsFunc, jsArgs, jsReturnVal)) { + setSpiderMonkeyException(cx); + return NULL; + } + + JS::RootedObject globalObj(cx, JS::CurrentGlobalOrNull(cx)); + return pyTypeFactory(cx, &globalObj, jsReturnVal)->getPyObject(); +} \ No newline at end of file diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 7885fd06..f5d50052 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -14,6 +14,8 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/PyType.hh" #include "include/FuncType.hh" +#include "include/JSFunctionProxy.hh" +#include "include/JSMethodProxy.hh" #include "include/JSObjectProxy.hh" #include "include/PyProxyHandler.hh" #include "include/pyTypeFactory.hh" @@ -122,13 +124,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { } memoizePyTypeAndGCThing(new StrType(object), returnType); } - else if (PyCFunction_Check(object) && PyCFunction_GetFunction(object) == callJSFunc) { - // If it's a wrapped JS function by us, return the underlying JS function rather than wrapping it again - PyObject *jsCxThisFuncTuple = PyCFunction_GetSelf(object); - JS::RootedValue *jsFunc = (JS::RootedValue *)PyLong_AsVoidPtr(PyTuple_GetItem(jsCxThisFuncTuple, 2)); - returnType.set(*jsFunc); - } - else if (PyFunction_Check(object) || PyCFunction_Check(object)) { + else if (PyMethod_Check(object) || PyFunction_Check(object) || PyCFunction_Check(object)) { // can't determine number of arguments for PyCFunctions, so just assume potentially unbounded uint16_t nargs = 0; if (PyFunction_Check(object)) { @@ -138,7 +134,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { } JSFunction *jsFunc = js::NewFunctionWithReserved(cx, callPyFunc, nargs, 0, NULL); - JSObject *jsFuncObject = JS_GetFunctionObject(jsFunc); + JS::RootedObject jsFuncObject(cx, JS_GetFunctionObject(jsFunc)); // We put the address of the PyObject in the JSFunction's 0th private slot so we can access it later js::SetFunctionNativeReserved(jsFuncObject, 0, JS::PrivateValue((void *)object)); @@ -163,6 +159,19 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { else if (PyObject_TypeCheck(object, &JSObjectProxyType)) { returnType.setObject(*((JSObjectProxy *)object)->jsObject); } + else if (PyObject_TypeCheck(object, &JSMethodProxyType)) { + JS::RootedObject func(cx, *((JSMethodProxy *)object)->jsFunc); + PyObject *self = ((JSMethodProxy *)object)->self; + + JS::Rooted> args(cx); + args[0].set(jsTypeFactory(cx, self)); + JS::Rooted boundFunction(cx); + JS_CallFunctionName(cx, func, "bind", args, &boundFunction); + returnType.set(boundFunction); + } + else if (PyObject_TypeCheck(object, &JSFunctionProxyType)) { + returnType.setObject(**((JSFunctionProxy *)object)->jsFunc); + } else if (PyDict_Check(object) || PyList_Check(object)) { JS::RootedValue v(cx); JSObject *proxy; @@ -234,8 +243,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { JS::Value pyFuncVal = js::GetFunctionNativeReserved(&(callargs.callee()), 0); PyObject *pyFunc = (PyObject *)(pyFuncVal.toPrivate()); - JS::RootedObject *thisv = new JS::RootedObject(cx); - JS_ValueToObject(cx, callargs.thisv(), thisv); + JS::RootedObject *globalObject = new JS::RootedObject(cx, JS::CurrentGlobalOrNull(cx)); if (!callargs.length()) { #if PY_VERSION_HEX >= 0x03090000 @@ -256,7 +264,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { PyObject *pyArgs = PyTuple_New(callargs.length()); for (size_t i = 0; i < callargs.length(); i++) { JS::RootedValue *jsArg = new JS::RootedValue(cx, callargs[i]); - PyType *pyArg = pyTypeFactory(cx, thisv, jsArg); + PyType *pyArg = pyTypeFactory(cx, globalObject, jsArg); if (!pyArg) return false; // error occurred PyObject *pyArgObj = pyArg->getPyObject(); if (!pyArgObj) return false; // error occurred diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 2e1decf3..5b03fc5f 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -11,12 +11,13 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" - #include "include/BoolType.hh" #include "include/setSpiderMonkeyException.hh" #include "include/DateType.hh" #include "include/FloatType.hh" #include "include/FuncType.hh" +#include "include/JSFunctionProxy.hh" +#include "include/JSMethodProxy.hh" #include "include/JSObjectProxy.hh" #include "include/PyType.hh" #include "include/pyTypeFactory.hh" @@ -89,6 +90,36 @@ PyTypeObject JSObjectProxyType = { .tp_new = JSObjectProxyMethodDefinitions::JSObjectProxy_new, }; +PyTypeObject JSFunctionProxyType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "pythonmonkey.JSFunctionProxy", + .tp_basicsize = sizeof(JSFunctionProxy), + // .tp_dealloc = (destructor)JSFunctionProxyMethodDefinitions::JSFunctionProxy_dealloc, + // .tp_repr = (reprfunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_repr, + .tp_call = JSFunctionProxyMethodDefinitions::JSFunctionProxy_call, + // .tp_getattro = (getattrofunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_get, + // .tp_setattro = (setattrofunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_assign, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Javascript Function proxy object"), + // .tp_iter = (getiterfunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_iter, + .tp_new = JSFunctionProxyMethodDefinitions::JSFunctionProxy_new, +}; + +PyTypeObject JSMethodProxyType = { + .ob_base = PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "pythonmonkey.JSMethodProxy", + .tp_basicsize = sizeof(JSMethodProxy), + // .tp_dealloc = (destructor)JSMethodProxyMethodDefinitions::JSMethodProxy_dealloc, + // .tp_repr = (reprfunc)JSMethodProxyMethodDefinitions::JSMethodProxy_repr, + .tp_call = JSMethodProxyMethodDefinitions::JSMethodProxy_call, + // .tp_getattro = (getattrofunc)JSMethodProxyMethodDefinitions::JSMethodProxy_get, + // .tp_setattro = (setattrofunc)JSMethodProxyMethodDefinitions::JSMethodProxy_assign, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = PyDoc_STR("Javascript Method proxy object"), + // .tp_iter = (getiterfunc)JSMethodProxyMethodDefinitions::JSMethodProxy_iter, + .tp_new = JSMethodProxyMethodDefinitions::JSMethodProxy_new, +}; + static void cleanup() { delete autoRealm; delete global; @@ -391,6 +422,10 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; if (PyType_Ready(&JSObjectProxyType) < 0) return NULL; + if (PyType_Ready(&JSFunctionProxyType) < 0) + return NULL; + if (PyType_Ready(&JSMethodProxyType) < 0) + return NULL; pyModule = PyModule_Create(&pythonmonkey); if (pyModule == NULL) @@ -416,7 +451,21 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } - if (PyModule_AddObject(pyModule, "SpiderMonkeyError", SpiderMonkeyError)) { + Py_INCREF(&JSFunctionProxyType); + if (PyModule_AddObject(pyModule, "JSFunctionProxy", (PyObject *)&JSFunctionProxyType) < 0) { + Py_DECREF(&JSFunctionProxyType); + Py_DECREF(pyModule); + return NULL; + } + + Py_INCREF(&JSMethodProxyType); + if (PyModule_AddObject(pyModule, "JSMethodProxy", (PyObject *)&JSMethodProxyType) < 0) { + Py_DECREF(&JSMethodProxyType); + Py_DECREF(pyModule); + return NULL; + } + + if (PyModule_AddObject(pyModule, "SpiderMonkeyError", SpiderMonkeyError) < 0) { Py_DECREF(pyModule); return NULL; } diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index c8c810a3..fd07793c 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -38,9 +38,6 @@ #include -// TODO (Caleb Aikens) get below properties -static PyMethodDef callJSFuncDef = {"JSFunctionCallable", callJSFunc, METH_VARARGS, NULL}; - PyType *pyTypeFactory(PyObject *object) { PyType *pyType; @@ -132,16 +129,15 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted } case js::ESClass::Function: { PyObject *pyFunc; + FuncType *f; if (JS_IsNativeFunction(obj, callPyFunc)) { // It's a wrapped python function by us // Get the underlying python function from the 0th reserved slot JS::Value pyFuncVal = js::GetFunctionNativeReserved(obj, 0); pyFunc = (PyObject *)(pyFuncVal.toPrivate()); + f = new FuncType(pyFunc); } else { - // FIXME (Tom Tang): `jsCxThisFuncTuple` and the tuple items are not going to be GCed - PyObject *jsCxThisFuncTuple = PyTuple_Pack(3, PyLong_FromVoidPtr(cx), PyLong_FromVoidPtr(thisObj), PyLong_FromVoidPtr(rval)); - pyFunc = PyCFunction_New(&callJSFuncDef, jsCxThisFuncTuple); + f = new FuncType(cx, *rval); } - FuncType *f = new FuncType(pyFunc); memoizePyTypeAndGCThing(f, *rval); // TODO (Caleb Aikens) consider putting this in the FuncType constructor return f; } @@ -191,29 +187,4 @@ PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::Ro return new NullType(); } return v; -} - -PyObject *callJSFunc(PyObject *jsCxThisFuncTuple, PyObject *args) { - // TODO (Caleb Aikens) convert PyObject *args to JS::Rooted JSargs - JSContext *cx = (JSContext *)PyLong_AsVoidPtr(PyTuple_GetItem(jsCxThisFuncTuple, 0)); - JS::RootedObject *thisObj = (JS::RootedObject *)PyLong_AsVoidPtr(PyTuple_GetItem(jsCxThisFuncTuple, 1)); - JS::RootedValue *jsFunc = (JS::RootedValue *)PyLong_AsVoidPtr(PyTuple_GetItem(jsCxThisFuncTuple, 2)); - - JS::RootedVector jsArgsVector(cx); - for (size_t i = 0; i < PyTuple_Size(args); i++) { - JS::Value jsValue = jsTypeFactory(cx, PyTuple_GetItem(args, i)); - if (PyErr_Occurred()) { // Check if an exception has already been set in the flow of control - return NULL; // Fail-fast - } - jsArgsVector.append(jsValue); - } - - JS::HandleValueArray jsArgs(jsArgsVector); - JS::Rooted *jsReturnVal = new JS::Rooted(cx); - if (!JS_CallFunctionValue(cx, *thisObj, *jsFunc, jsArgs, jsReturnVal)) { - setSpiderMonkeyException(cx); - return NULL; - } - - return pyTypeFactory(cx, thisObj, jsReturnVal)->getPyObject(); } \ No newline at end of file From 654ddd935251346235e37b6b17e4402f385d586d Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 17 Jan 2024 15:14:13 -0500 Subject: [PATCH 004/170] feat(JSStringProxy): implement JSStringProxy, as well as replace memoizePyTypeAndGCThing with a FinalizationRegistry --- README.md | 4 +- include/JSFunctionProxy.hh | 7 ++ include/JSMethodProxy.hh | 7 ++ include/JSStringProxy.hh | 33 ++++++ include/PyProxyHandler.hh | 2 + include/modules/pythonmonkey/pythonmonkey.hh | 22 +--- src/JSFunctionProxy.cc | 6 + src/JSMethodProxy.cc | 6 + src/PyProxyHandler.cc | 14 ++- src/StrType.cc | 5 +- src/jsTypeFactory.cc | 35 ++++-- src/modules/pythonmonkey/pythonmonkey.cc | 113 ++++++++---------- src/pyTypeFactory.cc | 3 - tests/python/test_event_loop.py | 2 +- tests/python/test_functions_this.py | 117 +++++++++++++++++++ 15 files changed, 274 insertions(+), 102 deletions(-) create mode 100644 include/JSStringProxy.hh create mode 100644 tests/python/test_functions_this.py diff --git a/README.md b/README.md index 348cceb6..a46b9194 100644 --- a/README.md +++ b/README.md @@ -241,11 +241,11 @@ that if you update an object in JavaScript, the corresponding Dict in Python wil | JavaScript Type | Python Type | |:---------------------|:----------------| -| string | String +| string | pythonmonkey.JSStringProxy (String) | number | Float | bigint | pythonmonkey.bigint (Integer) | boolean | Bool -| function | Function +| function | pythonmonkey.JSFunctionProxy || pythonmonkey.JSMethodProxy (Function || Method) | object - most | pythonmonkey.JSObjectProxy (Dict) | object - Date | datetime | object - Array | List diff --git a/include/JSFunctionProxy.hh b/include/JSFunctionProxy.hh index 732ddad2..a73ae6ea 100644 --- a/include/JSFunctionProxy.hh +++ b/include/JSFunctionProxy.hh @@ -30,6 +30,13 @@ typedef struct { */ struct JSFunctionProxyMethodDefinitions { public: +/** + * @brief Deallocation method (.tp_dealloc), removes the reference to the underlying JSFunction before freeing the JSFunctionProxy + * + * @param self - The JSFunctionProxy to be free'd + */ + static void JSFunctionProxy_dealloc(JSFunctionProxy *self); + /** * @brief New method (.tp_new), creates a new instance of the JSFunctionProxy type, exposed as the __new()__ method in python * diff --git a/include/JSMethodProxy.hh b/include/JSMethodProxy.hh index a364027e..f08bd81b 100644 --- a/include/JSMethodProxy.hh +++ b/include/JSMethodProxy.hh @@ -33,6 +33,13 @@ typedef struct { */ struct JSMethodProxyMethodDefinitions { public: +/** + * @brief Deallocation method (.tp_dealloc), removes the reference to the underlying JSFunction before freeing the JSMethodProxy + * + * @param self - The JSMethodProxy to be free'd + */ + static void JSMethodProxy_dealloc(JSMethodProxy *self); + /** * @brief New method (.tp_new), creates a new instance of the JSMethodProxy type, exposed as the __new()__ method in python * diff --git a/include/JSStringProxy.hh b/include/JSStringProxy.hh new file mode 100644 index 00000000..135eac6f --- /dev/null +++ b/include/JSStringProxy.hh @@ -0,0 +1,33 @@ +/** + * @file JSStringProxy.hh + * @author Caleb Aikens (caleb@distributive.network) + * @brief JSStringProxy is a custom C-implemented python type that derives from str. It acts as a proxy for JSStrings from Spidermonkey, and behaves like a str would. + * @version 0.1 + * @date 2024-01-03 + * + * Copyright (c) 2024 Distributive Corp. + * + */ + +#ifndef PythonMonkey_JSStringProxy_ +#define PythonMonkey_JSStringProxy_ + +#include + +#include + +/** + * @brief The typedef for the backing store that will be used by JSStringProxy objects. All it contains is a pointer to the JSString + * + */ +typedef struct { + PyUnicodeObject str; + JS::PersistentRootedValue jsString; +} JSStringProxy; + +/** + * @brief Struct for the JSStringProxyType, used by all JSStringProxy objects + */ +extern PyTypeObject JSStringProxyType; + +#endif \ No newline at end of file diff --git a/include/PyProxyHandler.hh b/include/PyProxyHandler.hh index f3480224..19976f41 100644 --- a/include/PyProxyHandler.hh +++ b/include/PyProxyHandler.hh @@ -293,6 +293,8 @@ public: PyListProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; static const char family; + void finalize(JS::GCContext *gcx, JSObject *proxy) const override; + bool getOwnPropertyDescriptor( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle> desc diff --git a/include/modules/pythonmonkey/pythonmonkey.hh b/include/modules/pythonmonkey/pythonmonkey.hh index 22e08e5d..7f964bce 100644 --- a/include/modules/pythonmonkey/pythonmonkey.hh +++ b/include/modules/pythonmonkey/pythonmonkey.hh @@ -24,6 +24,7 @@ #define PythonMonkey_BigInt PyObject_GetAttrString(PyState_FindModule(&pythonmonkey), "bigint") /**< macro for pythonmonkey.bigint class object */ extern JSContext *GLOBAL_CX; /**< pointer to PythonMonkey's JSContext */ +extern JS::PersistentRootedObject *jsFunctionRegistry; /** *global; /**< pointer to the global object of PythonMonkey's JSContext */ static JSAutoRealm *autoRealm; /**< pointer to PythonMonkey's AutoRealm */ static JobQueue *JOB_QUEUE; /**< pointer to PythonMonkey's event-loop job queue */ @@ -34,27 +35,6 @@ static JobQueue *JOB_QUEUE; /**< pointer to PythonMonkey's event-loop job queue */ static void cleanup(); -/** - * @brief This function is used to memoize PyTypes and GCThings that use the same backing store for their data, - * so that the JS garbage collector doesn't collect memory still in use by Python. It does this by storing the - * pointers in an unordered_map, with the key being the PyType pointer, and the value being a vector of GCThing - * pointers. - * - * @param pyType - Pointer to the PyType to be memoized - * @param GCThing - Pointer to the GCThing to be memoized - */ -void memoizePyTypeAndGCThing(PyType *pyType, JS::Handle GCThing); - -/** - * @brief Callback function passed to JS_SetGCCallback to handle PythonMonkey shared memory - * - * @param cx - Pointer to the JS Context (not used) - * @param status - enum specifying whether the Callback triggered at the beginning or end of the GC Cycle - * @param reason - reason for the GC Cycle - * @param data - - */ -void handleSharedPythonMonkeyMemory(JSContext *cx, JSGCStatus status, JS::GCReason reason, void *data); - /** * @brief Function exposed by the python module that calls the spidermonkey garbage collector * diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index 7ed8c061..ce2cb93e 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -20,6 +20,12 @@ #include +void JSFunctionProxyMethodDefinitions::JSFunctionProxy_dealloc(JSFunctionProxy *self) +{ + delete self->jsFunc; + return; +} + PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { JSFunctionProxy *self = (JSFunctionProxy *)subtype->tp_alloc(subtype, 0); if (self) { diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index 939f0451..f20041dc 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -20,6 +20,12 @@ #include +void JSMethodProxyMethodDefinitions::JSMethodProxy_dealloc(JSMethodProxy *self) +{ + delete self->jsFunc; + return; +} + PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { JSFunctionProxy *jsFunctionProxy; PyObject *im_self; diff --git a/src/PyProxyHandler.cc b/src/PyProxyHandler.cc index d1f7cbad..e20a5339 100644 --- a/src/PyProxyHandler.cc +++ b/src/PyProxyHandler.cc @@ -144,8 +144,9 @@ bool PyDictProxyHandler::getOwnEnumerablePropertyKeys( return this->ownPropertyKeys(cx, proxy, props); } -// @TODO (Caleb Aikens) implement this -void PyDictProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const {} +void PyDictProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { + Py_DECREF(pyObject); +} bool PyDictProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, @@ -210,6 +211,10 @@ bool PyListProxyHandler::getOwnPropertyDescriptor( return true; } +void PyListProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { + Py_DECREF(pyObject); +} + bool PyListProxyHandler::defineProperty( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, JS::ObjectOpResult &result @@ -374,8 +379,9 @@ bool PyObjectProxyHandler::getOwnEnumerablePropertyKeys( return this->ownPropertyKeys(cx, proxy, props); } -// @TODO (Caleb Aikens) implement this -void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const {} +void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { + Py_DECREF(pyObject); +} bool PyObjectProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, diff --git a/src/StrType.cc b/src/StrType.cc index 44cf0491..dbb43ae4 100644 --- a/src/StrType.cc +++ b/src/StrType.cc @@ -1,6 +1,7 @@ #include "include/StrType.hh" #include "include/PyType.hh" +#include "include/JSStringProxy.hh" #include @@ -56,9 +57,11 @@ StrType::StrType(JSContext *cx, JSString *str) { size_t length = JS::GetLinearStringLength(lstr); - pyObject = (PyObject *)PyObject_New(PyUnicodeObject, &PyUnicode_Type); // new reference + pyObject = (PyObject *)PyObject_New(JSStringProxy, &JSStringProxyType); // new reference Py_INCREF(pyObject); // XXX: Why? + ((JSStringProxy *)pyObject)->jsString.setString((JSString *)lstr); + // Initialize as legacy string (https://github.com/python/cpython/blob/v3.12.0b1/Include/cpython/unicodeobject.h#L78-L93) // see https://github.com/python/cpython/blob/v3.11.3/Objects/unicodeobject.c#L1230-L1245 PY_UNICODE_OBJECT_HASH(pyObject) = -1; diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index f5d50052..63f3c971 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -39,8 +39,18 @@ #define LOW_SURROGATE_END 0xDFFF #define BMP_END 0x10000 +#include +#include +#include + +#include + +std::unordered_map charToPyObjectMap; // a map of char16_t buffers to their corresponding PyObjects, used when finalizing JSExternalStrings + struct PythonExternalString : public JSExternalStringCallbacks { - void finalize(char16_t *chars) const override {} + void finalize(char16_t *chars) const override { + Py_DECREF(charToPyObjectMap[chars]); + } size_t sizeOfBuffer(const char16_t *chars, mozilla::MallocSizeOf mallocSizeOf) const override { return 0; } @@ -103,12 +113,13 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { break; } case (PyUnicode_2BYTE_KIND): { + charToPyObjectMap[(char16_t *)PyUnicode_2BYTE_DATA(object)] = object; JSString *str = JS_NewExternalString(cx, (char16_t *)PyUnicode_2BYTE_DATA(object), PyUnicode_GET_LENGTH(object), &PythonExternalStringCallbacks); returnType.setString(str); break; } case (PyUnicode_1BYTE_KIND): { - + charToPyObjectMap[(char16_t *)PyUnicode_2BYTE_DATA(object)] = object; JSString *str = JS_NewExternalString(cx, (char16_t *)PyUnicode_1BYTE_DATA(object), PyUnicode_GET_LENGTH(object), &PythonExternalStringCallbacks); /* TODO (Caleb Aikens): this is a hack to set the JSString::LATIN1_CHARS_BIT, because there isnt an API for latin1 JSExternalStrings. * Ideally we submit a patch to Spidermonkey to make this part of their API with the following signature: @@ -122,7 +133,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { break; } } - memoizePyTypeAndGCThing(new StrType(object), returnType); + Py_INCREF(object); } else if (PyMethod_Check(object) || PyFunction_Check(object) || PyCFunction_Check(object)) { // can't determine number of arguments for PyCFunctions, so just assume potentially unbounded @@ -135,12 +146,17 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { JSFunction *jsFunc = js::NewFunctionWithReserved(cx, callPyFunc, nargs, 0, NULL); JS::RootedObject jsFuncObject(cx, JS_GetFunctionObject(jsFunc)); - // We put the address of the PyObject in the JSFunction's 0th private slot so we can access it later js::SetFunctionNativeReserved(jsFuncObject, 0, JS::PrivateValue((void *)object)); returnType.setObject(*jsFuncObject); - memoizePyTypeAndGCThing(new FuncType(object), returnType); Py_INCREF(object); // otherwise the python function object would be double-freed on GC in Python 3.11+ + + // add function to jsFunctionRegistry, to DECREF the PyObject when the JSFunction is finalized + JS::RootedValueArray<2> registerArgs(GLOBAL_CX); + registerArgs[0].setObject(*jsFuncObject); + registerArgs[1].setPrivate(object); + JS::RootedValue ignoredOutVal(GLOBAL_CX); + JS_CallFunctionName(GLOBAL_CX, *jsFunctionRegistry, "register", registerArgs, &ignoredOutVal); } else if (PyExceptionInstance_Check(object)) { JSObject *error = ExceptionType(object).toJsError(cx); @@ -154,7 +170,6 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { BufferType *pmBuffer = new BufferType(object); JSObject *typedArray = pmBuffer->toJsTypedArray(cx); // may return null returnType.setObjectOrNull(typedArray); - memoizePyTypeAndGCThing(pmBuffer, returnType); } else if (PyObject_TypeCheck(object, &JSObjectProxyType)) { returnType.setObject(*((JSObjectProxy *)object)->jsObject); @@ -168,6 +183,12 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { JS::Rooted boundFunction(cx); JS_CallFunctionName(cx, func, "bind", args, &boundFunction); returnType.set(boundFunction); + // add function to jsFunctionRegistry, to DECREF the PyObject when the JSFunction is finalized + JS::RootedValueArray<2> registerArgs(GLOBAL_CX); + registerArgs[0].set(boundFunction); + registerArgs[1].setPrivate(object); + JS::RootedValue ignoredOutVal(GLOBAL_CX); + JS_CallFunctionName(GLOBAL_CX, *jsFunctionRegistry, "register", registerArgs, &ignoredOutVal); } else if (PyObject_TypeCheck(object, &JSFunctionProxyType)) { returnType.setObject(**((JSFunctionProxy *)object)->jsFunc); @@ -192,8 +213,6 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { PromiseType *p = new PromiseType(object); JSObject *promise = p->toJsPromise(cx); // may return null returnType.setObjectOrNull(promise); - // nested awaitables would have already been GCed if finished - // memoizePyTypeAndGCThing(p, returnType); } else { JS::RootedValue v(cx); diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 5b03fc5f..3569451f 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -19,6 +19,7 @@ #include "include/JSFunctionProxy.hh" #include "include/JSMethodProxy.hh" #include "include/JSObjectProxy.hh" +#include "include/JSStringProxy.hh" #include "include/PyType.hh" #include "include/pyTypeFactory.hh" #include "include/StrType.hh" @@ -42,18 +43,20 @@ #include #include -#include -#include - JSContext *GLOBAL_CX; -typedef std::unordered_map *>>::iterator PyToGCIterator; +JS::PersistentRootedObject *jsFunctionRegistry; + +bool functionRegistryCallback(JSContext *cx, unsigned int argc, JS::Value *vp) { + JS::CallArgs callargs = JS::CallArgsFromVp(argc, vp); + Py_DECREF((PyObject *)callargs[0].toPrivate()); + return true; +} + typedef struct { PyObject_HEAD } NullObject; -std::unordered_map *>> PyTypeToGCThing; /**< data structure to hold memoized PyObject & GCThing data for handling GC*/ - static PyTypeObject NullType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "pythonmonkey.null", @@ -90,18 +93,24 @@ PyTypeObject JSObjectProxyType = { .tp_new = JSObjectProxyMethodDefinitions::JSObjectProxy_new, }; +PyTypeObject JSStringProxyType = { + .tp_name = "pythonmonkey.JSStringProxy", + .tp_basicsize = sizeof(JSStringProxy), + .tp_flags = Py_TPFLAGS_DEFAULT + | Py_TPFLAGS_UNICODE_SUBCLASS // https://docs.python.org/3/c-api/typeobj.html#Py_TPFLAGS_LONG_SUBCLASS + | Py_TPFLAGS_BASETYPE, // can be subclassed + .tp_doc = PyDoc_STR("Javascript String value"), + .tp_base = &PyUnicode_Type, // extending the builtin int type +}; + PyTypeObject JSFunctionProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "pythonmonkey.JSFunctionProxy", .tp_basicsize = sizeof(JSFunctionProxy), - // .tp_dealloc = (destructor)JSFunctionProxyMethodDefinitions::JSFunctionProxy_dealloc, - // .tp_repr = (reprfunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_repr, + .tp_dealloc = (destructor)JSFunctionProxyMethodDefinitions::JSFunctionProxy_dealloc, .tp_call = JSFunctionProxyMethodDefinitions::JSFunctionProxy_call, - // .tp_getattro = (getattrofunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_get, - // .tp_setattro = (setattrofunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_assign, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Javascript Function proxy object"), - // .tp_iter = (getiterfunc)JSFunctionProxyMethodDefinitions::JSFunctionProxy_iter, .tp_new = JSFunctionProxyMethodDefinitions::JSFunctionProxy_new, }; @@ -109,18 +118,15 @@ PyTypeObject JSMethodProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "pythonmonkey.JSMethodProxy", .tp_basicsize = sizeof(JSMethodProxy), - // .tp_dealloc = (destructor)JSMethodProxyMethodDefinitions::JSMethodProxy_dealloc, - // .tp_repr = (reprfunc)JSMethodProxyMethodDefinitions::JSMethodProxy_repr, + .tp_dealloc = (destructor)JSMethodProxyMethodDefinitions::JSMethodProxy_dealloc, .tp_call = JSMethodProxyMethodDefinitions::JSMethodProxy_call, - // .tp_getattro = (getattrofunc)JSMethodProxyMethodDefinitions::JSMethodProxy_get, - // .tp_setattro = (setattrofunc)JSMethodProxyMethodDefinitions::JSMethodProxy_assign, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Javascript Method proxy object"), - // .tp_iter = (getiterfunc)JSMethodProxyMethodDefinitions::JSMethodProxy_iter, .tp_new = JSMethodProxyMethodDefinitions::JSMethodProxy_new, }; static void cleanup() { + delete jsFunctionRegistry; delete autoRealm; delete global; delete JOB_QUEUE; @@ -128,50 +134,6 @@ static void cleanup() { JS_ShutDown(); } -void memoizePyTypeAndGCThing(PyType *pyType, JS::Handle GCThing) { - JS::PersistentRooted *RootedGCThing = new JS::PersistentRooted(GLOBAL_CX, GCThing); - PyToGCIterator pyIt = PyTypeToGCThing.find(pyType); - - if (pyIt == PyTypeToGCThing.end()) { // if the PythonObject is not memoized - std::vector *> gcVector( - {{RootedGCThing}}); - PyTypeToGCThing.insert({{pyType, gcVector}}); - } - else { - pyIt->second.push_back(RootedGCThing); - } -} - -void handleSharedPythonMonkeyMemory(JSContext *cx, JSGCStatus status, JS::GCReason reason, void *data) { - if (status == JSGCStatus::JSGC_BEGIN) { - PyToGCIterator pyIt = PyTypeToGCThing.begin(); - while (pyIt != PyTypeToGCThing.end()) { - PyObject *pyObj = pyIt->first->getPyObject(); - // If the PyObject reference count is exactly 1, then the only reference to the object is the one - // we are holding, which means the object is ready to be free'd. - if (_PyGC_FINALIZED(pyObj) || pyObj->ob_refcnt == 1) { // PyObject_GC_IsFinalized is only available in Python 3.9+ - for (JS::PersistentRooted *rval: pyIt->second) { // for each related GCThing - bool found = false; - for (PyToGCIterator innerPyIt = PyTypeToGCThing.begin(); innerPyIt != PyTypeToGCThing.end(); innerPyIt++) { // for each other PyType pointer - if (innerPyIt != pyIt && std::find(innerPyIt->second.begin(), innerPyIt->second.end(), rval) != innerPyIt->second.end()) { // if the PyType is also related to the GCThing - found = true; - break; - } - } - // if this PyObject is the last PyObject that references this GCThing, then the GCThing can also be free'd - if (!found) { - delete rval; - } - } - pyIt = PyTypeToGCThing.erase(pyIt); - } - else { - pyIt++; - } - } - } -}; - static PyObject *collect(PyObject *self, PyObject *args) { JS_GC(GLOBAL_CX); Py_RETURN_NONE; @@ -387,7 +349,10 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } - JS::RealmOptions options; + JS::RealmCreationOptions creationOptions = JS::RealmCreationOptions(); + JS::RealmBehaviors behaviours = JS::RealmBehaviors(); + creationOptions.setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithCleanupSome); // enable FinalizationRegistry + JS::RealmOptions options = JS::RealmOptions(creationOptions, behaviours); static JSClass globalClass = {"global", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps}; global = new JS::RootedObject(GLOBAL_CX, JS_NewGlobalObject(GLOBAL_CX, &globalClass, nullptr, JS::FireOnNewGlobalHook, options)); if (!global) { @@ -404,7 +369,6 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) autoRealm = new JSAutoRealm(GLOBAL_CX, *global); - JS_SetGCCallback(GLOBAL_CX, handleSharedPythonMonkeyMemory, NULL); JS_DefineProperty(GLOBAL_CX, *global, "debuggerGlobal", debuggerGlobal, JSPROP_READONLY); // XXX: SpiderMonkey bug??? @@ -422,6 +386,8 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; if (PyType_Ready(&JSObjectProxyType) < 0) return NULL; + if (PyType_Ready(&JSStringProxyType) < 0) + return NULL; if (PyType_Ready(&JSFunctionProxyType) < 0) return NULL; if (PyType_Ready(&JSMethodProxyType) < 0) @@ -451,6 +417,13 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } + Py_INCREF(&JSStringProxyType); + if (PyModule_AddObject(pyModule, "JSStringProxy", (PyObject *)&JSStringProxyType) < 0) { + Py_DECREF(&JSStringProxyType); + Py_DECREF(pyModule); + return NULL; + } + Py_INCREF(&JSFunctionProxyType); if (PyModule_AddObject(pyModule, "JSFunctionProxy", (PyObject *)&JSFunctionProxyType) < 0) { Py_DECREF(&JSFunctionProxyType); @@ -480,5 +453,21 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } + // initialize FinalizationRegistry of JSFunctions to Python Functions + JS::RootedValue FinalizationRegistry(GLOBAL_CX); + JS::RootedObject registryObject(GLOBAL_CX); + + JS_GetProperty(GLOBAL_CX, *global, "FinalizationRegistry", &FinalizationRegistry); + JS::Rooted> args(GLOBAL_CX); + JSFunction *registryCallback = JS_NewFunction(GLOBAL_CX, functionRegistryCallback, 1, 0, NULL); + JS::RootedObject registryCallbackObject(GLOBAL_CX, JS_GetFunctionObject(registryCallback)); + args[0].setObject(*registryCallbackObject); + if (!JS::Construct(GLOBAL_CX, FinalizationRegistry, args, ®istryObject)) { + setSpiderMonkeyException(GLOBAL_CX); + return NULL; + } + jsFunctionRegistry = new JS::PersistentRootedObject(GLOBAL_CX); + jsFunctionRegistry->set(registryObject); + return pyModule; } diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index fd07793c..78232474 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -81,7 +81,6 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted } else if (rval->isString()) { StrType *s = new StrType(cx, rval->toString()); - memoizePyTypeAndGCThing(s, *rval); // TODO (Caleb Aikens) consider putting this in the StrType constructor return s; } else if (rval->isSymbol()) { @@ -138,7 +137,6 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted } else { f = new FuncType(cx, *rval); } - memoizePyTypeAndGCThing(f, *rval); // TODO (Caleb Aikens) consider putting this in the FuncType constructor return f; } case js::ESClass::Number: { @@ -155,7 +153,6 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted JS::RootedValue unboxed(cx); js::Unbox(cx, obj, &unboxed); StrType *s = new StrType(cx, unboxed.toString()); - memoizePyTypeAndGCThing(s, *rval); // TODO (Caleb Aikens) consider putting this in the StrType constructor return s; } default: { diff --git a/tests/python/test_event_loop.py b/tests/python/test_event_loop.py index 1377f400..a5d3cc83 100644 --- a/tests/python/test_event_loop.py +++ b/tests/python/test_event_loop.py @@ -167,7 +167,7 @@ async def c(): assert "nested" == await pm.eval("(promise) => promise")(c()) assert "nested" == await pm.eval("(promise) => promise")(await c()) assert "nested" == await pm.eval("(promise) => promise")(await (await c())) - with pytest.raises(TypeError, match="object str can't be used in 'await' expression"): + with pytest.raises(TypeError, match="object pythonmonkey.JSStringProxy can't be used in 'await' expression"): await pm.eval("(promise) => promise")(await (await (await c()))) # Python awaitable throwing exceptions diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py new file mode 100644 index 00000000..4018470f --- /dev/null +++ b/tests/python/test_functions_this.py @@ -0,0 +1,117 @@ +import pythonmonkey as pm + +def test_python_functions_self(): + def pyFunc(param): + return param + + assert 1 == pyFunc(1) + assert 2 == pm.eval("""(pyFunc) => { + return pyFunc(2); + } + """)(pyFunc) + assert 3 == pm.eval("""(pyFunc) => { + let jsObj = {}; + jsObj.pyFunc = pyFunc; + return pyFunc(3); + } + """)(pyFunc) + +def test_python_methods_self(): + def pyFunc(self, param): + return [self, param] + + class Class: + pass + Class.pyMethod = pyFunc + + pyObj = Class() + result = pyObj.pyMethod(1) + assert pyObj == result[0] and 1 == result[1] + pyMethod = pyObj.pyMethod + result = pyMethod(2) + assert pyObj == result[0] and 2 == result[1] + result = pm.eval("""(pyObj) => { + return pyObj.pyMethod(3); + } + """)(pyObj) + assert pyObj == result[0] and 3 == result[1] + result = pm.eval("""(pyObj) => { + let jsObj = {}; + jsObj.pyMethod = pyObj.pyMethod; + return jsObj.pyMethod(4); + } + """)(pyObj) + assert pyObj == result[0] and 4 == result[1] #pyMethod is bound to pyObj, so `self` is not `jsObj` + result = pm.eval("""(pyObj) => { + let pyMethod = pyObj.pyMethod; + return pyMethod(5); + } + """)(pyObj) + assert pyObj == result[0] and 5 == result[1] #pyMethod is bound to pyObj, so `self` is not `globalThis` + +def test_javascript_functions_this(): + jsFunc = pm.eval(""" + (function(param) { + return [this, param]; + }) + """) + + class Class: + pass + Class.jsFunc = jsFunc # jsFunc is not bound to Class, so `this` will be `globalThis`, not the object + + pyObj = Class() + jsObj = pm.eval("({})") + jsObj.jsFunc = jsFunc + globalThis = pm.eval("globalThis") + result = jsFunc(1) + assert globalThis == result[0] and 1 == result[1] + result = pyObj.jsFunc(2) + assert globalThis == result[0] and 2 == result[1] + result = jsObj.jsFunc(3) + assert globalThis == result[0] and 3 == result[1] # TODO (Caleb Aikens) should `this` be `globalThis` or `jsObj` here? + result = pm.eval("""(jsFunc) => { + return jsFunc(4); + } + """)(jsFunc) + assert globalThis == result[0] and 4 == result[1] + result = pm.eval("""(pyObj) => { + return pyObj.jsFunc(5); + } + """)(pyObj) + assert pyObj == result[0] and 5 == result[1] + result = pm.eval("""(jsObj) => { + return jsObj.jsFunc(6); + } + """)(jsObj) + assert jsObj == result[0] and 6 == result[1] + +def test_JSMethodProxy_this(): + jsFunc = pm.eval(""" + (function(param) { + return [this, param]; + }) + """) + + class Class: + pass + + pyObj = Class() + pyObj.jsMethod = pm.JSMethodProxy(jsFunc, pyObj) # jsMethod is bound to pyObj, so `this` will always be `pyObj` + jsObj = pm.eval("({})") + jsObj.jsMethod = pyObj.jsMethod + globalThis = pm.eval("globalThis") + result = pyObj.jsMethod(1) + assert pyObj == result[0] and 1 == result[1] + result = jsObj.jsMethod(2) + assert pyObj == result[0] and 2 == result[1] + result = pm.eval("""(pyObj) => { + return pyObj.jsMethod(3); + } + """)(pyObj) + assert pyObj == result[0] and 3 == result[1] + result = pm.eval("""(jsObj) => { + return jsObj.jsMethod(4); + } + """)(jsObj) + assert pyObj == result[0] and 4 == result[1] #TODO (Caleb Aikens) should `this` be `pyObj` or `jsObj` here? \ No newline at end of file From f439f9627a2189add2d0d83fe7eb90ec74741f48 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 31 Jan 2024 08:56:17 -0500 Subject: [PATCH 005/170] fix JSArrayProxy and PyListProxy sort with proxied function arguments --- include/PyObjectProxyHandler.hh | 4 +- src/JSArrayProxy.cc | 64 ++++++------- src/PyListProxyHandler.cc | 115 +++++------------------ src/PyObjectProxyHandler.cc | 6 +- src/jsTypeFactory.cc | 4 + src/modules/pythonmonkey/pythonmonkey.cc | 4 +- tests/python/test_arrays.py | 12 +-- 7 files changed, 69 insertions(+), 140 deletions(-) diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index eb25082e..8b3ce72b 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -4,9 +4,9 @@ * @brief Structs for creating JS proxy objects. Used for default object coercion * @version 0.1 * @date 2024-01-25 - * + * * Copyright (c) 2023 Distributive Corp. - * + * */ #ifndef PythonMonkey_PyObjectProxy_ diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 2a224553..ea8bf8f8 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -18,6 +18,7 @@ #include "include/jsTypeFactory.hh" #include "include/pyTypeFactory.hh" #include "include/PyBaseProxyHandler.hh" +#include "include/JSFunctionProxy.hh" #include #include @@ -1291,48 +1292,41 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_sort(JSArrayProxy *self, P } } } - else if (PyCFunction_Check(keyfunc)) { - // check if builtin 1-arg python or js 2-arg compare - int flags = PyCFunction_GetFlags((PyObject *)keyfunc); + else if (PyObject_TypeCheck(keyfunc, &JSFunctionProxyType)) { + JS::Rooted> jArgs(GLOBAL_CX); + jArgs[0].setObject(**((JSFunctionProxy *)keyfunc)->jsFunc); + if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); + return NULL; + } - if (flags & METH_VARARGS && !(flags & METH_KEYWORDS)) { - // we got a JS compare function, use it as-is - JS::Rooted> jArgs(GLOBAL_CX); - jArgs[0].set(jsTypeFactory(GLOBAL_CX, keyfunc)); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { - PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); - return NULL; - } + if (reverse) { + JSArrayProxy_reverse(self); + } + } + else if (PyCFunction_Check(keyfunc)) { + JS::RootedObject funObj(GLOBAL_CX, JS_GetFunctionObject(JS_NewFunction(GLOBAL_CX, sort_compare_key_func, 2, 0, NULL))); - if (reverse) { - JSArrayProxy_reverse(self); - } + JS::RootedValue privateValue(GLOBAL_CX, JS::PrivateValue(keyfunc)); + if (!JS_SetProperty(GLOBAL_CX, funObj, "_key_func_param", privateValue)) { // JS::SetReservedSlot(functionObj, KeyFuncSlot, JS::PrivateValue(keyfunc)); does not work + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); + return NULL; } - else { - // we got a built-in python function - JS::RootedObject funObj(GLOBAL_CX, JS_GetFunctionObject(JS_NewFunction(GLOBAL_CX, sort_compare_key_func, 2, 0, NULL))); - JS::RootedValue privateValue(GLOBAL_CX, JS::PrivateValue(keyfunc)); - if (!JS_SetProperty(GLOBAL_CX, funObj, "_key_func_param", privateValue)) { // JS::SetReservedSlot(functionObj, KeyFuncSlot, JS::PrivateValue(keyfunc)); does not work - PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); - return NULL; - } + JS::RootedValue reverseValue(GLOBAL_CX); + reverseValue.setBoolean(reverse); + if (!JS_SetProperty(GLOBAL_CX, funObj, "_reverse_param", reverseValue)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); + return NULL; + } - JS::RootedValue reverseValue(GLOBAL_CX); - reverseValue.setBoolean(reverse); - if (!JS_SetProperty(GLOBAL_CX, funObj, "_reverse_param", reverseValue)) { + JS::Rooted> jArgs(GLOBAL_CX); + jArgs[0].setObject(*funObj); + if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + if (!PyErr_Occurred()) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); - return NULL; - } - - JS::Rooted> jArgs(GLOBAL_CX); - jArgs[0].setObject(*funObj); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { - if (!PyErr_Occurred()) { - PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); - } - return NULL; } + return NULL; } } else { diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 77a67dff..72535993 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -13,6 +13,7 @@ #include "include/jsTypeFactory.hh" #include "include/JSArrayProxy.hh" +#include "include/JSFunctionProxy.hh" #include "include/pyTypeFactory.hh" #include "include/StrType.hh" @@ -1581,11 +1582,20 @@ static int invokeCallBack(PyObject *list, int index, JS::HandleValue leftValue, throw "JS_CallFunction failed"; } + if (!retVal.isNumber()) { + PyErr_Format(PyExc_TypeError, "incorrect compare function return type"); + return 0; + } + return retVal.toInt32(); } // Adapted from Kernigan&Ritchie's C book static void quickSort(PyObject *list, int left, int right, JSContext *cx, JS::HandleFunction callBack) { + if (PyErr_Occurred()) { + return; + } + if (left >= right) { // base case return; @@ -1597,7 +1607,11 @@ static void quickSort(PyObject *list, int left, int right, JSContext *cx, JS::Ha int last = left; for (int index = left + 1; index <= right; index++) { - if (invokeCallBack(list, index, leftValue, cx, callBack) < 0) { + int result = invokeCallBack(list, index, leftValue, cx, callBack); + if (PyErr_Occurred()) { + return; + } + if (result < 0) { swapItems(list, ++last, index); } } @@ -1658,42 +1672,6 @@ static bool js_sort_compare_default(JSContext *cx, unsigned argc, JS::Value *vp) return true; } -// private -static bool js_sort_compare_key_func(JSContext *cx, unsigned argc, JS::Value *vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - - JS::RootedObject callee(cx, &args.callee()); - - JS::RootedValue keyFunc(cx); - if (!JS_GetProperty(cx, callee, "_key_func_param", &keyFunc)) { - PyErr_Format(PyExc_SystemError, "JSAPI call failed"); - return false; - } - PyObject *keyfunc = (PyObject *)keyFunc.toPrivate(); - - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); - - JS::RootedValue *elementVal = new JS::RootedValue(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, elementVal)->getPyObject(); - - elementVal = new JS::RootedValue(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, elementVal)->getPyObject(); - - PyObject *result = PyObject_CallFunction(keyfunc, "OO", args_0, args_1); - if (!result) { - return false; - } - - if (PyLong_Check(result)) { - args.rval().setInt32((int32_t)PyLong_AsLongLong(result)); - return true; - } - else { - PyErr_Format(PyExc_TypeError, "incorrect compare function return type"); - return false; - } -} - static bool array_sort(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); @@ -1724,64 +1702,19 @@ static bool array_sort(JSContext *cx, unsigned argc, JS::Value *vp) { } JS::RootedValue callBack(cx, callbackfn); - - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - - PyObject *pyFunc = pyTypeFactory(cx, global, new JS::RootedValue(cx, args[0].get()))->getPyObject(); - // check if JS or Python function - if (PyFunction_Check(pyFunc)) { - // it's a user-defined python function, check has two arguments - PyObject *code = PyFunction_GetCode(pyFunc); - if (((PyCodeObject *)code)->co_argcount == 1) { - JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG); - return false; - } - else { - JSFunction *compareFun = JS_NewFunction(cx, js_sort_compare_key_func, 2, 0, NULL); - JS::RootedFunction rootedFun(cx, compareFun); - JS::RootedObject rootedFunObj(cx, JS_GetFunctionObject(compareFun)); - - JS::RootedValue privateValue(cx, JS::PrivateValue(pyFunc)); - if (!JS_SetProperty(cx, rootedFunObj, "_key_func_param", privateValue)) { // JS::SetReservedSlot(functionObj, KeyFuncSlot, JS::PrivateValue(keyfunc)); does not work - PyErr_Format(PyExc_SystemError, "JSAPI call failed"); - return NULL; - } - - try { - quickSort(self, 0, len - 1, cx, rootedFun); - } catch (const char *message) { - return false; - } - - // cleanup - if (!JS_DeleteProperty(cx, rootedFunObj, "_key_func_param")) { - PyErr_Format(PyExc_SystemError, "JSAPI call failed"); - return false; - } - } - } else { - // it's either a JS function or a builtin python func - int flags = PyCFunction_GetFlags(pyFunc); - - if (flags & METH_VARARGS && !(flags & METH_KEYWORDS)) { - // it's a user-defined JS func - JS::RootedFunction funObj(cx, JS_ValueToFunction(cx, callBack)); - - try { - quickSort(self, 0, len - 1, cx, funObj); - } catch (const char *message) { - return false; - } - } - else { - // it's a built-in python function - JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_BAD_SORT_ARG); - return false; - } + JS::RootedFunction rootedFun(cx, JS_ValueToFunction(cx, callBack)); + try { + quickSort(self, 0, len - 1, cx, rootedFun); + } catch (const char *message) { + return false; } } } + if (PyErr_Occurred()) { + return false; + } + // return ref to self args.rval().set(jsTypeFactory(cx, self)); return true; diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index f814cc0a..d7cd3b80 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -1,12 +1,12 @@ /** * @file PyObjectProxyHandler.cc * @author Caleb Aikens (caleb@distributive.network) - * @brief + * @brief * @version 0.1 * @date 2024-01-30 - * + * * Copyright (c) 2023 Distributive Corp. - * + * */ #include "include/PyObjectProxyHandler.hh" diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 8805da06..341ab0dd 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -19,6 +19,7 @@ #include "include/JSObjectProxy.hh" #include "include/JSArrayProxy.hh" #include "include/PyDictProxyHandler.hh" +#include "include/JSStringProxy.hh" #include "include/PyListProxyHandler.hh" #include "include/PyObjectProxyHandler.hh" #include "include/pyTypeFactory.hh" @@ -103,6 +104,9 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { else if (PyFloat_Check(object)) { returnType.setNumber(PyFloat_AsDouble(object)); } + else if (PyObject_TypeCheck(object, &JSStringProxyType)) { + returnType.setString(((JSStringProxy *)object)->jsString.toString()); + } else if (PyUnicode_Check(object)) { switch (PyUnicode_KIND(object)) { case (PyUnicode_4BYTE_KIND): { diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 817c7110..7a0b3ade 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -48,8 +48,6 @@ #include #include -JSContext *GLOBAL_CX; - JS::PersistentRootedObject *jsFunctionRegistry; bool functionRegistryCallback(JSContext *cx, unsigned int argc, JS::Value *vp) { @@ -573,7 +571,7 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) Py_DECREF(pyModule); return NULL; } - + Py_INCREF(&JSArrayIterProxyType); if (PyModule_AddObject(pyModule, "JSArrayIterProxy", (PyObject *)&JSArrayIterProxyType) < 0) { Py_DECREF(&JSArrayIterProxyType); diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index d8ccc977..442471ce 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -741,7 +741,7 @@ def myFunc(e,f): try: pm.eval("(arr, compareFun) => {arr.sort(compareFun)}")(items, myFunc) assert (False) - except Exception as e: + except Exception as e: assert str(type(e)) == "" assert str(e) == "incorrect compare function return type" @@ -753,8 +753,8 @@ def myFunc(e,f): pm.eval("(arr, compareFun) => {arr.sort(compareFun)}")(items, myFunc) assert (False) except Exception as e: - assert str(type(e)) == "" - assert str(e) == "object of type 'float' has no len()" + assert str(type(e)) == "" + assert "object of type 'float' has no len()" in str(e) def test_sort_with_one_arg_keyfunc(): items = ['Four', 'Three', 'One'] @@ -765,16 +765,16 @@ def myFunc(e): assert (False) except Exception as e: assert str(type(e)) == "" - assert str(e).__contains__("invalid Array.prototype.sort argument") + assert "takes 1 positional argument but 2 were given" in str(e) def test_sort_with_builtin_keyfunc(): items = ['Four', 'Three', 'One'] try: pm.eval("(arr, compareFun) => {arr.sort(compareFun)}")(items, len) assert (False) - except Exception as e: + except Exception as e: assert str(type(e)) == "" - assert str(e).__contains__("invalid Array.prototype.sort argument") + assert "len() takes exactly one argument (2 given)" in str(e) def test_sort_with_js_func(): items = ['Four', 'Three', 'One'] From e4f4d905d340bd781de3b45bca621b0afe6e4a5d Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 1 Feb 2024 16:29:27 -0500 Subject: [PATCH 006/170] added globalThis tests --- tests/python/test_pythonmonkey_eval.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/python/test_pythonmonkey_eval.py b/tests/python/test_pythonmonkey_eval.py index 6ce9a3e9..1968cb7f 100644 --- a/tests/python/test_pythonmonkey_eval.py +++ b/tests/python/test_pythonmonkey_eval.py @@ -3,6 +3,8 @@ import random from datetime import datetime, timedelta, timezone import math +from io import StringIO +import sys def test_passes(): assert True @@ -289,4 +291,17 @@ def concatenate(a, b): def test_globalThis(): obj = pm.eval('globalThis') - assert str(obj).__contains__("{'python': {'pythonMonkey':") \ No newline at end of file + assert str(obj).__contains__("{'python': {'pythonMonkey':") + +def test_console_globalThis(): + temp_out = StringIO() + sys.stdout = temp_out + pm.eval('console.log(globalThis)') + assert temp_out.getvalue().__contains__("{ python: \n { pythonMonkey: \n") + +def test_console_array(): + temp_out = StringIO() + sys.stdout = temp_out + items = [1, 2, 3] + pm.eval('console.log')(items) + assert temp_out.getvalue() == "[ \x1b[33m1\x1b[39m, \x1b[33m2\x1b[39m, \x1b[33m3\x1b[39m ]\n" \ No newline at end of file From a5b12a56ef4ae550cf7556a573f02ff48e800c21 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 1 Feb 2024 16:30:13 -0500 Subject: [PATCH 007/170] cleanup --- src/DictType.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/DictType.cc b/src/DictType.cc index 86e029f9..1ba29b64 100644 --- a/src/DictType.cc +++ b/src/DictType.cc @@ -3,7 +3,6 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/JSObjectProxy.hh" #include "include/PyType.hh" -#include "include/pyTypeFactory.hh" #include #include From 71269e78a1f68226b2e4535ac828a379abbb2676 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 1 Feb 2024 17:37:25 -0500 Subject: [PATCH 008/170] value parameter to PyTypeFactory no longer needs to be allocated on the heap --- src/JSArrayIterProxy.cc | 12 +-- src/JSArrayProxy.cc | 101 +++++++++++------------ src/JSFunctionProxy.cc | 6 +- src/JSMethodProxy.cc | 6 +- src/JSObjectIterProxy.cc | 12 +-- src/JSObjectProxy.cc | 40 ++++----- src/JobQueue.cc | 5 +- src/PromiseType.cc | 4 +- src/PyDictProxyHandler.cc | 4 +- src/PyListProxyHandler.cc | 64 +++++++------- src/PyObjectProxyHandler.cc | 4 +- src/internalBinding.cc | 4 +- src/jsTypeFactory.cc | 4 +- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- 14 files changed, 130 insertions(+), 138 deletions(-) diff --git a/src/JSArrayIterProxy.cc b/src/JSArrayIterProxy.cc index dc8aade8..bc30381b 100644 --- a/src/JSArrayIterProxy.cc +++ b/src/JSArrayIterProxy.cc @@ -47,16 +47,16 @@ PyObject *JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_next(JSArrayIterPr if (self->it.reversed) { if (self->it.it_index >= 0) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index--, elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), elementVal)->getPyObject(); + JS::RootedValue elementVal(GLOBAL_CX); + JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index--, &elementVal); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), &elementVal)->getPyObject(); } } else { if (self->it.it_index < JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)seq)) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index++, elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), elementVal)->getPyObject(); + JS::RootedValue elementVal(GLOBAL_CX); + JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index++, &elementVal); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), &elementVal)->getPyObject(); } } diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index e393668e..4bfafac7 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -62,10 +62,10 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py for (size_t index = 0;; index++) { const char *methodName = JSArrayProxyType.tp_methods[index].ml_name; if (methodName == NULL || !PyUnicode_Check(key)) { // reached end of list - JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, value); + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, thisObj, &value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -81,12 +81,12 @@ static PyObject *list_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihig JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setInt32(ilow); jArgs[1].setInt32(ihigh); - JS::RootedValue *jReturnedArray = new JS::RootedValue(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "slice", jArgs, jReturnedArray)) { + JS::RootedValue jReturnedArray(GLOBAL_CX); + if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "slice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jReturnedArray)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &jReturnedArray)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy *self, PyObject *key) @@ -111,10 +111,10 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedId id(GLOBAL_CX); JS_IndexToId(GLOBAL_CX, index, &id); - JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, value); + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &value)->getPyObject(); } else if (PySlice_Check(key)) { Py_ssize_t start, stop, step, slicelength, index; @@ -140,10 +140,10 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS_SetElement(GLOBAL_CX, jCombinedArray, index, elementVal); } - JS::RootedValue *jCombinedArrayValue = new JS::RootedValue(GLOBAL_CX); - jCombinedArrayValue->setObjectOrNull(jCombinedArray); + JS::RootedValue jCombinedArrayValue(GLOBAL_CX); + jCombinedArrayValue.setObjectOrNull(jCombinedArray); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &jCombinedArrayValue)->getPyObject(); } } else { @@ -442,20 +442,19 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * } } - JS::RootedValue *elementVal; + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); Py_ssize_t index; /* Search for the first index where items are different */ for (index = 0; index < selfLength && index < otherLength; index++) { - elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *leftItem = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + PyObject *leftItem = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); PyObject *rightItem; if (PyObject_TypeCheck(other, &JSArrayProxyType)) { - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)other)->jsArray, index, elementVal); - rightItem = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)other)->jsArray, index, &elementVal); + rightItem = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); } else { rightItem = ((PyListObject *)other)->ob_item[index]; } @@ -490,10 +489,9 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * Py_RETURN_TRUE; } - elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); /* Compare the final item again using the proper operator */ - return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); + return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { @@ -515,6 +513,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { /* "[" + "1" + ", 2" * (len - 1) + "]" */ writer.min_length = 1 + 1 + (2 + 1) * (selfLength - 1) + 1; + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); if (_PyUnicodeWriter_WriteChar(&writer, '[') < 0) { @@ -529,14 +528,13 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { } } - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); PyObject *s; - if (&elementVal->toObject() == self->jsArray.get()) { + if (&elementVal.toObject() == self->jsArray.get()) { s = PyObject_Repr((PyObject *)self); } else { - s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject()); + s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject()); } if (s == NULL) { goto error; @@ -676,11 +674,11 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyO int cmp; Py_ssize_t numElements = JSArrayProxy_length(self); + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (index = 0, cmp = 0; cmp == 0 && index < numElements; ++index) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); - PyObject *item = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + PyObject *item = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); Py_INCREF(item); cmp = PyObject_RichCompareBool(item, element, Py_EQ); Py_DECREF(item); @@ -752,12 +750,7 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot(JSArrayProxy *self) { } int JSArrayProxyMethodDefinitions::JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg) { - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); - for (Py_ssize_t i = JSArrayProxy_length(self); --i >= 0; ) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, i, elementVal); - Py_VISIT(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject()); - } + // TODO return 0; } @@ -944,20 +937,20 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_pop(JSArrayProxy *self, Py // need the value in the returned array, not the array itself JS::RootedObject rootedReturnedArray(GLOBAL_CX, jReturnedArray.toObjectOrNull()); - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, rootedReturnedArray, 0, elementVal); + JS::RootedValue elementVal(GLOBAL_CX); + JS_GetElement(GLOBAL_CX, rootedReturnedArray, 0, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &elementVal)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, PyObject *value) { Py_ssize_t selfSize = JSArrayProxy_length(self); + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (Py_ssize_t index = 0; index < selfSize; index++) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + PyObject *obj = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1019,11 +1012,11 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_index(JSArrayProxy *self, } } + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (Py_ssize_t index = start; index < stop && index < selfSize; index++) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + PyObject *obj = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1043,11 +1036,11 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_count(JSArrayProxy *self, Py_ssize_t count = 0; Py_ssize_t length = JSArrayProxy_length(self); + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (Py_ssize_t index = 0; index < length; index++) { - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, self->jsArray, index, elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + PyObject *obj = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1096,15 +1089,15 @@ static bool sort_compare_key_func(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); - JS::RootedValue *elementVal = new JS::RootedValue(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, elementVal)->getPyObject(); + JS::RootedValue elementVal0(cx, args[0]); + PyObject *args_0 = pyTypeFactory(cx, global, &elementVal0)->getPyObject(); PyObject *args_0_result = PyObject_CallFunction(keyfunc, "O", args_0); if (!args_0_result) { return false; } - elementVal = new JS::RootedValue(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, elementVal)->getPyObject(); + JS::RootedValue elementVal1(cx, args[1]); + PyObject *args_1 = pyTypeFactory(cx, global, &elementVal1)->getPyObject(); PyObject *args_1_result = PyObject_CallFunction(keyfunc, "O", args_1); if (!args_1_result) { return false; @@ -1146,11 +1139,11 @@ static bool sort_compare_default(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); - JS::RootedValue *elementVal = new JS::RootedValue(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, elementVal)->getPyObject(); + JS::RootedValue elementVal0(cx, args[0]); + PyObject *args_0 = pyTypeFactory(cx, global, &elementVal0)->getPyObject(); - elementVal = new JS::RootedValue(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, elementVal)->getPyObject(); + JS::RootedValue elementVal1(cx, args[1]); + PyObject *args_1 = pyTypeFactory(cx, global, &elementVal1)->getPyObject(); int cmp = PyObject_RichCompareBool(args_0, args_1, Py_LT); if (cmp > 0) { diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index ce2cb93e..b41eb9d8 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -51,11 +51,11 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, } JS::HandleValueArray jsArgs(jsArgsVector); - JS::Rooted *jsReturnVal = new JS::Rooted(cx); - if (!JS_CallFunctionValue(cx, thisObj, jsFunc, jsArgs, jsReturnVal)) { + JS::RootedValue jsReturnVal(cx); + if (!JS_CallFunctionValue(cx, thisObj, jsFunc, jsArgs, &jsReturnVal)) { setSpiderMonkeyException(cx); return NULL; } - return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); + return pyTypeFactory(cx, &thisObj, &jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index f20041dc..adfdaf57 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -61,12 +61,12 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO } JS::HandleValueArray jsArgs(jsArgsVector); - JS::Rooted *jsReturnVal = new JS::Rooted(cx); - if (!JS_CallFunctionValue(cx, selfObject, jsFunc, jsArgs, jsReturnVal)) { + JS::RootedValue jsReturnVal(cx); + if (!JS_CallFunctionValue(cx, selfObject, jsFunc, jsArgs, &jsReturnVal)) { setSpiderMonkeyException(cx); return NULL; } JS::RootedObject globalObj(cx, JS::CurrentGlobalOrNull(cx)); - return pyTypeFactory(cx, &globalObj, jsReturnVal)->getPyObject(); + return pyTypeFactory(cx, &globalObj, &jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index 1a20dac1..17640393 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -58,9 +58,9 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject if (self->it.kind != KIND_KEYS) { JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSObjectProxy *)(self->it.di_dict))->jsObject)); - JS::RootedValue *jsVal = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, jsVal); - value = pyTypeFactory(GLOBAL_CX, global, jsVal)->getPyObject(); + JS::RootedValue jsVal(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); + value = pyTypeFactory(GLOBAL_CX, global, &jsVal)->getPyObject(); } PyObject *ret; @@ -85,9 +85,9 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject if (self->it.kind != KIND_KEYS) { JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSObjectProxy *)(self->it.di_dict))->jsObject)); - JS::RootedValue *jsVal = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, jsVal); - value = pyTypeFactory(GLOBAL_CX, global, jsVal)->getPyObject(); + JS::RootedValue jsVal(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); + value = pyTypeFactory(GLOBAL_CX, global, &jsVal)->getPyObject(); } PyObject *ret; diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 7f530249..cdf8177e 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -90,10 +90,10 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, for (size_t index = 0;; index++) { const char *methodName = JSObjectProxyType.tp_methods[index].ml_name; if (methodName == NULL || !PyUnicode_Check(key)) { - JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsObject); - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, thisObj, &value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -190,11 +190,11 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr for (size_t i = 0; i < length; i++) { JS::HandleId id = props[i]; - JS::RootedValue *key = new JS::RootedValue(GLOBAL_CX); - key->setString(id.toString()); + JS::RootedValue key(GLOBAL_CX); + key.setString(id.toString()); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - PyObject *pyKey = pyTypeFactory(GLOBAL_CX, global, key)->getPyObject(); + PyObject *pyKey = pyTypeFactory(GLOBAL_CX, global, &key)->getPyObject(); PyObject *pyVal1 = PyObject_GetItem((PyObject *)self, pyKey); PyObject *pyVal2 = PyObject_GetItem((PyObject *)other, pyKey); if (!pyVal2) { // if other.key is NULL then not equal @@ -308,13 +308,13 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self goto error; } - JS::RootedValue *elementVal = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, elementVal); + JS::RootedValue elementVal(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &elementVal); - if (&elementVal->toObject() == self->jsObject.get()) { + if (&elementVal.toObject() == self->jsObject.get()) { value = (PyObject *)self; } else { - value = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); } Py_INCREF(value); @@ -454,13 +454,13 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, } JS::RootedObject rootedObject(GLOBAL_CX, Object.toObjectOrNull()); - JS::RootedValue *ret = new JS::RootedValue(GLOBAL_CX); + JS::RootedValue ret(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, rootedObject, "assign", args, ret)) { + if (!JS_CallFunctionName(GLOBAL_CX, rootedObject, "assign", args, &ret)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, global, ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, global, &ret)->getPyObject(); } } @@ -568,9 +568,9 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy return NULL; } - JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); - if (value->isUndefined()) { + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); + if (value.isUndefined()) { if (default_value != NULL) { Py_INCREF(default_value); return default_value; @@ -583,7 +583,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - return pyTypeFactory(GLOBAL_CX, global, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, global, &value)->getPyObject(); } } @@ -619,12 +619,12 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method(JSObjectProx } JS::RootedObject rootedObject(GLOBAL_CX, Object.toObjectOrNull()); - JS::RootedValue *ret = new JS::RootedValue(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, rootedObject, "assign", args, ret)) { + JS::RootedValue ret(GLOBAL_CX); + if (!JS_CallFunctionName(GLOBAL_CX, rootedObject, "assign", args, &ret)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, global, ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, global, &ret)->getPyObject(); } PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_update_method(JSObjectProxy *self, PyObject *args, PyObject *kwds) { diff --git a/src/JobQueue.cc b/src/JobQueue.cc index ee7a013f..2aa7f15f 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -24,8 +24,9 @@ bool JobQueue::enqueuePromiseJob(JSContext *cx, // FIXME (Tom Tang): memory leak, objects not free-ed // FIXME (Tom Tang): `job` function is going to be GC-ed ??? auto global = new JS::RootedObject(cx, incumbentGlobal); - auto jobv = new JS::RootedValue(cx, JS::ObjectValue(*job)); - auto callback = pyTypeFactory(cx, global, jobv)->getPyObject(); + // auto jobv = new JS::RootedValue(cx, JS::ObjectValue(*job)); + JS::RootedValue jobv(cx, JS::ObjectValue(*job)); + auto callback = pyTypeFactory(cx, global, &jobv)->getPyObject(); // Inform the JS runtime that the job queue is no longer empty JS::JobQueueMayNotBeEmpty(cx); diff --git a/src/PromiseType.cc b/src/PromiseType.cc index bf0c6dd9..b43d4f98 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -42,8 +42,8 @@ static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { // The result might be another JS function, so we must keep them alive JS::RootedObject *thisv = new JS::RootedObject(cx); args.computeThis(cx, thisv); // thisv is the global object, not the promise - JS::RootedValue *resultArg = new JS::RootedValue(cx, args[0]); - PyObject *result = pyTypeFactory(cx, thisv, resultArg)->getPyObject(); + JS::RootedValue resultArg(cx, args[0]); + PyObject *result = pyTypeFactory(cx, thisv, &resultArg)->getPyObject(); if (state == JS::PromiseState::Rejected && !PyExceptionInstance_Check(result)) { // Wrap the result object into a SpiderMonkeyError object // because only *Exception objects can be thrown in Python `raise` statement and alike diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index 9f6983ed..1b81cdcc 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -140,10 +140,10 @@ bool PyDictProxyHandler::getOwnPropertyDescriptor( bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, JS::HandleValue receiver, JS::ObjectOpResult &result) const { - JS::RootedValue *rootedV = new JS::RootedValue(cx, v); + JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, global, &rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 72535993..d2913b00 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -89,10 +89,10 @@ static bool array_push(JSContext *cx, unsigned argc, JS::Value *vp) { // surely JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); unsigned numArgs = args.length(); + JS::RootedValue elementVal(cx); for (unsigned index = 0; index < numArgs; index++) { - JS::RootedValue *elementVal = new JS::RootedValue(cx); - elementVal->set(args[index].get()); - if (PyList_Append(self, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + elementVal.set(args[index].get()); + if (PyList_Append(self, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { return false; } } @@ -139,10 +139,10 @@ static bool array_unshift(JSContext *cx, unsigned argc, JS::Value *vp) { // sure PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedValue elementVal(cx); for (int index = args.length() - 1; index >= 0; index--) { - JS::RootedValue *elementVal = new JS::RootedValue(cx); - elementVal->set(args[index].get()); - if (PyList_Insert(self, 0, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + elementVal.set(args[index].get()); + if (PyList_Insert(self, 0, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { return false; } } @@ -253,8 +253,8 @@ static bool array_indexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - JS::RootedValue *elementVal = new JS::RootedValue(cx, args[0].get()); - PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, global, elementVal)->getPyObject(), start); + JS::RootedValue elementVal(cx, args[0].get()); + PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, global, &elementVal)->getPyObject(), start); if (!result) { PyErr_Clear(); @@ -331,10 +331,11 @@ static bool array_splice(JSContext *cx, unsigned argc, JS::Value *vp) { return false; } + JS::RootedValue elementVal(cx); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); for (int index = 0; index < insertCount; index++) { - JS::RootedValue *elementVal = new JS::RootedValue(cx, args[index + 2].get()); - if (PyList_SetItem(inserted, index, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + elementVal.set(args[index + 2].get()); + if (PyList_SetItem(inserted, index, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { return false; } } @@ -397,8 +398,8 @@ static bool array_fill(JSContext *cx, unsigned argc, JS::Value *vp) { } JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - JS::RootedValue *fillValue = new JS::RootedValue(cx, args[0].get()); - PyObject *fillValueItem = pyTypeFactory(cx, global, fillValue)->getPyObject(); + JS::RootedValue fillValue(cx, args[0].get()); + PyObject *fillValueItem = pyTypeFactory(cx, global, &fillValue)->getPyObject(); for (int index = actualStart; index < actualEnd; index++) { if (PyList_SetItem(self, index, fillValueItem) < 0) { return false; @@ -525,20 +526,19 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } unsigned numArgs = args.length(); + JS::RootedValue elementVal(cx); for (unsigned index = 0; index < numArgs; index++) { - JS::RootedValue *elementVal = new JS::RootedValue(cx); - elementVal->set(args[index].get()); + elementVal.set(args[index].get()); - PyObject *item = pyTypeFactory(cx, global, elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(cx, global, &elementVal)->getPyObject(); if (PyObject_TypeCheck(item, &JSArrayProxyType)) { // flatten the array only a depth 1 Py_ssize_t itemLength = JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)item); for (Py_ssize_t flatIndex = 0; flatIndex < itemLength; flatIndex++) { - elementVal = new JS::RootedValue(cx); - if (!JS_GetElement(cx, ((JSArrayProxy *)item)->jsArray, flatIndex, elementVal)) { + if (!JS_GetElement(cx, ((JSArrayProxy *)item)->jsArray, flatIndex, &elementVal)) { return false; } - if (PyList_Append(result, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { return false; } } @@ -553,7 +553,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } } else { - if (PyList_Append(result, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { return false; } } @@ -603,8 +603,8 @@ static bool array_lastIndexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - JS::RootedValue *elementVal = new JS::RootedValue(cx, args[0].get()); - PyObject *element = pyTypeFactory(cx, global, elementVal)->getPyObject(); + JS::RootedValue elementVal(cx, args[0].get()); + PyObject *element = pyTypeFactory(cx, global, &elementVal)->getPyObject(); for (int64_t index = start; index >= 0; index--) { PyObject *item = PyList_GetItem(self, index); Py_INCREF(item); @@ -1190,19 +1190,19 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, uint32_t targetIndex = start; + JS::RootedValue elementVal(cx); JS::RootedObject rootedGlobal(cx, global); for (uint32_t sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { - JS::RootedValue *elementVal = new JS::RootedValue(cx); if (PyObject_TypeCheck(source, &JSArrayProxyType)) { - JS_GetElement(cx, ((JSArrayProxy *)source)->jsArray, sourceIndex, elementVal); + JS_GetElement(cx, ((JSArrayProxy *)source)->jsArray, sourceIndex, &elementVal); } else if (PyObject_TypeCheck(source, &PyList_Type)) { - elementVal->set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); + elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); } - PyObject *element = pyTypeFactory(cx, &rootedGlobal, elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, &rootedGlobal, &elementVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1237,7 +1237,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, JS::SetArrayLength(cx, rootedRetArray, targetIndex + 1); } - JS_SetElement(cx, rootedRetArray, targetIndex, *elementVal); + JS_SetElement(cx, rootedRetArray, targetIndex, elementVal); targetIndex++; } @@ -1268,16 +1268,14 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject glo elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); } - JS::RootedValue *retVal = new JS::RootedValue(cx); - jArgs[0].set(elementVal); jArgs[1].setInt32(sourceIndex); jArgs[2].set(sourceValue); - if (!JS_CallFunctionValue(cx, thisArg, callBack, jArgs, retVal)) { + if (!JS_CallFunctionValue(cx, thisArg, callBack, jArgs, &retVal)) { return false; } - PyObject *element = pyTypeFactory(cx, &rootedGlobal, retVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, &rootedGlobal, &retVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1336,7 +1334,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject glo JS::SetArrayLength(cx, rootedRetArray, targetIndex + 1); } - JS_SetElement(cx, rootedRetArray, targetIndex, *retVal); + JS_SetElement(cx, rootedRetArray, targetIndex, retVal); targetIndex++; } @@ -2059,8 +2057,8 @@ bool PyListProxyHandler::defineProperty( // FIXME (Tom Tang): memory leak JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - JS::RootedValue *itemV = new JS::RootedValue(cx, desc.value()); - PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); + JS::RootedValue itemV(cx, desc.value()); + PyObject *item = pyTypeFactory(cx, global, &itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { return result.failBadIndex(); } diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index d7cd3b80..3af67651 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -93,10 +93,10 @@ bool PyObjectProxyHandler::getOwnPropertyDescriptor( bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v, JS::HandleValue receiver, JS::ObjectOpResult &result) const { - JS::RootedValue *rootedV = new JS::RootedValue(cx, v); + JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, &rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/internalBinding.cc b/src/internalBinding.cc index 791783f7..61764791 100644 --- a/src/internalBinding.cc +++ b/src/internalBinding.cc @@ -61,8 +61,8 @@ PyObject *getInternalBindingPyFn(JSContext *cx) { // Convert to a Python function // FIXME (Tom Tang): memory leak, not free-ed JS::RootedObject *thisObj = new JS::RootedObject(cx, nullptr); - JS::RootedValue *jsFnVal = new JS::RootedValue(cx, JS::ObjectValue(*jsFn)); - PyObject *pyFn = pyTypeFactory(cx, thisObj, jsFnVal)->getPyObject(); + JS::RootedValue jsFnVal(cx, JS::ObjectValue(*jsFn)); + PyObject *pyFn = pyTypeFactory(cx, thisObj, &jsFnVal)->getPyObject(); return pyFn; } diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 341ab0dd..6e11da72 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -301,8 +301,8 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { // populate python args tuple PyObject *pyArgs = PyTuple_New(callArgsLength); for (size_t i = 0; i < callArgsLength; i++) { - JS::RootedValue *jsArg = new JS::RootedValue(cx, callargs[i]); - PyType *pyArg = pyTypeFactory(cx, thisv, jsArg); + JS::RootedValue jsArg(cx, callargs[i]); + PyType *pyArg = pyTypeFactory(cx, thisv, &jsArg); if (!pyArg) return false; // error occurred PyObject *pyArgObj = pyArg->getPyObject(); if (!pyArgObj) return false; // error occurred diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 7a0b3ade..5bbcc78b 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -352,7 +352,7 @@ static PyObject *eval(PyObject *self, PyObject *args) { delete code; // evaluate source code - JS::Rooted *rval = new JS::Rooted(GLOBAL_CX); + JS::RootedValue rval = new JS::RootedValue(GLOBAL_CX); if (!JS::Evaluate(GLOBAL_CX, options, source, rval)) { setSpiderMonkeyException(GLOBAL_CX); return NULL; From cb3feef58b9d947e4b7246caa72f700f820e347c Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 1 Feb 2024 17:37:38 -0500 Subject: [PATCH 009/170] cleanup --- src/JSObjectKeysProxy.cc | 1 - src/JSObjectValuesProxy.cc | 1 - src/TupleType.cc | 1 - 3 files changed, 3 deletions(-) diff --git a/src/JSObjectKeysProxy.cc b/src/JSObjectKeysProxy.cc index 81adbe59..698b1e81 100644 --- a/src/JSObjectKeysProxy.cc +++ b/src/JSObjectKeysProxy.cc @@ -17,7 +17,6 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/jsTypeFactory.hh" -#include "include/pyTypeFactory.hh" #include "include/PyDictProxyHandler.hh" #include diff --git a/src/JSObjectValuesProxy.cc b/src/JSObjectValuesProxy.cc index d6a7a9b5..7c7c8977 100644 --- a/src/JSObjectValuesProxy.cc +++ b/src/JSObjectValuesProxy.cc @@ -17,7 +17,6 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/jsTypeFactory.hh" -#include "include/pyTypeFactory.hh" #include "include/PyDictProxyHandler.hh" #include diff --git a/src/TupleType.cc b/src/TupleType.cc index 7c2018cc..9c3748dc 100644 --- a/src/TupleType.cc +++ b/src/TupleType.cc @@ -12,7 +12,6 @@ #include "include/TupleType.hh" #include "include/PyType.hh" -#include "include/pyTypeFactory.hh" #include From 13938dbb8c2dae08fcd86c6864d370e96d43f0ef Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 1 Feb 2024 17:43:15 -0500 Subject: [PATCH 010/170] should have been part of the previous commit --- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 5bbcc78b..d31abc7d 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -352,7 +352,7 @@ static PyObject *eval(PyObject *self, PyObject *args) { delete code; // evaluate source code - JS::RootedValue rval = new JS::RootedValue(GLOBAL_CX); + JS::RootedValue *rval = new JS::RootedValue(GLOBAL_CX); if (!JS::Evaluate(GLOBAL_CX, options, source, rval)) { setSpiderMonkeyException(GLOBAL_CX); return NULL; From c1fbc1fb3ad7ad7046bcf410583581f5544fb545 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 11:27:32 -0500 Subject: [PATCH 011/170] pyTypeFactory use HandleValue instead of pointer --- src/JSArrayIterProxy.cc | 4 +- src/JSArrayProxy.cc | 51 ++++++++++++------------ src/JSFunctionProxy.cc | 2 +- src/JSMethodProxy.cc | 4 +- src/JSObjectIterProxy.cc | 4 +- src/JSObjectProxy.cc | 12 +++--- src/JobQueue.cc | 2 +- src/PromiseType.cc | 2 +- src/PyDictProxyHandler.cc | 2 +- src/PyListProxyHandler.cc | 24 +++++------ src/PyObjectProxyHandler.cc | 2 +- src/internalBinding.cc | 2 +- src/internalBinding/timers.cc | 2 +- src/jsTypeFactory.cc | 2 +- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- src/pyTypeFactory.cc | 38 +++++++++--------- 16 files changed, 78 insertions(+), 77 deletions(-) diff --git a/src/JSArrayIterProxy.cc b/src/JSArrayIterProxy.cc index bc30381b..f63b278a 100644 --- a/src/JSArrayIterProxy.cc +++ b/src/JSArrayIterProxy.cc @@ -49,14 +49,14 @@ PyObject *JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_next(JSArrayIterPr if (self->it.it_index >= 0) { JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index--, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), &elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), elementVal)->getPyObject(); } } else { if (self->it.it_index < JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)seq)) { JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index++, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), &elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), elementVal)->getPyObject(); } } diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 4bfafac7..31d62d2e 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -65,7 +65,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, &value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -86,7 +86,7 @@ static PyObject *list_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihig PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &jReturnedArray)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jReturnedArray)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy *self, PyObject *key) @@ -114,7 +114,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), value)->getPyObject(); } else if (PySlice_Check(key)) { Py_ssize_t start, stop, step, slicelength, index; @@ -132,7 +132,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy return list_slice(self, start, stop); } else { - JS::RootedObject jCombinedArray = JS::RootedObject(GLOBAL_CX, JS::NewArrayObject(GLOBAL_CX, slicelength)); + JS::RootedObject jCombinedArray(GLOBAL_CX, JS::NewArrayObject(GLOBAL_CX, slicelength)); JS::RootedValue elementVal(GLOBAL_CX); for (size_t cur = start, index = 0; index < slicelength; cur += (size_t)step, index++) { @@ -143,7 +143,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &jCombinedArrayValue)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); } } else { @@ -450,11 +450,11 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * for (index = 0; index < selfLength && index < otherLength; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *leftItem = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + PyObject *leftItem = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); PyObject *rightItem; if (PyObject_TypeCheck(other, &JSArrayProxyType)) { JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)other)->jsArray, index, &elementVal); - rightItem = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + rightItem = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); } else { rightItem = ((PyListObject *)other)->ob_item[index]; } @@ -491,7 +491,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); /* Compare the final item again using the proper operator */ - return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); + return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { @@ -534,7 +534,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { if (&elementVal.toObject() == self->jsArray.get()) { s = PyObject_Repr((PyObject *)self); } else { - s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject()); + s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject()); } if (s == NULL) { goto error; @@ -614,7 +614,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_concat(JSArrayProxy *self, } } - JS::RootedObject jCombinedArray = JS::RootedObject(GLOBAL_CX, JS::NewArrayObject(GLOBAL_CX, (size_t)sizeSelf + (size_t)sizeValue)); + JS::RootedObject jCombinedArray(GLOBAL_CX, JS::NewArrayObject(GLOBAL_CX, (size_t)sizeSelf + (size_t)sizeValue)); JS::RootedValue elementVal(GLOBAL_CX); @@ -636,8 +636,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_concat(JSArrayProxy *self, } } - JS::RootedValue *jCombinedArrayValue = new JS::RootedValue(GLOBAL_CX); - jCombinedArrayValue->setObjectOrNull(jCombinedArray); + JS::RootedValue jCombinedArrayValue(GLOBAL_CX); + jCombinedArrayValue.setObjectOrNull(jCombinedArray); return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); } @@ -652,7 +652,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, return PyErr_NoMemory(); } - JS::RootedObject jCombinedArray = JS::RootedObject(GLOBAL_CX, JS::NewArrayObject(GLOBAL_CX, input_size * n)); + JS::RootedObject jCombinedArray(GLOBAL_CX, JS::NewArrayObject(GLOBAL_CX, input_size * n)); // repeat within new array // one might think of using copyWithin but in SpiderMonkey it's implemented in JS! JS::RootedValue elementVal(GLOBAL_CX); @@ -666,7 +666,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &jCombinedArrayValue)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); } int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyObject *element) { @@ -674,11 +674,12 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyO int cmp; Py_ssize_t numElements = JSArrayProxy_length(self); + JS::RootedValue elementVal(GLOBAL_CX); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (index = 0, cmp = 0; cmp == 0 && index < numElements; ++index) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *item = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); Py_INCREF(item); cmp = PyObject_RichCompareBool(item, element, Py_EQ); Py_DECREF(item); @@ -758,8 +759,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setInt32(0); jArgs[1].setInt32(JSArrayProxy_length(self)); - JS::RootedValue *jReturnedArray = new JS::RootedValue(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "slice", jArgs, jReturnedArray)) { + JS::RootedValue jReturnedArray(GLOBAL_CX); + if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "slice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -940,7 +941,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_pop(JSArrayProxy *self, Py JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, rootedReturnedArray, 0, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), &elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), elementVal)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, PyObject *value) { @@ -950,7 +951,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (Py_ssize_t index = 0; index < selfSize; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1016,7 +1017,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_index(JSArrayProxy *self, JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (Py_ssize_t index = start; index < stop && index < selfSize; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1040,7 +1041,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_count(JSArrayProxy *self, JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); for (Py_ssize_t index = 0; index < length; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1090,14 +1091,14 @@ static bool sort_compare_key_func(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); JS::RootedValue elementVal0(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, &elementVal0)->getPyObject(); + PyObject *args_0 = pyTypeFactory(cx, global, elementVal0)->getPyObject(); PyObject *args_0_result = PyObject_CallFunction(keyfunc, "O", args_0); if (!args_0_result) { return false; } JS::RootedValue elementVal1(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, &elementVal1)->getPyObject(); + PyObject *args_1 = pyTypeFactory(cx, global, elementVal1)->getPyObject(); PyObject *args_1_result = PyObject_CallFunction(keyfunc, "O", args_1); if (!args_1_result) { return false; @@ -1140,10 +1141,10 @@ static bool sort_compare_default(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); JS::RootedValue elementVal0(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, &elementVal0)->getPyObject(); + PyObject *args_0 = pyTypeFactory(cx, global, elementVal0)->getPyObject(); JS::RootedValue elementVal1(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, &elementVal1)->getPyObject(); + PyObject *args_1 = pyTypeFactory(cx, global, elementVal1)->getPyObject(); int cmp = PyObject_RichCompareBool(args_0, args_1, Py_LT); if (cmp > 0) { diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index b41eb9d8..440ea31d 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -57,5 +57,5 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, return NULL; } - return pyTypeFactory(cx, &thisObj, &jsReturnVal)->getPyObject(); + return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index adfdaf57..c1c075e7 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -67,6 +67,6 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO return NULL; } - JS::RootedObject globalObj(cx, JS::CurrentGlobalOrNull(cx)); - return pyTypeFactory(cx, &globalObj, &jsReturnVal)->getPyObject(); + JS::RootedObject *globalObj = new JS::RootedObject(cx, JS::CurrentGlobalOrNull(cx)); + return pyTypeFactory(cx, globalObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index 17640393..3b17d739 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -60,7 +60,7 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSObjectProxy *)(self->it.di_dict))->jsObject)); JS::RootedValue jsVal(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, global, &jsVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, global, jsVal)->getPyObject(); } PyObject *ret; @@ -87,7 +87,7 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSObjectProxy *)(self->it.di_dict))->jsObject)); JS::RootedValue jsVal(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, global, &jsVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, global, jsVal)->getPyObject(); } PyObject *ret; diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index cdf8177e..7d4c1f41 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -93,7 +93,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsObject); - return pyTypeFactory(GLOBAL_CX, thisObj, &value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -194,7 +194,7 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr key.setString(id.toString()); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - PyObject *pyKey = pyTypeFactory(GLOBAL_CX, global, &key)->getPyObject(); + PyObject *pyKey = pyTypeFactory(GLOBAL_CX, global, key)->getPyObject(); PyObject *pyVal1 = PyObject_GetItem((PyObject *)self, pyKey); PyObject *pyVal2 = PyObject_GetItem((PyObject *)other, pyKey); if (!pyVal2) { // if other.key is NULL then not equal @@ -314,7 +314,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self if (&elementVal.toObject() == self->jsObject.get()) { value = (PyObject *)self; } else { - value = pyTypeFactory(GLOBAL_CX, global, &elementVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); } Py_INCREF(value); @@ -460,7 +460,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, global, &ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, global, ret)->getPyObject(); } } @@ -583,7 +583,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - return pyTypeFactory(GLOBAL_CX, global, &value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, global, value)->getPyObject(); } } @@ -624,7 +624,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method(JSObjectProx PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, global, &ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, global, ret)->getPyObject(); } PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_update_method(JSObjectProxy *self, PyObject *args, PyObject *kwds) { diff --git a/src/JobQueue.cc b/src/JobQueue.cc index 2aa7f15f..f878f5be 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -26,7 +26,7 @@ bool JobQueue::enqueuePromiseJob(JSContext *cx, auto global = new JS::RootedObject(cx, incumbentGlobal); // auto jobv = new JS::RootedValue(cx, JS::ObjectValue(*job)); JS::RootedValue jobv(cx, JS::ObjectValue(*job)); - auto callback = pyTypeFactory(cx, global, &jobv)->getPyObject(); + auto callback = pyTypeFactory(cx, global, jobv)->getPyObject(); // Inform the JS runtime that the job queue is no longer empty JS::JobQueueMayNotBeEmpty(cx); diff --git a/src/PromiseType.cc b/src/PromiseType.cc index b43d4f98..9050e841 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -43,7 +43,7 @@ static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *thisv = new JS::RootedObject(cx); args.computeThis(cx, thisv); // thisv is the global object, not the promise JS::RootedValue resultArg(cx, args[0]); - PyObject *result = pyTypeFactory(cx, thisv, &resultArg)->getPyObject(); + PyObject *result = pyTypeFactory(cx, thisv, resultArg)->getPyObject(); if (state == JS::PromiseState::Rejected && !PyExceptionInstance_Check(result)) { // Wrap the result object into a SpiderMonkeyError object // because only *Exception objects can be thrown in Python `raise` statement and alike diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index 1b81cdcc..af3902f7 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -143,7 +143,7 @@ bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, global, &rootedV)->getPyObject())) { + if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index d2913b00..2947e28d 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -92,7 +92,7 @@ static bool array_push(JSContext *cx, unsigned argc, JS::Value *vp) { // surely JS::RootedValue elementVal(cx); for (unsigned index = 0; index < numArgs; index++) { elementVal.set(args[index].get()); - if (PyList_Append(self, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { + if (PyList_Append(self, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { return false; } } @@ -142,7 +142,7 @@ static bool array_unshift(JSContext *cx, unsigned argc, JS::Value *vp) { // sure JS::RootedValue elementVal(cx); for (int index = args.length() - 1; index >= 0; index--) { elementVal.set(args[index].get()); - if (PyList_Insert(self, 0, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { + if (PyList_Insert(self, 0, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { return false; } } @@ -254,7 +254,7 @@ static bool array_indexOf(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); JS::RootedValue elementVal(cx, args[0].get()); - PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, global, &elementVal)->getPyObject(), start); + PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, global, elementVal)->getPyObject(), start); if (!result) { PyErr_Clear(); @@ -335,7 +335,7 @@ static bool array_splice(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); for (int index = 0; index < insertCount; index++) { elementVal.set(args[index + 2].get()); - if (PyList_SetItem(inserted, index, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { + if (PyList_SetItem(inserted, index, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { return false; } } @@ -399,7 +399,7 @@ static bool array_fill(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); JS::RootedValue fillValue(cx, args[0].get()); - PyObject *fillValueItem = pyTypeFactory(cx, global, &fillValue)->getPyObject(); + PyObject *fillValueItem = pyTypeFactory(cx, global, fillValue)->getPyObject(); for (int index = actualStart; index < actualEnd; index++) { if (PyList_SetItem(self, index, fillValueItem) < 0) { return false; @@ -530,7 +530,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { for (unsigned index = 0; index < numArgs; index++) { elementVal.set(args[index].get()); - PyObject *item = pyTypeFactory(cx, global, &elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(cx, global, elementVal)->getPyObject(); if (PyObject_TypeCheck(item, &JSArrayProxyType)) { // flatten the array only a depth 1 Py_ssize_t itemLength = JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)item); @@ -538,7 +538,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { if (!JS_GetElement(cx, ((JSArrayProxy *)item)->jsArray, flatIndex, &elementVal)) { return false; } - if (PyList_Append(result, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { return false; } } @@ -553,7 +553,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } } else { - if (PyList_Append(result, pyTypeFactory(cx, global, &elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { return false; } } @@ -604,7 +604,7 @@ static bool array_lastIndexOf(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); JS::RootedValue elementVal(cx, args[0].get()); - PyObject *element = pyTypeFactory(cx, global, &elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, global, elementVal)->getPyObject(); for (int64_t index = start; index >= 0; index--) { PyObject *item = PyList_GetItem(self, index); Py_INCREF(item); @@ -1202,7 +1202,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); } - PyObject *element = pyTypeFactory(cx, &rootedGlobal, &elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, &rootedGlobal, elementVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1275,7 +1275,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject glo return false; } - PyObject *element = pyTypeFactory(cx, &rootedGlobal, &retVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, &rootedGlobal, retVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -2058,7 +2058,7 @@ bool PyListProxyHandler::defineProperty( // FIXME (Tom Tang): memory leak JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); JS::RootedValue itemV(cx, desc.value()); - PyObject *item = pyTypeFactory(cx, global, &itemV)->getPyObject(); + PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { return result.failBadIndex(); } diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 3af67651..13c508c5 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -96,7 +96,7 @@ bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::Handle JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, &rootedV)->getPyObject())) { + if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/internalBinding.cc b/src/internalBinding.cc index 61764791..410eca6d 100644 --- a/src/internalBinding.cc +++ b/src/internalBinding.cc @@ -62,7 +62,7 @@ PyObject *getInternalBindingPyFn(JSContext *cx) { // FIXME (Tom Tang): memory leak, not free-ed JS::RootedObject *thisObj = new JS::RootedObject(cx, nullptr); JS::RootedValue jsFnVal(cx, JS::ObjectValue(*jsFn)); - PyObject *pyFn = pyTypeFactory(cx, thisObj, &jsFnVal)->getPyObject(); + PyObject *pyFn = pyTypeFactory(cx, thisObj, jsFnVal)->getPyObject(); return pyFn; } diff --git a/src/internalBinding/timers.cc b/src/internalBinding/timers.cc index ec4d5e6c..fc30df42 100644 --- a/src/internalBinding/timers.cc +++ b/src/internalBinding/timers.cc @@ -23,7 +23,7 @@ static bool enqueueWithDelay(JSContext *cx, unsigned argc, JS::Value *vp) { // Convert to a Python function // FIXME (Tom Tang): memory leak, not free-ed JS::RootedObject *thisv = new JS::RootedObject(cx, nullptr); - JS::RootedValue *jobArg = new JS::RootedValue(cx, jobArgVal); + JS::RootedValue jobArg(cx, jobArgVal); PyObject *job = pyTypeFactory(cx, thisv, jobArg)->getPyObject(); // Schedule job to the running Python event-loop diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 6e11da72..5f0c58f6 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -302,7 +302,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { PyObject *pyArgs = PyTuple_New(callArgsLength); for (size_t i = 0; i < callArgsLength; i++) { JS::RootedValue jsArg(cx, callargs[i]); - PyType *pyArg = pyTypeFactory(cx, thisv, &jsArg); + PyType *pyArg = pyTypeFactory(cx, thisv, jsArg); if (!pyArg) return false; // error occurred PyObject *pyArgObj = pyArg->getPyObject(); if (!pyArgObj) return false; // error occurred diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index d31abc7d..c856b5c2 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -359,7 +359,7 @@ static PyObject *eval(PyObject *self, PyObject *args) { } // translate to the proper python type - PyType *returnValue = pyTypeFactory(GLOBAL_CX, global, rval); + PyType *returnValue = pyTypeFactory(GLOBAL_CX, global, *rval); if (PyErr_Occurred()) { return NULL; } diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index 88425643..f8897f0e 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -68,32 +68,32 @@ PyType *pyTypeFactory(PyObject *object) { return pyType; } -PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted *rval) { - if (rval->isUndefined()) { +PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval) { + if (rval.isUndefined()) { return new NoneType(); } - else if (rval->isNull()) { + else if (rval.isNull()) { return new NullType(); } - else if (rval->isBoolean()) { - return new BoolType(rval->toBoolean()); + else if (rval.isBoolean()) { + return new BoolType(rval.toBoolean()); } - else if (rval->isNumber()) { - return new FloatType(rval->toNumber()); + else if (rval.isNumber()) { + return new FloatType(rval.toNumber()); } - else if (rval->isString()) { - StrType *s = new StrType(cx, rval->toString()); + else if (rval.isString()) { + StrType *s = new StrType(cx, rval.toString()); return s; } - else if (rval->isSymbol()) { + else if (rval.isSymbol()) { printf("symbol type is not handled by PythonMonkey yet"); } - else if (rval->isBigInt()) { - return new IntType(cx, rval->toBigInt()); + else if (rval.isBigInt()) { + return new IntType(cx, rval.toBigInt()); } - else if (rval->isObject()) { + else if (rval.isObject()) { JS::Rooted obj(cx); - JS_ValueToObject(cx, *rval, &obj); + JS_ValueToObject(cx, rval, &obj); if (JS::GetClass(obj)->isProxyObject()) { if (js::GetProxyHandler(obj)->family() == &PyDictProxyHandler::family) { // this is one of our proxies for python dicts return new DictType(((PyDictProxyHandler *)js::GetProxyHandler(obj))->pyObject); @@ -137,7 +137,7 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted pyFunc = (PyObject *)(pyFuncVal.toPrivate()); f = new FuncType(pyFunc); } else { - f = new FuncType(cx, *rval); + f = new FuncType(cx, rval); } return f; } @@ -167,20 +167,20 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted } } } - return new DictType(cx, *rval); + return new DictType(cx, rval); } - else if (rval->isMagic()) { + else if (rval.isMagic()) { printf("magic type is not handled by PythonMonkey yet\n"); } std::string errorString("pythonmonkey cannot yet convert Javascript value of: "); - JS::RootedString str(cx, JS::ToString(cx, *rval)); + JS::RootedString str(cx, JS::ToString(cx, rval)); errorString += JS_EncodeStringToUTF8(cx, str).get(); PyErr_SetString(PyExc_TypeError, errorString.c_str()); return NULL; } -PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::Rooted *rval) { +PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval) { PyType *v = pyTypeFactory(cx, thisObj, rval); if (PyErr_Occurred()) { // Clear Python error From 636fcdeafea3ea3161e1f17b867811df7aba08a3 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 11:27:41 -0500 Subject: [PATCH 012/170] cleanup --- src/JSObjectItemsProxy.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JSObjectItemsProxy.cc b/src/JSObjectItemsProxy.cc index 91f593d2..b06c1c09 100644 --- a/src/JSObjectItemsProxy.cc +++ b/src/JSObjectItemsProxy.cc @@ -17,7 +17,6 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/jsTypeFactory.hh" -#include "include/pyTypeFactory.hh" #include "include/PyBaseProxyHandler.hh" #include From b5bd09493c316b2cef03508e6447aa9cacaf9867 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 11:28:03 -0500 Subject: [PATCH 013/170] should have bee part of previous commit --- include/pyTypeFactory.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index 3b79cbdd..e76143e1 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -35,11 +35,11 @@ PyType *pyTypeFactory(PyObject *object); * @param rval - Pointer to the JS::Value who's type and value we wish to encapsulate * @return PyType* - Pointer to a PyType object corresponding to the JS::Value */ -PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted *rval); +PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval); /** * @brief same to pyTypeFactory, but it's guaranteed that no error would be set on the Python error stack, instead * return `pythonmonkey.null` on error */ -PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::Rooted *rval); +PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval); #endif \ No newline at end of file From fade8f0c4a2d9282a71c83e5b0b06d060e11488c Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 12:35:46 -0500 Subject: [PATCH 014/170] cleanup --- src/DictType.cc | 1 - src/FuncType.cc | 2 -- src/IntType.cc | 2 -- src/ListType.cc | 5 ----- src/NoneType.cc | 11 ----------- src/NullType.cc | 11 ----------- src/PromiseType.cc | 3 --- src/PyType.cc | 1 - src/StrType.cc | 1 - src/TupleType.cc | 12 ------------ src/setSpiderMonkeyException.cc | 1 - 11 files changed, 50 deletions(-) diff --git a/src/DictType.cc b/src/DictType.cc index 1ba29b64..93568dac 100644 --- a/src/DictType.cc +++ b/src/DictType.cc @@ -1,6 +1,5 @@ #include "include/DictType.hh" -#include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/JSObjectProxy.hh" #include "include/PyType.hh" diff --git a/src/FuncType.cc b/src/FuncType.cc index b2d76d6b..6b3a53fd 100644 --- a/src/FuncType.cc +++ b/src/FuncType.cc @@ -1,6 +1,4 @@ #include "include/FuncType.hh" - -#include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/JSFunctionProxy.hh" #include "include/PyType.hh" diff --git a/src/IntType.cc b/src/IntType.cc index 71b8c976..9e441856 100644 --- a/src/IntType.cc +++ b/src/IntType.cc @@ -1,7 +1,5 @@ #include "include/modules/pythonmonkey/pythonmonkey.hh" - #include "include/IntType.hh" - #include "include/PyType.hh" #include "include/TypeEnum.hh" diff --git a/src/ListType.cc b/src/ListType.cc index 96eeec53..2d79bc68 100644 --- a/src/ListType.cc +++ b/src/ListType.cc @@ -2,14 +2,9 @@ #include "include/PyType.hh" #include "include/JSArrayProxy.hh" -#include "include/modules/pythonmonkey/pythonmonkey.hh" - #include - - - ListType::ListType() : PyType(PyList_New(0)) {} ListType::ListType(PyObject *object) : PyType(object) {} diff --git a/src/NoneType.cc b/src/NoneType.cc index 3136c779..b7bc2e6c 100644 --- a/src/NoneType.cc +++ b/src/NoneType.cc @@ -1,14 +1,3 @@ -/** - * @file NoneType.hh - * @author Caleb Aikens (caleb@distributive.network) - * @brief Struct for representing None - * @version 0.1 - * @date 2023-02-22 - * - * @copyright Copyright (c) 2023 - * - */ - #include "include/NoneType.hh" #include "include/PyType.hh" diff --git a/src/NullType.cc b/src/NullType.cc index 9aa510ed..c830c7f3 100644 --- a/src/NullType.cc +++ b/src/NullType.cc @@ -1,14 +1,3 @@ -/** - * @file NullType.hh - * @author Caleb Aikens (caleb@distributive.network) - * @brief Struct for representing JS null in a python object - * @version 0.1 - * @date 2023-02-22 - * - * @copyright Copyright (c) 2023 - * - */ - #include "include/NullType.hh" #include "include/modules/pythonmonkey/pythonmonkey.hh" diff --git a/src/PromiseType.cc b/src/PromiseType.cc index 9050e841..2fac0866 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -2,7 +2,6 @@ * @file PromiseType.cc * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Promises - * @version 0.1 * @date 2023-03-29 * * @copyright Copyright (c) 2023 @@ -10,9 +9,7 @@ */ #include "include/modules/pythonmonkey/pythonmonkey.hh" - #include "include/PromiseType.hh" - #include "include/PyEventLoop.hh" #include "include/PyType.hh" #include "include/TypeEnum.hh" diff --git a/src/PyType.cc b/src/PyType.cc index 5725b054..d74af221 100644 --- a/src/PyType.cc +++ b/src/PyType.cc @@ -1,5 +1,4 @@ #include "include/PyType.hh" - #include "include/TypeEnum.hh" #include diff --git a/src/StrType.cc b/src/StrType.cc index dbb43ae4..0aae1b53 100644 --- a/src/StrType.cc +++ b/src/StrType.cc @@ -1,5 +1,4 @@ #include "include/StrType.hh" - #include "include/PyType.hh" #include "include/JSStringProxy.hh" diff --git a/src/TupleType.cc b/src/TupleType.cc index 9c3748dc..25c6b076 100644 --- a/src/TupleType.cc +++ b/src/TupleType.cc @@ -1,16 +1,4 @@ -/** - * @file TupleType.cc - * @author Giovanni Tedesco - * @brief Implementation for the methods of the Tuple Type struct - * @version 0.1 - * @date 2022-08-19 - * - * @copyright Copyright (c) 2022 - * - */ - #include "include/TupleType.hh" - #include "include/PyType.hh" #include diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index 0412a847..285f68b6 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -2,7 +2,6 @@ * @file setSpiderMonkeyException.cc * @author Caleb Aikens (caleb@distributive.network) * @brief Call this function whenever a JS_* function call fails in order to set an appropriate python exception (remember to also return NULL) - * @version 0.1 * @date 2023-02-28 * * @copyright Copyright (c) 2023 From fee7bf3eaa4e57d8c6539c6b080b788c07d6e073 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 13:26:55 -0500 Subject: [PATCH 015/170] typo fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ac8db92..6586eeaa 100644 --- a/README.md +++ b/README.md @@ -357,7 +357,7 @@ exports['today'] = date.today() # Troubleshooting Tips ## CommonJS (require) -If you are having trouble with the CommonJS require function, set environment variable `DEBUG='ctx-module*'` and you can see the filenames it tries to laod. +If you are having trouble with the CommonJS require function, set environment variable `DEBUG='ctx-module*'` and you can see the filenames it tries to load. ## pmdb From 695c71a8520b8801d4ab0e0e61368a011617aa7a Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 17:26:32 -0500 Subject: [PATCH 016/170] crash fix --- src/JSObjectProxy.cc | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 7d4c1f41..f2cdb37b 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -292,13 +292,12 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self Py_INCREF(key); PyObject *s = PyObject_Repr(key); - if (s == NULL) { - Py_DECREF(s); goto error; } int res = _PyUnicodeWriter_WriteStr(&writer, s); + Py_DECREF(s); if (res < 0) { goto error; @@ -316,12 +315,10 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self } else { value = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); } - Py_INCREF(value); - s = PyObject_Repr(value); + s = PyObject_Repr(value); if (s == NULL) { - Py_DECREF(s); goto error; } From 234da4687ca5a9c712ebb18bd860a30245e05f36 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 17:39:55 -0500 Subject: [PATCH 017/170] test for crash fix --- tests/python/test_dicts.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/python/test_dicts.py b/tests/python/test_dicts.py index 783ec376..c4c9b63e 100644 --- a/tests/python/test_dicts.py +++ b/tests/python/test_dicts.py @@ -1,5 +1,6 @@ import pythonmonkey as pm import sys +import subprocess def test_eval_dicts(): d = {"a":1} @@ -312,4 +313,15 @@ def test_toLocaleString(): items = {'a': 10} result = [0] pm.eval("(result, obj) => {result[0] = obj.toLocaleString()}")(result, items) - assert result[0] == '[object Object]' \ No newline at end of file + assert result[0] == '[object Object]' + +#repr +def test_repr_max_recursion_depth(): + subprocess.check_call('npm install crypto-js', shell=True) + CryptoJS = pm.require('crypto-js') + try: + repr(CryptoJS) + assert (False) + except Exception as e: + assert str(type(e)) == "" + assert str(e) == "maximum recursion depth exceeded while getting the repr of an object" \ No newline at end of file From 324ad7aaeff11de2c06888d3f2ceb40b62ca8308 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 17:46:56 -0500 Subject: [PATCH 018/170] added test for issue #58 --- tests/python/test_functions_this.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index 4018470f..e45cb7fd 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -1,4 +1,5 @@ import pythonmonkey as pm +import subprocess def test_python_functions_self(): def pyFunc(param): @@ -114,4 +115,11 @@ class Class: return jsObj.jsMethod(4); } """)(jsObj) - assert pyObj == result[0] and 4 == result[1] #TODO (Caleb Aikens) should `this` be `pyObj` or `jsObj` here? \ No newline at end of file + assert pyObj == result[0] and 4 == result[1] #TODO (Caleb Aikens) should `this` be `pyObj` or `jsObj` here? + +#require +def test_require_this(): + subprocess.check_call('npm install crypto-js', shell=True) + CryptoJS = pm.require('crypto-js') + cipher = CryptoJS.SHA256("Hello, World!").toString() + assert cipher == "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" \ No newline at end of file From 2f5264f4df83c4fdb57fce4ed94a89ee1d6a5c4a Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 2 Feb 2024 17:52:18 -0500 Subject: [PATCH 019/170] better naming --- tests/python/test_functions_this.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index e45cb7fd..99bd9608 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -118,7 +118,7 @@ class Class: assert pyObj == result[0] and 4 == result[1] #TODO (Caleb Aikens) should `this` be `pyObj` or `jsObj` here? #require -def test_require_this(): +def test_require_correct_this(): subprocess.check_call('npm install crypto-js', shell=True) CryptoJS = pm.require('crypto-js') cipher = CryptoJS.SHA256("Hello, World!").toString() From 839d3e84a862a4c59694b1b3a730cc57b9ea8216 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 11:38:07 -0500 Subject: [PATCH 020/170] cleanup --- include/JSArrayProxy.hh | 10 ---------- include/PyBaseProxyHandler.hh | 2 +- include/PyDictProxyHandler.hh | 15 +++++---------- include/PyObjectProxyHandler.hh | 8 ++------ src/JSArrayProxy.cc | 5 ----- src/JSObjectProxy.cc | 8 ++------ src/jsTypeFactory.cc | 2 +- 7 files changed, 11 insertions(+), 39 deletions(-) diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index 911e5edf..9b42a26a 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -192,16 +192,6 @@ public: */ static int JSArrayProxy_clear_slot(JSArrayProxy *self); - /** - * @brief .tp_traverse method - * - * @param self - The JSArrayProxy - * @param visitproc - The function to be applied on each element of the list - * @param arg - The argument to the visit function - * @return 0 on success - */ - static int JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg); - /** * @brief copy method * diff --git a/include/PyBaseProxyHandler.hh b/include/PyBaseProxyHandler.hh index 793f6b2a..7e5e6d7d 100644 --- a/include/PyBaseProxyHandler.hh +++ b/include/PyBaseProxyHandler.hh @@ -25,7 +25,7 @@ struct PyBaseProxyHandler : public js::BaseProxyHandler { public: PyBaseProxyHandler(PyObject *pyObj, const void *family) : js::BaseProxyHandler(family), pyObject(pyObj) {}; - PyObject *pyObject; // @TODO (Caleb Aikens) Consider putting this in a private slot + PyObject *pyObject; bool getPrototypeIfOrdinary(JSContext *cx, JS::HandleObject proxy, bool *isOrdinary, JS::MutableHandleObject protop) const override final; bool preventExtensions(JSContext *cx, JS::HandleObject proxy, JS::ObjectOpResult &result) const override final; diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index bf56c2a6..d7843bd0 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -40,7 +40,7 @@ public: * @param cx - pointer to JSContext * @param proxy - The proxy object who's property we wish to delete * @param id - The key we wish to delete - * @param result - @TODO (Caleb Aikens) read up on JS::ObjectOpResult + * @param result - operation result * @return true - call succeeded * @return false - call failed and an exception has been raised */ @@ -49,7 +49,7 @@ public: /** * @brief [[HasProperty]] * @param cx - pointer to JSContext - * @param proxy - The proxy object who's propery we wish to check + * @param proxy - The proxy object who's property we wish to check * @param id - key value of the property to check * @param bp - out-paramter: true if object has property, false if not * @return true - call succeeded @@ -64,8 +64,8 @@ public: * @param proxy The proxy object who's property we wish to set * @param id Key of the property we wish to set * @param v Value that we wish to set the property to - * @param receiver @TODO (Caleb Aikens) read ECMAScript docs about this - * @param result @TODO (Caleb Aikens) read ECMAScript docs about this + * @param receiver unused + * @param result operation result * @return true call succeed * @return false call failed and an exception has been raised */ @@ -79,7 +79,6 @@ public: * @param proxy - The proxy object who's keys we output * @param props - out-parameter of object IDsoverride; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more /** * @return true - call succeeded * @return false - call failed and an exception has been raised @@ -87,10 +86,7 @@ public: bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const override; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly - * * @param cx pointer to JSContext * @param proxy The proxy object who's property we wish to check * @param id Key of the property we wish to check @@ -100,13 +96,12 @@ public: */ bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const override; + /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly * * @param cx - pointer to JSContext * @param proxy - The proxy object who's keys we outputoverride; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more /** * @param props - out-parameter of object IDs * @return true - call succeeded diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 8b3ce72b..6f5c4a47 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -44,7 +44,7 @@ public: * @param cx - pointer to JSContext * @param proxy - The proxy object who's property we wish to delete * @param id - The key we wish to delete - * @param result - @TODO (Caleb Aikens) read up on JS::ObjectOpResult + * @param result - operation result * @return true - call succeeded * @return false - call failed and an exception has been raised */ @@ -69,8 +69,7 @@ public: * @param proxy The proxy object who's property we wish to set * @param id Key of the property we wish to set * @param v Value that we wish to set the property to - * @param receiver @TODO (Caleb Aikens) read ECMAScript docs about this - * @param result @TODO (Caleb Aikens) read ECMAScript docs about this + * @param result operation result * @return true call succeed * @return false call failed and an exception has been raised */ @@ -88,8 +87,6 @@ public: */ bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const override; - - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more /** * @brief @TODO (Caleb Aikens) read up on what this trap does exactly * @@ -103,7 +100,6 @@ public: bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const override; /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly * * @param cx - pointer to JSContext * @param proxy - The proxy object who's keys we output diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 31d62d2e..ac5b15bf 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -750,11 +750,6 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot(JSArrayProxy *self) { return 0; } -int JSArrayProxyMethodDefinitions::JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg) { - // TODO - return 0; -} - PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setInt32(0); diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index f2cdb37b..a02c3260 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -39,7 +39,7 @@ bool keyToId(PyObject *key, JS::MutableHandleId idp) { idString.set(JS_NewStringCopyUTF8Z(GLOBAL_CX, utf8Chars)); return JS_StringToId(GLOBAL_CX, idString, idp); } else if (PyLong_Check(key)) { // key is int type - uint32_t keyAsInt = PyLong_AsUnsignedLong(key); // raise OverflowError if the value of pylong is out of range for a unsigned long + uint32_t keyAsInt = PyLong_AsUnsignedLong(key); // TODO raise OverflowError if the value of pylong is out of range for a unsigned long return JS_IndexToId(GLOBAL_CX, keyAsInt, idp); } else { return false; // fail @@ -48,7 +48,6 @@ bool keyToId(PyObject *key, JS::MutableHandleId idp) { void JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc(JSObjectProxy *self) { - // TODO (Caleb Aikens): intentional override of PyDict_Type's tp_dealloc. Probably results in leaking dict memory self->jsObject.set(nullptr); PyObject_GC_UnTrack(self); Py_TYPE(self)->tp_free((PyObject *)self); @@ -72,7 +71,6 @@ Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *s JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { - // @TODO (Caleb Aikens) raise exception here return -1; } return props.length(); @@ -181,7 +179,6 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { - // @TODO (Caleb Aikens) raise exception here return NULL; } @@ -561,7 +558,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy skip_optional: JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { - // TODO (Caleb Aikens): raise exception here PyObject *seq = PyTuple_New(length); + // TODO (Caleb Aikens): raise exception here return NULL; } @@ -588,7 +585,6 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(JSObjectPro JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { - // @TODO (Caleb Aikens) raise exception here return NULL; } diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 5f0c58f6..b29a9348 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -314,7 +314,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { setPyException(cx); return false; } - // @TODO (Caleb Aikens) need to check for python exceptions here + callargs.rval().set(jsTypeFactory(cx, pyRval)); if (PyErr_Occurred()) { setPyException(cx); From a7d7a1a3e59154f3f47d65e10faa1c90bbdd95a7 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 11:58:41 -0500 Subject: [PATCH 021/170] should have been part of the previous commit --- src/modules/pythonmonkey/pythonmonkey.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index c856b5c2..28e11cab 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -145,7 +145,6 @@ PyTypeObject JSArrayProxyType = { .tp_getattro = (getattrofunc)JSArrayProxyMethodDefinitions::JSArrayProxy_get, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS, .tp_doc = PyDoc_STR("Javascript Array proxy list"), - .tp_traverse = (traverseproc)JSArrayProxyMethodDefinitions::JSArrayProxy_traverse, .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot, .tp_richcompare = (richcmpfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare, .tp_iter = (getiterfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_iter, From 7b17b0f16328eb158beea64fc89cd03cc4941049 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 14:31:20 -0500 Subject: [PATCH 022/170] added array test --- tests/python/test_arrays.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 442471ce..7f4d3d72 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -1752,4 +1752,12 @@ def test_iterator_last_next(): result = [0] pm.eval("(result, arr) => { let iterator = arr[Symbol.iterator](); iterator.next(); iterator.next(); result[0] = iterator.next()}")(result, items) assert result[0].value == None - assert result[0].done == True \ No newline at end of file + assert result[0].done == True + +#Array.from +def test_array_from(): + items = [1,2] + result = [0] + pm.eval("(result, arr) => { result[0] = Array.from(arr)}")(result, items) + assert result[0] == [1,2] + assert result[0] is not items \ No newline at end of file From 982f57839fe8e13f3ccb15dd2504007e09f284ce Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 14:32:31 -0500 Subject: [PATCH 023/170] cleanup test that uses CryptoJS --- tests/python/test_functions_this.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index 99bd9608..3b4c432b 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -122,4 +122,5 @@ def test_require_correct_this(): subprocess.check_call('npm install crypto-js', shell=True) CryptoJS = pm.require('crypto-js') cipher = CryptoJS.SHA256("Hello, World!").toString() - assert cipher == "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" \ No newline at end of file + assert cipher == "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" + subprocess.check_call('npm uninstall crypto-js', shell=True) \ No newline at end of file From d0ed527e9a98ea1aff1067e8a8982e2874e302a4 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 14:34:00 -0500 Subject: [PATCH 024/170] cleanup after test --- tests/python/test_dicts.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python/test_dicts.py b/tests/python/test_dicts.py index c4c9b63e..df7efbb2 100644 --- a/tests/python/test_dicts.py +++ b/tests/python/test_dicts.py @@ -324,4 +324,5 @@ def test_repr_max_recursion_depth(): assert (False) except Exception as e: assert str(type(e)) == "" - assert str(e) == "maximum recursion depth exceeded while getting the repr of an object" \ No newline at end of file + assert str(e) == "maximum recursion depth exceeded while getting the repr of an object" + subprocess.check_call('npm uninstall crypto-js', shell=True) \ No newline at end of file From 428005a59f769dfa779f8d83461be1e90cf1ced5 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 15:40:55 -0500 Subject: [PATCH 025/170] cleanup --- src/TupleType.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TupleType.cc b/src/TupleType.cc index 25c6b076..d710418d 100644 --- a/src/TupleType.cc +++ b/src/TupleType.cc @@ -1,5 +1,4 @@ #include "include/TupleType.hh" -#include "include/PyType.hh" #include From 8e827f98695a0cda4bef7db9bc57ccc4798540cc Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Mon, 5 Feb 2024 15:44:18 -0500 Subject: [PATCH 026/170] formatting --- src/PyBaseProxyHandler.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/PyBaseProxyHandler.cc b/src/PyBaseProxyHandler.cc index dc50d095..1e86eac5 100644 --- a/src/PyBaseProxyHandler.cc +++ b/src/PyBaseProxyHandler.cc @@ -10,7 +10,9 @@ #include "include/PyBaseProxyHandler.hh" + #include + #include From 15db75875f603aaf20e0fe3ec221a7dfc3311007 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 09:58:45 -0500 Subject: [PATCH 027/170] added iterator test --- tests/python/test_arrays.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 7f4d3d72..ff37c701 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -1754,6 +1754,12 @@ def test_iterator_last_next(): assert result[0].value == None assert result[0].done == True +def test_iterator_iterator(): + items = [1,2,3,4] + result = [0] + pm.eval("(result, arr) => {let iter = arr[Symbol.iterator](); let head = iter.next().value; result[0] = [...iter] }")(result, items) + assert result[0] == [2,3,4] + #Array.from def test_array_from(): items = [1,2] From 050096f136e8cc34412977c932597cd29bb35721 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 10:42:08 -0500 Subject: [PATCH 028/170] crash fix and its test --- src/DictType.cc | 10 ++++++---- src/JSFunctionProxy.cc | 4 ++++ src/JSMethodProxy.cc | 4 ++++ src/ListType.cc | 6 ++++-- tests/python/test_arrays.py | 14 +++++++++++++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/DictType.cc b/src/DictType.cc index 93568dac..b4221548 100644 --- a/src/DictType.cc +++ b/src/DictType.cc @@ -17,8 +17,10 @@ DictType::DictType(PyObject *object) : PyType(object) {} DictType::DictType(JSContext *cx, JS::Handle jsObject) { JSObjectProxy *proxy = (JSObjectProxy *)PyObject_CallObject((PyObject *)&JSObjectProxyType, NULL); - JS::RootedObject obj(cx); - JS_ValueToObject(cx, jsObject, &obj); - proxy->jsObject.set(obj); - this->pyObject = (PyObject *)proxy; + if (proxy != NULL) { + JS::RootedObject obj(cx); + JS_ValueToObject(cx, jsObject, &obj); + proxy->jsObject.set(obj); + this->pyObject = (PyObject *)proxy; + } } \ No newline at end of file diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index 440ea31d..013e6157 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -57,5 +57,9 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, return NULL; } + if (PyErr_Occurred()) { + return NULL; + } + return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index c1c075e7..b702b306 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -67,6 +67,10 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO return NULL; } + if (PyErr_Occurred()) { + return NULL; + } + JS::RootedObject *globalObj = new JS::RootedObject(cx, JS::CurrentGlobalOrNull(cx)); return pyTypeFactory(cx, globalObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/ListType.cc b/src/ListType.cc index 2d79bc68..dac9cf2f 100644 --- a/src/ListType.cc +++ b/src/ListType.cc @@ -11,6 +11,8 @@ ListType::ListType(PyObject *object) : PyType(object) {} ListType::ListType(JSContext *cx, JS::HandleObject jsArrayObj) { JSArrayProxy *proxy = (JSArrayProxy *)PyObject_CallObject((PyObject *)&JSArrayProxyType, NULL); - proxy->jsArray.set(jsArrayObj); - this->pyObject = (PyObject *)proxy; + if (proxy != NULL) { + proxy->jsArray.set(jsArrayObj); + this->pyObject = (PyObject *)proxy; + } } \ No newline at end of file diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index ff37c701..5817c3f3 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -1766,4 +1766,16 @@ def test_array_from(): result = [0] pm.eval("(result, arr) => { result[0] = Array.from(arr)}")(result, items) assert result[0] == [1,2] - assert result[0] is not items \ No newline at end of file + assert result[0] is not items + +# assign generic dict to non-existent slot +def test_generic_dict_bad_index(): + items = [1,2,3] + result = [] + try: + pm.eval("(result, arr) => {result[0] = arr[Symbol.iterator]() }")(result, items) + assert (False) + except Exception as e: + assert str(type(e)) == "" + assert str(e) == ("list assignment index out of range") + \ No newline at end of file From 20a8227fb63cd6ab14231f7d69ed59fd0bf7ac61 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 11:39:08 -0500 Subject: [PATCH 029/170] assign to list beyond current length js-style --- src/PyListProxyHandler.cc | 84 ++++++++++++++++++++++++++++++++++++- tests/python/test_arrays.py | 25 +++++++---- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 2947e28d..3549e5cc 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2039,6 +2039,73 @@ void PyListProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { } } +/* Ensure ob_item has room for at least newsize elements, and set + * ob_size to newsize. If newsize > ob_size on entry, the content + * of the new slots at exit is undefined heap trash; it's the caller's + * responsibility to overwrite them with sane values. + * The number of allocated elements may grow, shrink, or stay the same. + * Failure is impossible if newsize <= self.allocated on entry, although + * that partly relies on an assumption that the system realloc() never + * fails when passed a number of bytes <= the number of bytes last + * allocated (the C standard doesn't guarantee this, but it's hard to + * imagine a realloc implementation where it wouldn't be true). + * Note that self->ob_item may change, and even if newsize is less + * than ob_size on entry. + */ +static int +list_resize(PyListObject *self, Py_ssize_t newsize) +{ + PyObject **items; + size_t new_allocated, num_allocated_bytes; + Py_ssize_t allocated = self->allocated; + + /* Bypass realloc() when a previous overallocation is large enough + to accommodate the newsize. If the newsize falls lower than half + the allocated size, then proceed with the realloc() to shrink the list. + */ + if (allocated >= newsize && newsize >= (allocated >> 1)) { + assert(self->ob_item != NULL || newsize == 0); + Py_SET_SIZE(self, newsize); + return 0; + } + + /* This over-allocates proportional to the list size, making room + * for additional growth. The over-allocation is mild, but is + * enough to give linear-time amortized behavior over a long + * sequence of appends() in the presence of a poorly-performing + * system realloc(). + * Add padding to make the allocated size multiple of 4. + * The growth pattern is: 0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ... + * Note: new_allocated won't overflow because the largest possible value + * is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t. + */ + new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3; + /* Do not overallocate if the new size is closer to overallocated size + * than to the old size. + */ + if (newsize - Py_SIZE(self) > (Py_ssize_t)(new_allocated - newsize)) + new_allocated = ((size_t)newsize + 3) & ~(size_t)3; + + if (newsize == 0) + new_allocated = 0; + if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) { + num_allocated_bytes = new_allocated * sizeof(PyObject *); + items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes); + } + else { + // integer overflow + items = NULL; + } + if (items == NULL) { + PyErr_NoMemory(); + return -1; + } + self->ob_item = items; + Py_SET_SIZE(self, newsize); + self->allocated = new_allocated; + return 0; +} + bool PyListProxyHandler::defineProperty( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, JS::ObjectOpResult &result @@ -2060,8 +2127,23 @@ bool PyListProxyHandler::defineProperty( JS::RootedValue itemV(cx, desc.value()); PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { - return result.failBadIndex(); + // expand array JS-style + Py_XINCREF(item); + Py_ssize_t len = PyList_GET_SIZE(pyObject); + if (list_resize((PyListObject *)pyObject, index + 1) < 0) { + Py_XDECREF(item); + return result.failBadIndex(); + } + PyList_SET_ITEM((PyListObject *)pyObject, index, item); + for (int i = len; i < index; i++) { + Py_INCREF(Py_None); + PyList_SET_ITEM((PyListObject *)pyObject, i, Py_None); + } + + // clear pending exception + PyErr_Clear(); } + return result.succeed(); } diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 5817c3f3..44bd79a8 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -1768,14 +1768,21 @@ def test_array_from(): assert result[0] == [1,2] assert result[0] is not items -# assign generic dict to non-existent slot -def test_generic_dict_bad_index(): +# bad index size expansion +def test_assign_generic_dict_bad_index(): items = [1,2,3] result = [] - try: - pm.eval("(result, arr) => {result[0] = arr[Symbol.iterator]() }")(result, items) - assert (False) - except Exception as e: - assert str(type(e)) == "" - assert str(e) == ("list assignment index out of range") - \ No newline at end of file + pm.eval("(result, arr) => {result[0] = arr[Symbol.iterator]() }")(result, items) + assert repr(result) == "[]" + +def test_assign_bad_index(): + items = [1,2,3] + result = [] + pm.eval("(result, arr) => {result[0] = 4}")(result, items) + assert result[0] == 4 + +def test_assign_bad_index(): + items = [1,2,3] + result = [] + pm.eval("(result, arr) => {result[0] = 4; result[5] = 6}")(result, items) + assert result == [4, None, None, None, None, 6] \ No newline at end of file From 2d934fe9b245100598f066078f12884848fa5aba Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 11:44:24 -0500 Subject: [PATCH 030/170] comment --- src/PyListProxyHandler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 3549e5cc..a91068a5 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2127,7 +2127,7 @@ bool PyListProxyHandler::defineProperty( JS::RootedValue itemV(cx, desc.value()); PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { - // expand array JS-style + // expand Py_XINCREF(item); Py_ssize_t len = PyList_GET_SIZE(pyObject); if (list_resize((PyListObject *)pyObject, index + 1) < 0) { From 63956876cb27134139561add1a30020a8fb138c9 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 11:46:04 -0500 Subject: [PATCH 031/170] cleanup --- src/PyListProxyHandler.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index a91068a5..ca5e23fa 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2165,14 +2165,14 @@ bool PyListProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, bool PyListProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult &result) const { Py_ssize_t index; if (!idToIndex(cx, id, &index)) { - return result.failBadIndex(); // report failure + return result.failBadIndex(); } // Set to undefined instead of actually deleting it if (PyList_SetItem(pyObject, index, Py_None) < 0) { - return result.failCantDelete(); // report failure + return result.failCantDelete(); } - return result.succeed(); // report success + return result.succeed(); } bool PyListProxyHandler::isArray(JSContext *cx, JS::HandleObject proxy, JS::IsArrayAnswer *answer) const { From 10c8c795c87e8ff51bceec13aa37982b9db5b746 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 11:50:26 -0500 Subject: [PATCH 032/170] improved naming --- src/PyListProxyHandler.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index ca5e23fa..515c3f3b 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2129,13 +2129,13 @@ bool PyListProxyHandler::defineProperty( if (PyList_SetItem(pyObject, index, item) < 0) { // expand Py_XINCREF(item); - Py_ssize_t len = PyList_GET_SIZE(pyObject); + Py_ssize_t oldLen = PyList_GET_SIZE(pyObject); if (list_resize((PyListObject *)pyObject, index + 1) < 0) { Py_XDECREF(item); return result.failBadIndex(); } PyList_SET_ITEM((PyListObject *)pyObject, index, item); - for (int i = len; i < index; i++) { + for (int i = oldLen; i < index; i++) { Py_INCREF(Py_None); PyList_SET_ITEM((PyListObject *)pyObject, i, Py_None); } From de32916b9260597f42054e603dcdb42b822ab1c5 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 11:50:39 -0500 Subject: [PATCH 033/170] formatting --- src/JSFunctionProxy.cc | 4 ++-- src/JSMethodProxy.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index 013e6157..abf2056b 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -57,8 +57,8 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, return NULL; } - if (PyErr_Occurred()) { - return NULL; + if (PyErr_Occurred()) { + return NULL; } return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index b702b306..9a75eb3c 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -67,8 +67,8 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO return NULL; } - if (PyErr_Occurred()) { - return NULL; + if (PyErr_Occurred()) { + return NULL; } JS::RootedObject *globalObj = new JS::RootedObject(cx, JS::CurrentGlobalOrNull(cx)); From c827e66256f03340d042f735fc71fa4dd0302b7b Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 6 Feb 2024 12:11:53 -0500 Subject: [PATCH 034/170] added missing pyExceptions --- src/JSObjectProxy.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index a02c3260..84614e2d 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -71,6 +71,7 @@ Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *s JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return -1; } return props.length(); @@ -179,6 +180,7 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } @@ -585,6 +587,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(JSObjectPro JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } From 8f2408f82dd07b4874a54dac57e5c0119dc37213 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 6 Feb 2024 12:36:35 -0500 Subject: [PATCH 035/170] no Py_SET_SIZE on python 3.8 --- src/PyListProxyHandler.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 515c3f3b..70c19d12 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2065,7 +2065,11 @@ list_resize(PyListObject *self, Py_ssize_t newsize) */ if (allocated >= newsize && newsize >= (allocated >> 1)) { assert(self->ob_item != NULL || newsize == 0); + #if PY_VERSION_HEX >= 0x03090000 // 3.9 Py_SET_SIZE(self, newsize); + #else + Py_SIZE(self) = newsize; + #endif return 0; } @@ -2101,7 +2105,11 @@ list_resize(PyListObject *self, Py_ssize_t newsize) return -1; } self->ob_item = items; + #if PY_VERSION_HEX >= 0x03090000 // 3.9 Py_SET_SIZE(self, newsize); + #else + Py_SIZE(self) = newsize; + #endif self->allocated = new_allocated; return 0; } From 95a1b4d0ed8847c2da5c1929018e795861db7b6c Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 6 Feb 2024 15:21:09 -0500 Subject: [PATCH 036/170] removed incorrect comment --- src/PromiseType.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PromiseType.cc b/src/PromiseType.cc index 2fac0866..7ab86e7d 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -24,7 +24,6 @@ #define PY_FUTURE_OBJ_SLOT 0 // slot id to access the python object in JS callbacks #define PROMISE_OBJ_SLOT 1 -// slot id must be less than 2 (https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/src/vm/JSFunction.h#l866), otherwise it will access to arbitrary unsafe memory locations static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); From 1dc7dd18a76aa25a23bf7d34ca591eb1a09bc119 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 6 Feb 2024 15:24:57 -0500 Subject: [PATCH 037/170] missing file headers improved --- include/PyEventLoop.hh | 1 - src/PyEventLoop.cc | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/PyEventLoop.hh b/include/PyEventLoop.hh index 36fdb03c..dd833a04 100644 --- a/include/PyEventLoop.hh +++ b/include/PyEventLoop.hh @@ -2,7 +2,6 @@ * @file PyEventLoop.hh * @author Tom Tang (xmader@distributive.network) * @brief Send jobs to the Python event-loop - * @version 0.1 * @date 2023-04-05 * * @copyright Copyright (c) 2023 diff --git a/src/PyEventLoop.cc b/src/PyEventLoop.cc index d5b7f0a9..e9ce4df7 100644 --- a/src/PyEventLoop.cc +++ b/src/PyEventLoop.cc @@ -1,3 +1,14 @@ +/** + * @file PyEventLoop.cc + * @author Tom Tang (xmader@distributive.network) + * @brief Send jobs to the Python event-loop + * @date 2023-04-05 + * + * @copyright Copyright (c) 2023 + * + */ + + #include "include/PyEventLoop.hh" #include From 2d1462d58380397622d10f8f31503f83708c2de9 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 6 Feb 2024 16:23:08 -0500 Subject: [PATCH 038/170] type improvement --- src/PyListProxyHandler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 70c19d12..d1a575be 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2143,7 +2143,7 @@ bool PyListProxyHandler::defineProperty( return result.failBadIndex(); } PyList_SET_ITEM((PyListObject *)pyObject, index, item); - for (int i = oldLen; i < index; i++) { + for (Py_ssize_t i = oldLen; i < index; i++) { Py_INCREF(Py_None); PyList_SET_ITEM((PyListObject *)pyObject, i, Py_None); } From 9ff38bc862374712eea6749751a99112678b4bca Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 6 Feb 2024 16:23:22 -0500 Subject: [PATCH 039/170] improved comment --- src/PromiseType.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PromiseType.cc b/src/PromiseType.cc index 7ab86e7d..becf7e91 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -22,7 +22,8 @@ #include -#define PY_FUTURE_OBJ_SLOT 0 // slot id to access the python object in JS callbacks +// slot ids to access the python object in JS callbacks +#define PY_FUTURE_OBJ_SLOT 0 #define PROMISE_OBJ_SLOT 1 static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { From 6f2581324786c49efa6ff6b424ce590d62461952 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 7 Feb 2024 10:01:01 -0500 Subject: [PATCH 040/170] fix(JSObjectProxy): correctly handle 'this' value of methods on JSObjectProxys --- src/JSObjectProxy.cc | 28 ++++++++++++++++++++++++++++ tests/python/test_functions_this.py | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 7f530249..2798bf8a 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -93,6 +93,34 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsObject); + // if value is a JSFunction, bind `this` to self + /* TODO (Caleb Aikens) its potentially problematic to bind it like this since if the function + * ever gets assigned to another object like so: + * + * jsObjA.func = jsObjB.func + * jsObjA.func() # `this` will be jsObjB not jsObjA + * + * it will be bound to the wrong object, however I can't find a better way to do this, + * and even pyodide works this way weirdly enough: + * https://github.com/pyodide/pyodide/blob/ee863a7f7907dfb6ee4948bde6908453c9d7ac43/src/core/jsproxy.c#L388 + * + * if the user wants to get an unbound JS function to bind later, they will have to get it without accessing it through + * a JSObjectProxy (such as via pythonmonkey.eval or as the result of some other function) + */ + if (value->isObject()) { + JS::RootedObject valueObject(GLOBAL_CX); + JS_ValueToObject(GLOBAL_CX, *value, &valueObject); + js::ESClass cls; + JS::GetBuiltinClass(GLOBAL_CX, valueObject, &cls); + if (cls == js::ESClass::Function) { + JS::Rooted> args(GLOBAL_CX); + args[0].setObject(*(self->jsObject)); + JS::Rooted boundFunction(GLOBAL_CX); + JS_CallFunctionName(GLOBAL_CX, valueObject, "bind", args, &boundFunction); + value->set(boundFunction); + } + } + return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else { diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index 3b4c432b..9648225d 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -70,7 +70,7 @@ class Class: result = pyObj.jsFunc(2) assert globalThis == result[0] and 2 == result[1] result = jsObj.jsFunc(3) - assert globalThis == result[0] and 3 == result[1] # TODO (Caleb Aikens) should `this` be `globalThis` or `jsObj` here? + assert jsObj == result[0] and 3 == result[1] result = pm.eval("""(jsFunc) => { return jsFunc(4); } From 7b2b613ed9b8626916d752b2cd84312139356e96 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 7 Feb 2024 10:14:16 -0500 Subject: [PATCH 041/170] test(JSObjectProxy): added test for issue #172 --- tests/python/test_functions_this.py | 49 ++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index 9648225d..c2eb0199 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -123,4 +123,51 @@ def test_require_correct_this(): CryptoJS = pm.require('crypto-js') cipher = CryptoJS.SHA256("Hello, World!").toString() assert cipher == "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f" - subprocess.check_call('npm uninstall crypto-js', shell=True) \ No newline at end of file + subprocess.check_call('npm uninstall crypto-js', shell=True) + +def test_require_correct_this_old_style_class(): + example = pm.eval(""" + () => { + // old style class + function Rectangle(w, h) { + this.w = w; + this.h = h; + } + + Rectangle.prototype = { + getThis: function () { + return this; + }, + getArea: function () { + return this.w * this.h; + }, + }; + + // es5 class + class Rectangle2 { + constructor(w,h) { + this.w = w; + this.h = h; + } + + getThis() { + return this; + } + + getArea() { + return this.w * this.h; + } + } + + return { Rectangle: Rectangle, Rectangle2: Rectangle2}; + } + """)() + r = pm.new(example.Rectangle)(1,2) + + assert r.getArea() == 2 + assert r.getThis() == r + + r2 = pm.new(example.Rectangle2)(1,2) + + assert r2.getArea() == 2 + assert r2.getThis() == r2 \ No newline at end of file From 5ab92c8b771726a8b5f19d8a30314d596b83f0fb Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 7 Feb 2024 10:47:35 -0500 Subject: [PATCH 042/170] chore(meta): update all top-of-file comments --- .vscode/c_cpp_properties.json | 3 ++- CMakeLists.txt | 2 +- LICENSE | 2 +- cmake/externals/autopep8/CMakeLists.txt | 2 +- cmake/externals/uncrustify/CMakeLists.txt | 2 +- cmake/format/CMakeLists.txt | 2 +- cmake/format/uncrustify.cfg | 2 +- include/BoolType.hh | 3 +-- include/BufferType.hh | 3 +-- include/DateType.hh | 3 +-- include/DictType.hh | 3 +-- include/ExceptionType.hh | 3 +-- include/FloatType.hh | 3 +-- include/FuncType.hh | 3 +-- include/IntType.hh | 3 +-- include/JSArrayIterProxy.hh | 3 +-- include/JSArrayProxy.hh | 3 +-- include/JSFunctionProxy.hh | 3 +-- include/JSMethodProxy.hh | 3 +-- include/JSObjectItemsProxy.hh | 3 +-- include/JSObjectIterProxy.hh | 3 +-- include/JSObjectKeysProxy.hh | 3 +-- include/JSObjectProxy.hh | 3 +-- include/JSObjectValuesProxy.hh | 3 +-- include/JSStringProxy.hh | 3 +-- include/JobQueue.hh | 3 +-- include/ListType.hh | 3 +-- include/NoneType.hh | 3 +-- include/NullType.hh | 3 +-- include/PromiseType.hh | 3 +-- include/PyBaseProxyHandler.hh | 3 +-- include/PyDictProxyHandler.hh | 2 +- include/PyEventLoop.hh | 3 +-- include/PyListProxyHandler.hh | 3 +-- include/PyObjectProxyHandler.hh | 3 +-- include/PyType.hh | 3 +-- include/StrType.hh | 3 +-- include/TupleType.hh | 3 +-- include/TypeEnum.hh | 3 +-- include/internalBinding.hh | 3 +-- include/jsTypeFactory.hh | 3 +-- include/modules/pythonmonkey/pythonmonkey.hh | 3 +-- include/pyTypeFactory.hh | 3 +-- include/setSpiderMonkeyException.hh | 3 +-- peter-jr | 2 +- src/BufferType.cc | 3 +-- src/JSArrayIterProxy.cc | 2 +- src/JSArrayProxy.cc | 3 +-- src/JSFunctionProxy.cc | 3 +-- src/JSMethodProxy.cc | 3 +-- src/JSObjectItemsProxy.cc | 3 +-- src/JSObjectIterProxy.cc | 2 +- src/JSObjectKeysProxy.cc | 3 +-- src/JSObjectProxy.cc | 3 +-- src/JSObjectValuesProxy.cc | 3 +-- src/NoneType.cc | 3 +-- src/NullType.cc | 3 +-- src/PromiseType.cc | 3 +-- src/PyBaseProxyHandler.cc | 2 +- src/PyDictProxyHandler.cc | 2 +- src/PyListProxyHandler.cc | 2 +- src/PyObjectProxyHandler.cc | 3 +-- src/TupleType.cc | 3 +-- src/internalBinding.cc | 3 ++- src/jsTypeFactory.cc | 3 +-- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- src/pyTypeFactory.cc | 3 +-- src/setSpiderMonkeyException.cc | 3 +-- 68 files changed, 70 insertions(+), 120 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index d1ac024f..a3eea6a2 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,7 +3,8 @@ { "name": "Linux", "includePath": [ - "${workspaceFolder}/include/**" + "${workspaceFolder}/include/**", + "/usr/include/python3.11" ], "defines": [], "compilerPath": "/usr/bin/clang", diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cbe02d0..f9ec11f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Distributive Inc. All Rights Reserved. +# Copyright (c) 2024 Distributive Corp. All Rights Reserved. cmake_minimum_required(VERSION 3.25) # Set minimum cmake version diff --git a/LICENSE b/LICENSE index 085d2547..94696de0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023-2024 Distributive Corp. +Copyright (c) 2022-2024 Distributive Corp. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/cmake/externals/autopep8/CMakeLists.txt b/cmake/externals/autopep8/CMakeLists.txt index ebc95787..092d07db 100644 --- a/cmake/externals/autopep8/CMakeLists.txt +++ b/cmake/externals/autopep8/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Distributive Inc. All Rights Reserved. +# Copyright (c) 2024 Distributive Corp. All Rights Reserved. set(AUTOPEP8_ROOT "${CMAKE_CURRENT_BINARY_DIR}/install" CACHE PATH "The autopep8 root directory." diff --git a/cmake/externals/uncrustify/CMakeLists.txt b/cmake/externals/uncrustify/CMakeLists.txt index ba95422b..080f93b6 100644 --- a/cmake/externals/uncrustify/CMakeLists.txt +++ b/cmake/externals/uncrustify/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Distributive Inc. All Rights Reserved. +# Copyright (c) 2024 Distributive Corp. All Rights Reserved. set(UNCRUSTIFY_ROOT "${CMAKE_CURRENT_BINARY_DIR}/install" CACHE PATH "The Uncrustify root directory." diff --git a/cmake/format/CMakeLists.txt b/cmake/format/CMakeLists.txt index 19d8c7ce..24e34517 100644 --- a/cmake/format/CMakeLists.txt +++ b/cmake/format/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2022 Distributive Inc. All Rights Reserved. +# Copyright (c) 2024 Distributive Corp. All Rights Reserved. if(NOT UNCRUSTIFY_EXECUTABLE) if(UNCRUSTIFY_ROOT STREQUAL "") diff --git a/cmake/format/uncrustify.cfg b/cmake/format/uncrustify.cfg index 56d94610..5de0790b 100644 --- a/cmake/format/uncrustify.cfg +++ b/cmake/format/uncrustify.cfg @@ -1,4 +1,4 @@ -# Copyright (c) 2019 Kings Distributed Systems. All Rights Reserved. +# Copyright (c) 2024 Distributive Corp. All Rights Reserved. file_ext CPP .cc .hh diff --git a/include/BoolType.hh b/include/BoolType.hh index 5e2cc05c..951bbf5e 100644 --- a/include/BoolType.hh +++ b/include/BoolType.hh @@ -2,10 +2,9 @@ * @file BoolType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python bools - * @version 0.1 * @date 2022-12-02 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/BufferType.hh b/include/BufferType.hh index ea2bd954..73fc5483 100644 --- a/include/BufferType.hh +++ b/include/BufferType.hh @@ -2,10 +2,9 @@ * @file BufferType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing ArrayBuffers - * @version 0.1 * @date 2023-04-27 * - * @copyright Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/DateType.hh b/include/DateType.hh index 23f59707..f7830ede 100644 --- a/include/DateType.hh +++ b/include/DateType.hh @@ -2,10 +2,9 @@ * @file DateType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python dates - * @version 0.1 * @date 2022-12-21 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/DictType.hh b/include/DictType.hh index 780a0e73..27d6e413 100644 --- a/include/DictType.hh +++ b/include/DictType.hh @@ -2,10 +2,9 @@ * @file DictType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python dictionaries - * @version 0.1 * @date 2022-08-10 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/ExceptionType.hh b/include/ExceptionType.hh index 86045e8f..bb71e2d7 100644 --- a/include/ExceptionType.hh +++ b/include/ExceptionType.hh @@ -2,10 +2,9 @@ * @file ExceptionType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Python Exception objects from a corresponding JS Error object - * @version 0.1 * @date 2023-04-11 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/FloatType.hh b/include/FloatType.hh index 2544c251..499b2bc6 100644 --- a/include/FloatType.hh +++ b/include/FloatType.hh @@ -2,10 +2,9 @@ * @file FloatType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python floats - * @version 0.1 * @date 2022-12-02 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/FuncType.hh b/include/FuncType.hh index af2d2dfb..391d5bfc 100644 --- a/include/FuncType.hh +++ b/include/FuncType.hh @@ -2,10 +2,9 @@ * @file FuncType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python functions - * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ #ifndef PythonMonkey_FuncType_ diff --git a/include/IntType.hh b/include/IntType.hh index 81111216..efbb431e 100644 --- a/include/IntType.hh +++ b/include/IntType.hh @@ -2,10 +2,9 @@ * @file IntType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) * @brief Struct for representing python ints - * @version 0.2 * @date 2023-03-16 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSArrayIterProxy.hh b/include/JSArrayIterProxy.hh index a4a9e660..fc4a93f7 100644 --- a/include/JSArrayIterProxy.hh +++ b/include/JSArrayIterProxy.hh @@ -2,10 +2,9 @@ * @file JSArrayIterProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSArrayIterProxy is a custom C-implemented python type that derives from PyListIter - * @version 0.1 * @date 2024-01-15 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index 911e5edf..ce4ce26e 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -2,10 +2,9 @@ * @file JSArrayProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSArrayProxy is a custom C-implemented python type that derives from list. It acts as a proxy for JSArrays from Spidermonkey, and behaves like a list would. - * @version 0.1 * @date 2023-11-22 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSFunctionProxy.hh b/include/JSFunctionProxy.hh index a73ae6ea..a46980ef 100644 --- a/include/JSFunctionProxy.hh +++ b/include/JSFunctionProxy.hh @@ -2,10 +2,9 @@ * @file JSFunctionProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. - * @version 0.1 * @date 2023-09-28 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSMethodProxy.hh b/include/JSMethodProxy.hh index f08bd81b..009229fb 100644 --- a/include/JSMethodProxy.hh +++ b/include/JSMethodProxy.hh @@ -2,10 +2,9 @@ * @file JSMethodProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. - * @version 0.1 * @date 2023-11-14 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectItemsProxy.hh b/include/JSObjectItemsProxy.hh index 91c8bc72..480a48ae 100644 --- a/include/JSObjectItemsProxy.hh +++ b/include/JSObjectItemsProxy.hh @@ -2,10 +2,9 @@ * @file JSObjectItemsProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectItemsProxy is a custom C-implemented python type that derives from dict items - * @version 0.1 * @date 2024-01-19 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectIterProxy.hh b/include/JSObjectIterProxy.hh index d6fb1452..d8f46075 100644 --- a/include/JSObjectIterProxy.hh +++ b/include/JSObjectIterProxy.hh @@ -2,10 +2,9 @@ * @file JSObjectIterProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectIterProxy is a custom C-implemented python type that derives from PyDictIterKey - * @version 0.1 * @date 2024-01-17 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectKeysProxy.hh b/include/JSObjectKeysProxy.hh index 1dea37be..2564bb3f 100644 --- a/include/JSObjectKeysProxy.hh +++ b/include/JSObjectKeysProxy.hh @@ -2,10 +2,9 @@ * @file JSObjectKeysProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectKeysProxy is a custom C-implemented python type that derives from dict keys - * @version 0.1 * @date 2024-01-16 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 920a03d5..54eb38f2 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -2,10 +2,9 @@ * @file JSObjectProxy.hh * @author Caleb Aikens (caleb@distributive.network) & Tom Tang (xmader@distributive.network) * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. - * @version 0.1 * @date 2023-06-26 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectValuesProxy.hh b/include/JSObjectValuesProxy.hh index 238d3066..c2b3be59 100644 --- a/include/JSObjectValuesProxy.hh +++ b/include/JSObjectValuesProxy.hh @@ -2,10 +2,9 @@ * @file JSObjectValuesProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectValuesProxy is a custom C-implemented python type that derives from dict values - * @version 0.1 * @date 2023-06-26 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSStringProxy.hh b/include/JSStringProxy.hh index 135eac6f..8c82374c 100644 --- a/include/JSStringProxy.hh +++ b/include/JSStringProxy.hh @@ -2,10 +2,9 @@ * @file JSStringProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSStringProxy is a custom C-implemented python type that derives from str. It acts as a proxy for JSStrings from Spidermonkey, and behaves like a str would. - * @version 0.1 * @date 2024-01-03 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JobQueue.hh b/include/JobQueue.hh index 5d10e9a7..183bcf9f 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -2,10 +2,9 @@ * @file JobQueue.hh * @author Tom Tang (xmader@distributive.network) * @brief Implement the ECMAScript Job Queue - * @version 0.1 * @date 2023-04-03 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/ListType.hh b/include/ListType.hh index df5ba9ac..d5112057 100644 --- a/include/ListType.hh +++ b/include/ListType.hh @@ -2,10 +2,9 @@ * @file ListType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python lists - * @version 0.1 * @date 2022-08-18 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/NoneType.hh b/include/NoneType.hh index a2f7a82d..abfbacd2 100644 --- a/include/NoneType.hh +++ b/include/NoneType.hh @@ -2,10 +2,9 @@ * @file NoneType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing None - * @version 0.1 * @date 2023-02-22 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/NullType.hh b/include/NullType.hh index 80b91e20..9dbf68c0 100644 --- a/include/NullType.hh +++ b/include/NullType.hh @@ -2,10 +2,9 @@ * @file NullType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing JS null in a python object - * @version 0.1 * @date 2023-02-22 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PromiseType.hh b/include/PromiseType.hh index cc8381f1..e2c7bc3e 100644 --- a/include/PromiseType.hh +++ b/include/PromiseType.hh @@ -2,10 +2,9 @@ * @file PromiseType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Promises - * @version 0.1 * @date 2023-03-29 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyBaseProxyHandler.hh b/include/PyBaseProxyHandler.hh index 793f6b2a..dc3700a8 100644 --- a/include/PyBaseProxyHandler.hh +++ b/include/PyBaseProxyHandler.hh @@ -2,10 +2,9 @@ * @file PyBaseProxyHandler.hh * @author Caleb Aikens (caleb@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief Structs for creating JS proxy objects. - * @version 0.1 * @date 2023-04-20 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index bf56c2a6..97ebc162 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -4,7 +4,7 @@ * @brief Structs for creating JS proxy objects. Used by DictType for object coercion * @date 2023-04-20 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyEventLoop.hh b/include/PyEventLoop.hh index 36fdb03c..8c623479 100644 --- a/include/PyEventLoop.hh +++ b/include/PyEventLoop.hh @@ -2,10 +2,9 @@ * @file PyEventLoop.hh * @author Tom Tang (xmader@distributive.network) * @brief Send jobs to the Python event-loop - * @version 0.1 * @date 2023-04-05 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyListProxyHandler.hh b/include/PyListProxyHandler.hh index ba7abe97..faebac51 100644 --- a/include/PyListProxyHandler.hh +++ b/include/PyListProxyHandler.hh @@ -2,10 +2,9 @@ * @file PyListProxyHandler.hh * @author Philippe Laporte (philippe@distributive.network) * @brief Structs for creating JS proxy objects. Used by ListType for List coercion - * @version 0.1 * @date 2023-12-01 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 8b3ce72b..5dc29737 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -2,10 +2,9 @@ * @file PyObjectProxyHandler.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Structs for creating JS proxy objects. Used for default object coercion - * @version 0.1 * @date 2024-01-25 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyType.hh b/include/PyType.hh index b75d7f1b..382c4729 100644 --- a/include/PyType.hh +++ b/include/PyType.hh @@ -2,10 +2,9 @@ * @file PyType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python types - * @version 0.1 * @date 2022-07-27 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/StrType.hh b/include/StrType.hh index b6f5e16c..af67ca71 100644 --- a/include/StrType.hh +++ b/include/StrType.hh @@ -2,10 +2,9 @@ * @file StrType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python strings - * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/TupleType.hh b/include/TupleType.hh index c1209cb4..783b8549 100644 --- a/include/TupleType.hh +++ b/include/TupleType.hh @@ -2,10 +2,9 @@ * @file TupleType.hh * @author Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python tuples - * @version 0.1 * @date 2022-08-19 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/TypeEnum.hh b/include/TypeEnum.hh index a2d2c875..580bf6f7 100644 --- a/include/TypeEnum.hh +++ b/include/TypeEnum.hh @@ -2,10 +2,9 @@ * @file TypeEnum.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) * @brief Enum for every PyType - * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/internalBinding.hh b/include/internalBinding.hh index 24c00082..c29aa79b 100644 --- a/include/internalBinding.hh +++ b/include/internalBinding.hh @@ -2,10 +2,9 @@ * @file internalBinding.hh * @author Tom Tang (xmader@distributive.network) * @brief - * @version 0.1 * @date 2023-05-16 * - * @copyright Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/jsTypeFactory.hh b/include/jsTypeFactory.hh index 6ec68412..6150ceaa 100644 --- a/include/jsTypeFactory.hh +++ b/include/jsTypeFactory.hh @@ -2,10 +2,9 @@ * @file jsTypeFactory.hh * @author Caleb Aikens (caleb@distributive.network) * @brief - * @version 0.1 * @date 2023-02-15 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/modules/pythonmonkey/pythonmonkey.hh b/include/modules/pythonmonkey/pythonmonkey.hh index 7f964bce..3e8c9424 100644 --- a/include/modules/pythonmonkey/pythonmonkey.hh +++ b/include/modules/pythonmonkey/pythonmonkey.hh @@ -2,10 +2,9 @@ * @file pythonmonkey.hh * @author Caleb Aikens (caleb@kingsds.network) * @brief This file defines the pythonmonkey module, along with its various functions. - * @version 0.1 * @date 2022-09-06 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ #ifndef PythonMonkey_Module_PythonMonkey diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index 3b79cbdd..c2d0353e 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -2,10 +2,9 @@ * @file pyTypeFactory.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Function for wrapping arbitrary PyObjects into the appropriate PyType class, and coercing JS types to python types - * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/setSpiderMonkeyException.hh b/include/setSpiderMonkeyException.hh index f838e5e4..fcaf8141 100644 --- a/include/setSpiderMonkeyException.hh +++ b/include/setSpiderMonkeyException.hh @@ -2,10 +2,9 @@ * @file setSpiderMonkeyException.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Call this function whenever a JS_* function call fails in order to set an appropriate python exception (remember to also return NULL) - * @version 0.1 * @date 2023-02-28 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/peter-jr b/peter-jr index 873a85f6..1bdbcfe9 100755 --- a/peter-jr +++ b/peter-jr @@ -27,7 +27,7 @@ if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then cat < Date: Wed, 7 Feb 2024 11:26:47 -0500 Subject: [PATCH 043/170] cleanup: no need for overriding methods that merely call super. proper traverse methods --- include/JSArrayProxy.hh | 44 +++++++++--------------- include/JSObjectProxy.hh | 38 ++++++++++---------- src/JSArrayProxy.cc | 17 ++++----- src/JSObjectProxy.cc | 25 +++++++------- src/modules/pythonmonkey/pythonmonkey.cc | 28 +++++++-------- 5 files changed, 66 insertions(+), 86 deletions(-) diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index 9b42a26a..ff2bb482 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -40,26 +40,6 @@ public: */ static void JSArrayProxy_dealloc(JSArrayProxy *self); - /** - * @brief New method (.tp_new), creates a new instance of the JSArrayProxy type, exposed as the __new()__ method in python - * - * @param type - The type of object to be created, will always be JSArrayProxyType or a derived type - * @param args - arguments to the __new()__ method, not used - * @param kwds - keyword arguments to the __new()__ method, not used - * @return PyObject* - A new instance of JSArrayProxy - */ - static PyObject *JSArrayProxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds); - - /** - * @brief Initialization method (.tp_init), initializes a newly created instance of JSArrayProxy. exposed as the __init()__ method in python - * - * @param self - The JSArrayProxy to be initialized - * @param args - arguments to the __init()__ method, expected to be a dict - * @param kwds - keyword arguments to the __init()__ method, not used - * @return int - -1 on exception, return any other value otherwise - */ - static int JSArrayProxy_init(JSArrayProxy *self, PyObject *args, PyObject *kwds); - /** * @brief Length method (.mp_length and .sq_length), returns the number of keys in the JSObject, used by the python len() method * @@ -78,13 +58,13 @@ public: static PyObject *JSArrayProxy_get(JSArrayProxy *self, PyObject *key); -/** - * @brief Getter method (.mp_subscript), returns a value from the JSArrayProxy given a key which can be a slice, used by several built-in python methods as well as the [] and operator - * - * @param self - The JSArrayProxy - * @param key - The key for the value in the JSArrayProxy - * @return PyObject* NULL on exception, the corresponding value otherwise - */ + /** + * @brief Getter method (.mp_subscript), returns a value from the JSArrayProxy given a key which can be a slice, used by several built-in python methods as well as the [] and operator + * + * @param self - The JSArrayProxy + * @param key - The key for the value in the JSArrayProxy + * @return PyObject* NULL on exception, the corresponding value otherwise + */ static PyObject *JSArrayProxy_get_subscript(JSArrayProxy *self, PyObject *key); /** @@ -192,6 +172,16 @@ public: */ static int JSArrayProxy_clear_slot(JSArrayProxy *self); + /** + * @brief .tp_traverse method + * + * @param self - The JSArrayProxy + * @param visitproc - The function to be applied on each element of the list + * @param arg - The argument to the visit function + * @return 0 on success + */ + static int JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg); + /** * @brief copy method * diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 920a03d5..9e292e6a 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -40,26 +40,6 @@ public: */ static void JSObjectProxy_dealloc(JSObjectProxy *self); - /** - * @brief New method (.tp_new), creates a new instance of the JSObjectProxy type, exposed as the __new()__ method in python - * - * @param type - The type of object to be created, will always be JSObjectProxyType or a derived type - * @param args - arguments to the __new()__ method, not used - * @param kwds - keyword arguments to the __new()__ method, not used - * @return PyObject* - A new instance of JSObjectProxy - */ - static PyObject *JSObjectProxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds); - - /** - * @brief Initialization method (.tp_init), initializes a newly created instance of JSObjectProxy. exposed as the __init()__ method in python - * - * @param self - The JSObjectProxy to be initialized - * @param args - arguments to the __init()__ method, expected to be a dict - * @param kwds - keyword arguments to the __init()__ method, not used - * @return int - -1 on exception, return any other value otherwise - */ - static int JSObjectProxy_init(JSObjectProxy *self, PyObject *args, PyObject *kwds); - /** * @brief Length method (.mp_length), returns the number of key-value pairs in the JSObject, used by the python len() method * @@ -96,6 +76,24 @@ public: */ static int JSObjectProxy_assign(JSObjectProxy *self, PyObject *key, PyObject *value); + /** + * @brief .tp_traverse method + * + * @param self - The JSObjectProxy + * @param visitproc - The function to be applied on each element of the dict + * @param arg - The argument to the visit function + * @return 0 on success + */ + static int JSObjectProxy_traverse(JSObjectProxy *self, visitproc visit, void *arg); + + /** + * @brief clear method + * + * @param self - The JSObjectProxy + * @return 0 on success + */ + static int JSObjectProxy_clear(JSObjectProxy *self); + /** * @brief Comparison method (.tp_richcompare), returns appropriate boolean given a comparison operator and other pyobject * diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index ac5b15bf..00ab09e3 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -29,20 +29,10 @@ void JSArrayProxyMethodDefinitions::JSArrayProxy_dealloc(JSArrayProxy *self) { self->jsArray.set(nullptr); + PyObject_GC_UnTrack(self); Py_TYPE(self)->tp_free((PyObject *)self); } -PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) -{ - return PyList_Type.tp_new(subtype, args, kwds); -} - -int JSArrayProxyMethodDefinitions::JSArrayProxy_init(JSArrayProxy *self, PyObject *args, PyObject *kwds) -{ - PyList_Type.tp_init((PyObject *)self, args, kwds); - return 0; -} - Py_ssize_t JSArrayProxyMethodDefinitions::JSArrayProxy_length(JSArrayProxy *self) { uint32_t length; @@ -750,6 +740,11 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot(JSArrayProxy *self) { return 0; } +int JSArrayProxyMethodDefinitions::JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg) { + // Nothing to be done + return 0; +} + PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setInt32(0); diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 84614e2d..84092867 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -53,19 +53,6 @@ void JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc(JSObjectProxy *self) Py_TYPE(self)->tp_free((PyObject *)self); } -PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) -{ - return PyDict_Type.tp_new(subtype, args, kwds); -} - -int JSObjectProxyMethodDefinitions::JSObjectProxy_init(JSObjectProxy *self, PyObject *args, PyObject *kwds) -{ - if (PyDict_Type.tp_init((PyObject *)self, args, kwds) < 0) { - return -1; - } - return 0; -} - Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *self) { JS::RootedIdVector props(GLOBAL_CX); @@ -133,6 +120,18 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, Py return 0; } +int JSObjectProxyMethodDefinitions::JSObjectProxy_traverse(JSObjectProxy *self, visitproc visit, void *arg) { + // Nothing to be done + return 0; +} + +int JSObjectProxyMethodDefinitions::JSObjectProxy_clear(JSObjectProxy *self) { + if (!JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(self)) { + return -1; + } + return 0; +} + PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare(JSObjectProxy *self, PyObject *other, int op) { if (op != Py_EQ && op != Py_NE) { diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 28e11cab..129e9c48 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -65,7 +65,7 @@ static PyTypeObject NullType = { .tp_name = "pythonmonkey.null", .tp_basicsize = sizeof(NullObject), .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Javascript null object"), + .tp_doc = PyDoc_STR("Javascript null object") }; static PyTypeObject BigIntType = { @@ -74,7 +74,7 @@ static PyTypeObject BigIntType = { | Py_TPFLAGS_LONG_SUBCLASS | Py_TPFLAGS_BASETYPE, // can be subclassed .tp_doc = PyDoc_STR("Javascript BigInt object"), - .tp_base = &PyLong_Type, // extending the builtin int type + .tp_base = &PyLong_Type }; PyTypeObject JSObjectProxyType = { @@ -90,25 +90,24 @@ PyTypeObject JSObjectProxyType = { .tp_hash = PyObject_HashNotImplemented, .tp_getattro = (getattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get, .tp_setattro = (setattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign, - .tp_flags = Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_DICT_SUBCLASS, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DICT_SUBCLASS | Py_TPFLAGS_HAVE_GC, .tp_doc = PyDoc_STR("Javascript Object proxy dict"), + .tp_traverse = (traverseproc)JSObjectProxyMethodDefinitions::JSObjectProxy_traverse, + .tp_clear = (inquiry)JSObjectProxyMethodDefinitions::JSObjectProxy_clear, .tp_richcompare = (richcmpfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare, .tp_iter = (getiterfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_iter, .tp_methods = JSObjectProxy_methods, - .tp_base = &PyDict_Type, - .tp_init = (initproc)JSObjectProxyMethodDefinitions::JSObjectProxy_init, - .tp_new = JSObjectProxyMethodDefinitions::JSObjectProxy_new, + .tp_base = &PyDict_Type }; PyTypeObject JSStringProxyType = { .tp_name = "pythonmonkey.JSStringProxy", .tp_basicsize = sizeof(JSStringProxy), .tp_flags = Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_UNICODE_SUBCLASS // https://docs.python.org/3/c-api/typeobj.html#Py_TPFLAGS_LONG_SUBCLASS + | Py_TPFLAGS_UNICODE_SUBCLASS | Py_TPFLAGS_BASETYPE, // can be subclassed .tp_doc = PyDoc_STR("Javascript String value"), - .tp_base = &PyUnicode_Type, // extending the builtin int type + .tp_base = &PyUnicode_Type }; PyTypeObject JSFunctionProxyType = { @@ -119,7 +118,7 @@ PyTypeObject JSFunctionProxyType = { .tp_call = JSFunctionProxyMethodDefinitions::JSFunctionProxy_call, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Javascript Function proxy object"), - .tp_new = JSFunctionProxyMethodDefinitions::JSFunctionProxy_new, + .tp_new = JSFunctionProxyMethodDefinitions::JSFunctionProxy_new }; PyTypeObject JSMethodProxyType = { @@ -130,7 +129,7 @@ PyTypeObject JSMethodProxyType = { .tp_call = JSMethodProxyMethodDefinitions::JSMethodProxy_call, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Javascript Method proxy object"), - .tp_new = JSMethodProxyMethodDefinitions::JSMethodProxy_new, + .tp_new = JSMethodProxyMethodDefinitions::JSMethodProxy_new }; PyTypeObject JSArrayProxyType = { @@ -143,15 +142,14 @@ PyTypeObject JSArrayProxyType = { .tp_as_sequence = &JSArrayProxy_sequence_methods, .tp_as_mapping = &JSArrayProxy_mapping_methods, .tp_getattro = (getattrofunc)JSArrayProxyMethodDefinitions::JSArrayProxy_get, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS | Py_TPFLAGS_HAVE_GC, .tp_doc = PyDoc_STR("Javascript Array proxy list"), + .tp_traverse = (traverseproc)JSArrayProxyMethodDefinitions::JSArrayProxy_traverse, .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot, .tp_richcompare = (richcmpfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare, .tp_iter = (getiterfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_iter, .tp_methods = JSArrayProxy_methods, - .tp_base = &PyList_Type, - .tp_init = (initproc)JSArrayProxyMethodDefinitions::JSArrayProxy_init, - .tp_new = JSArrayProxyMethodDefinitions::JSArrayProxy_new, + .tp_base = &PyList_Type }; PyTypeObject JSArrayIterProxyType = { From 4d1d9c21155b84a5e8084ae91e5732478cfc7669 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 12:05:35 -0500 Subject: [PATCH 044/170] cleanup --- src/jsTypeFactory.cc | 8 +++----- src/pyTypeFactory.cc | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index b29a9348..7a343ee0 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -1,11 +1,10 @@ /** * @file jsTypeFactory.cc - * @author Caleb Aikens (caleb@distributive.network) + * @author Caleb Aikens (caleb@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief - * @version 0.1 * @date 2023-02-15 * - * @copyright Copyright (c) 2023 + * @copyright 2023-2024 Distributive Corp. * */ @@ -37,7 +36,7 @@ #include #include -#include // https://docs.python.org/3/c-api/datetime.html +#include #include @@ -145,7 +144,6 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { // can't determine number of arguments for PyCFunctions, so just assume potentially unbounded uint16_t nargs = 0; if (PyFunction_Check(object)) { - // https://docs.python.org/3.11/reference/datamodel.html?highlight=co_argcount PyCodeObject *bytecode = (PyCodeObject *)PyFunction_GetCode(object); // borrowed reference nargs = bytecode->co_argcount; } diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index f8897f0e..09384bfd 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -1,11 +1,10 @@ /** * @file pyTypeFactory.cc - * @author Caleb Aikens (caleb@distributive.network) + * @author Caleb Aikens (caleb@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief Function for wrapping arbitrary PyObjects into the appropriate PyType class, and coercing JS types to python types - * @version 0.1 * @date 2023-03-29 * - * @copyright Copyright (c) 2023 + * @copyright 2023-2024 Distributive Corp. * */ From cc13130398d3a6cc8a0c347703987f95e89311c6 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 12:07:13 -0500 Subject: [PATCH 045/170] further cleanup --- src/jsTypeFactory.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 7a343ee0..c0515e07 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -291,7 +291,6 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { setPyException(cx); return false; } - // @TODO (Caleb Aikens) need to check for python exceptions here callargs.rval().set(jsTypeFactory(cx, pyRval)); return true; } From 32d2d41e6ab2a25645e2f337ea5ce4d8b8ec7842 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 12:22:00 -0500 Subject: [PATCH 046/170] added missing file headers. cleanup --- include/BoolType.hh | 1 - include/BufferType.hh | 1 - include/DateType.hh | 1 - include/DictType.hh | 1 - include/ExceptionType.hh | 1 - include/FloatType.hh | 1 - include/FuncType.hh | 2 +- include/IntType.hh | 1 - include/JSFunctionProxy.hh | 1 - include/JSMethodProxy.hh | 1 - include/JSStringProxy.hh | 1 - include/JobQueue.hh | 1 - include/ListType.hh | 1 - include/NoneType.hh | 1 - include/NullType.hh | 1 - include/PromiseType.hh | 1 - include/PyBaseProxyHandler.hh | 1 - include/PyListProxyHandler.hh | 1 - include/PyObjectProxyHandler.hh | 1 - include/PyType.hh | 1 - include/StrType.hh | 1 - include/TupleType.hh | 1 - include/TypeEnum.hh | 1 - include/internalBinding.hh | 1 - include/jsTypeFactory.hh | 1 - include/pyTypeFactory.hh | 1 - include/setSpiderMonkeyException.hh | 1 - src/BoolType.cc | 10 ++++++++++ src/BufferType.cc | 3 +-- src/DateType.cc | 10 ++++++++++ src/DictType.cc | 11 +++++++++++ src/ExceptionType.cc | 10 ++++++++++ src/FloatType.cc | 10 ++++++++++ src/FuncType.cc | 10 ++++++++++ src/IntType.cc | 10 ++++++++++ src/JobQueue.cc | 9 +++++++++ src/ListType.cc | 11 +++++++++++ src/NoneType.cc | 10 ++++++++++ src/NullType.cc | 10 ++++++++++ src/PyObjectProxyHandler.cc | 1 - src/PyType.cc | 10 ++++++++++ src/StrType.cc | 10 ++++++++++ src/TupleType.cc | 10 ++++++++++ 43 files changed, 143 insertions(+), 30 deletions(-) diff --git a/include/BoolType.hh b/include/BoolType.hh index 5e2cc05c..97274383 100644 --- a/include/BoolType.hh +++ b/include/BoolType.hh @@ -2,7 +2,6 @@ * @file BoolType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python bools - * @version 0.1 * @date 2022-12-02 * * @copyright Copyright (c) 2022 diff --git a/include/BufferType.hh b/include/BufferType.hh index ea2bd954..d428c103 100644 --- a/include/BufferType.hh +++ b/include/BufferType.hh @@ -2,7 +2,6 @@ * @file BufferType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing ArrayBuffers - * @version 0.1 * @date 2023-04-27 * * @copyright Copyright (c) 2023 Distributive Corp. diff --git a/include/DateType.hh b/include/DateType.hh index 23f59707..55ee1d6d 100644 --- a/include/DateType.hh +++ b/include/DateType.hh @@ -2,7 +2,6 @@ * @file DateType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python dates - * @version 0.1 * @date 2022-12-21 * * @copyright Copyright (c) 2022 diff --git a/include/DictType.hh b/include/DictType.hh index 780a0e73..1d2f0516 100644 --- a/include/DictType.hh +++ b/include/DictType.hh @@ -2,7 +2,6 @@ * @file DictType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python dictionaries - * @version 0.1 * @date 2022-08-10 * * @copyright Copyright (c) 2022 diff --git a/include/ExceptionType.hh b/include/ExceptionType.hh index 86045e8f..7cff9952 100644 --- a/include/ExceptionType.hh +++ b/include/ExceptionType.hh @@ -2,7 +2,6 @@ * @file ExceptionType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Python Exception objects from a corresponding JS Error object - * @version 0.1 * @date 2023-04-11 * * @copyright Copyright (c) 2023 diff --git a/include/FloatType.hh b/include/FloatType.hh index 2544c251..079ebca2 100644 --- a/include/FloatType.hh +++ b/include/FloatType.hh @@ -2,7 +2,6 @@ * @file FloatType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python floats - * @version 0.1 * @date 2022-12-02 * * @copyright Copyright (c) 2022 diff --git a/include/FuncType.hh b/include/FuncType.hh index af2d2dfb..1fb7e973 100644 --- a/include/FuncType.hh +++ b/include/FuncType.hh @@ -2,12 +2,12 @@ * @file FuncType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python functions - * @version 0.1 * @date 2022-08-08 * * @copyright Copyright (c) 2022 * */ + #ifndef PythonMonkey_FuncType_ #define PythonMonkey_FuncType_ diff --git a/include/IntType.hh b/include/IntType.hh index 81111216..0cf2634a 100644 --- a/include/IntType.hh +++ b/include/IntType.hh @@ -2,7 +2,6 @@ * @file IntType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) * @brief Struct for representing python ints - * @version 0.2 * @date 2023-03-16 * * @copyright Copyright (c) 2023 diff --git a/include/JSFunctionProxy.hh b/include/JSFunctionProxy.hh index a73ae6ea..f0e95554 100644 --- a/include/JSFunctionProxy.hh +++ b/include/JSFunctionProxy.hh @@ -2,7 +2,6 @@ * @file JSFunctionProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. - * @version 0.1 * @date 2023-09-28 * * Copyright (c) 2023 Distributive Corp. diff --git a/include/JSMethodProxy.hh b/include/JSMethodProxy.hh index f08bd81b..ffa02308 100644 --- a/include/JSMethodProxy.hh +++ b/include/JSMethodProxy.hh @@ -2,7 +2,6 @@ * @file JSMethodProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. - * @version 0.1 * @date 2023-11-14 * * Copyright (c) 2023 Distributive Corp. diff --git a/include/JSStringProxy.hh b/include/JSStringProxy.hh index 135eac6f..35d9f7ed 100644 --- a/include/JSStringProxy.hh +++ b/include/JSStringProxy.hh @@ -2,7 +2,6 @@ * @file JSStringProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSStringProxy is a custom C-implemented python type that derives from str. It acts as a proxy for JSStrings from Spidermonkey, and behaves like a str would. - * @version 0.1 * @date 2024-01-03 * * Copyright (c) 2024 Distributive Corp. diff --git a/include/JobQueue.hh b/include/JobQueue.hh index 5d10e9a7..ef68bf7e 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -2,7 +2,6 @@ * @file JobQueue.hh * @author Tom Tang (xmader@distributive.network) * @brief Implement the ECMAScript Job Queue - * @version 0.1 * @date 2023-04-03 * * @copyright Copyright (c) 2023 diff --git a/include/ListType.hh b/include/ListType.hh index df5ba9ac..28842a66 100644 --- a/include/ListType.hh +++ b/include/ListType.hh @@ -2,7 +2,6 @@ * @file ListType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python lists - * @version 0.1 * @date 2022-08-18 * * @copyright Copyright (c) 2022 diff --git a/include/NoneType.hh b/include/NoneType.hh index a2f7a82d..bfd34169 100644 --- a/include/NoneType.hh +++ b/include/NoneType.hh @@ -2,7 +2,6 @@ * @file NoneType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing None - * @version 0.1 * @date 2023-02-22 * * @copyright Copyright (c) 2023 diff --git a/include/NullType.hh b/include/NullType.hh index 80b91e20..cdc5066f 100644 --- a/include/NullType.hh +++ b/include/NullType.hh @@ -2,7 +2,6 @@ * @file NullType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing JS null in a python object - * @version 0.1 * @date 2023-02-22 * * @copyright Copyright (c) 2023 diff --git a/include/PromiseType.hh b/include/PromiseType.hh index cc8381f1..df8becea 100644 --- a/include/PromiseType.hh +++ b/include/PromiseType.hh @@ -2,7 +2,6 @@ * @file PromiseType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Promises - * @version 0.1 * @date 2023-03-29 * * @copyright Copyright (c) 2023 diff --git a/include/PyBaseProxyHandler.hh b/include/PyBaseProxyHandler.hh index 7e5e6d7d..58993228 100644 --- a/include/PyBaseProxyHandler.hh +++ b/include/PyBaseProxyHandler.hh @@ -2,7 +2,6 @@ * @file PyBaseProxyHandler.hh * @author Caleb Aikens (caleb@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief Structs for creating JS proxy objects. - * @version 0.1 * @date 2023-04-20 * * Copyright (c) 2023-2024 Distributive Corp. diff --git a/include/PyListProxyHandler.hh b/include/PyListProxyHandler.hh index ba7abe97..b94c9057 100644 --- a/include/PyListProxyHandler.hh +++ b/include/PyListProxyHandler.hh @@ -2,7 +2,6 @@ * @file PyListProxyHandler.hh * @author Philippe Laporte (philippe@distributive.network) * @brief Structs for creating JS proxy objects. Used by ListType for List coercion - * @version 0.1 * @date 2023-12-01 * * Copyright (c) 2023-2024 Distributive Corp. diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 6f5c4a47..ce003eea 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -2,7 +2,6 @@ * @file PyObjectProxyHandler.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Structs for creating JS proxy objects. Used for default object coercion - * @version 0.1 * @date 2024-01-25 * * Copyright (c) 2023 Distributive Corp. diff --git a/include/PyType.hh b/include/PyType.hh index b75d7f1b..e90180d4 100644 --- a/include/PyType.hh +++ b/include/PyType.hh @@ -2,7 +2,6 @@ * @file PyType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python types - * @version 0.1 * @date 2022-07-27 * * @copyright Copyright (c) 2022 diff --git a/include/StrType.hh b/include/StrType.hh index b6f5e16c..409d52be 100644 --- a/include/StrType.hh +++ b/include/StrType.hh @@ -2,7 +2,6 @@ * @file StrType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python strings - * @version 0.1 * @date 2022-08-08 * * @copyright Copyright (c) 2022 diff --git a/include/TupleType.hh b/include/TupleType.hh index c1209cb4..e2b66e78 100644 --- a/include/TupleType.hh +++ b/include/TupleType.hh @@ -2,7 +2,6 @@ * @file TupleType.hh * @author Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python tuples - * @version 0.1 * @date 2022-08-19 * * @copyright Copyright (c) 2022 diff --git a/include/TypeEnum.hh b/include/TypeEnum.hh index a2d2c875..ec6f441d 100644 --- a/include/TypeEnum.hh +++ b/include/TypeEnum.hh @@ -2,7 +2,6 @@ * @file TypeEnum.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) * @brief Enum for every PyType - * @version 0.1 * @date 2022-08-08 * * @copyright Copyright (c) 2023 Distributive Corp. diff --git a/include/internalBinding.hh b/include/internalBinding.hh index 24c00082..ca5907db 100644 --- a/include/internalBinding.hh +++ b/include/internalBinding.hh @@ -2,7 +2,6 @@ * @file internalBinding.hh * @author Tom Tang (xmader@distributive.network) * @brief - * @version 0.1 * @date 2023-05-16 * * @copyright Copyright (c) 2023 Distributive Corp. diff --git a/include/jsTypeFactory.hh b/include/jsTypeFactory.hh index 6ec68412..e3af11cc 100644 --- a/include/jsTypeFactory.hh +++ b/include/jsTypeFactory.hh @@ -2,7 +2,6 @@ * @file jsTypeFactory.hh * @author Caleb Aikens (caleb@distributive.network) * @brief - * @version 0.1 * @date 2023-02-15 * * @copyright Copyright (c) 2023 diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index e76143e1..ecdaeac6 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -2,7 +2,6 @@ * @file pyTypeFactory.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Function for wrapping arbitrary PyObjects into the appropriate PyType class, and coercing JS types to python types - * @version 0.1 * @date 2022-08-08 * * @copyright Copyright (c) 2022 diff --git a/include/setSpiderMonkeyException.hh b/include/setSpiderMonkeyException.hh index f838e5e4..5c24e794 100644 --- a/include/setSpiderMonkeyException.hh +++ b/include/setSpiderMonkeyException.hh @@ -2,7 +2,6 @@ * @file setSpiderMonkeyException.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Call this function whenever a JS_* function call fails in order to set an appropriate python exception (remember to also return NULL) - * @version 0.1 * @date 2023-02-28 * * @copyright Copyright (c) 2023 diff --git a/src/BoolType.cc b/src/BoolType.cc index 47239a35..2e70b472 100644 --- a/src/BoolType.cc +++ b/src/BoolType.cc @@ -1,3 +1,13 @@ +/** + * @file BoolType.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief Struct for representing python bools + * @date 2022-12-02 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/BoolType.hh" #include "include/PyType.hh" diff --git a/src/BufferType.cc b/src/BufferType.cc index f94b76b3..f58292d6 100644 --- a/src/BufferType.cc +++ b/src/BufferType.cc @@ -1,8 +1,7 @@ /** - * @file BufferType.hh + * @file BufferType.cc * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing ArrayBuffers - * @version 0.1 * @date 2023-04-27 * * @copyright Copyright (c) 2023 Distributive Corp. diff --git a/src/DateType.cc b/src/DateType.cc index 8b9dc584..9aebee8b 100644 --- a/src/DateType.cc +++ b/src/DateType.cc @@ -1,3 +1,13 @@ +/** + * @file DateType.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief Struct for representing python dates + * @date 2022-12-21 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/DateType.hh" #include "include/PyType.hh" diff --git a/src/DictType.cc b/src/DictType.cc index b4221548..eaf2ca65 100644 --- a/src/DictType.cc +++ b/src/DictType.cc @@ -1,3 +1,14 @@ +/** + * @file DictType.cc + * @author Caleb Aikens (caleb@distributive.network), Giovanni Tedesco (giovanni@distributive.network) and Philippe Laporte (philippe@distributive.network) + * @brief Struct representing python dictionaries + * @date 2022-08-10 + * + * @copyright Copyright (c) 2022 + * + */ + + #include "include/DictType.hh" #include "include/JSObjectProxy.hh" diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index e3fdb92f..6cb0b5bc 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -1,3 +1,13 @@ +/** + * @file ExceptionType.cc + * @author Tom Tang (xmader@distributive.network) + * @brief Struct for representing Python Exception objects from a corresponding JS Error object + * @date 2023-04-11 + * + * @copyright Copyright (c) 2023 + * + */ + #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/setSpiderMonkeyException.hh" diff --git a/src/FloatType.cc b/src/FloatType.cc index 4c4c4bf0..1dcd4e51 100644 --- a/src/FloatType.cc +++ b/src/FloatType.cc @@ -1,3 +1,13 @@ +/** + * @file FloatType.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief Struct for representing python floats + * @date 2022-12-02 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/FloatType.hh" #include "include/PyType.hh" diff --git a/src/FuncType.cc b/src/FuncType.cc index 6b3a53fd..1ad47e90 100644 --- a/src/FuncType.cc +++ b/src/FuncType.cc @@ -1,3 +1,13 @@ +/** + * @file FuncType.cc + * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) + * @brief Struct representing python functions + * @date 2022-08-08 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/FuncType.hh" #include "include/JSFunctionProxy.hh" #include "include/PyType.hh" diff --git a/src/IntType.cc b/src/IntType.cc index 9e441856..b371f3b2 100644 --- a/src/IntType.cc +++ b/src/IntType.cc @@ -1,3 +1,13 @@ +/** + * @file IntType.cc + * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) + * @brief Struct for representing python ints + * @date 2023-03-16 + * + * @copyright Copyright (c) 2023 + * + */ + #include "include/modules/pythonmonkey/pythonmonkey.hh" #include "include/IntType.hh" #include "include/PyType.hh" diff --git a/src/JobQueue.cc b/src/JobQueue.cc index f878f5be..ba6e5a66 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -1,3 +1,12 @@ +/** + * @file JobQueue.cc + * @author Tom Tang (xmader@distributive.network) + * @brief Implement the ECMAScript Job Queue + * @date 2023-04-03 + * + * @copyright Copyright (c) 2023 + * + */ #include "include/JobQueue.hh" diff --git a/src/ListType.cc b/src/ListType.cc index dac9cf2f..f5f8df67 100644 --- a/src/ListType.cc +++ b/src/ListType.cc @@ -1,3 +1,14 @@ +/** + * @file ListType.cc + * @author Caleb Aikens (caleb@distributive.network), Giovanni Tedesco (giovanni@distributive.network) and Philippe Laporte (philippe@distributive.network) + * @brief Struct for representing python lists + * @date 2022-08-18 + * + * @copyright Copyright (c) 2022, 2023, 2024 Distributive Corp + * + */ + + #include "include/ListType.hh" #include "include/PyType.hh" #include "include/JSArrayProxy.hh" diff --git a/src/NoneType.cc b/src/NoneType.cc index b7bc2e6c..80ecc2b9 100644 --- a/src/NoneType.cc +++ b/src/NoneType.cc @@ -1,3 +1,13 @@ +/** + * @file NoneType.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief Struct for representing None + * @date 2023-02-22 + * + * @copyright Copyright (c) 2023 + * + */ + #include "include/NoneType.hh" #include "include/PyType.hh" diff --git a/src/NullType.cc b/src/NullType.cc index c830c7f3..0748fca0 100644 --- a/src/NullType.cc +++ b/src/NullType.cc @@ -1,3 +1,13 @@ +/** + * @file NullType.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief Struct for representing JS null in a python object + * @date 2023-02-22 + * + * @copyright Copyright (c) 2023 + * + */ + #include "include/NullType.hh" #include "include/modules/pythonmonkey/pythonmonkey.hh" diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 13c508c5..4f523b10 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -2,7 +2,6 @@ * @file PyObjectProxyHandler.cc * @author Caleb Aikens (caleb@distributive.network) * @brief - * @version 0.1 * @date 2024-01-30 * * Copyright (c) 2023 Distributive Corp. diff --git a/src/PyType.cc b/src/PyType.cc index d74af221..d9881c42 100644 --- a/src/PyType.cc +++ b/src/PyType.cc @@ -1,3 +1,13 @@ +/** + * @file PyType.cc + * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) + * @brief Struct representing python types + * @date 2022-07-27 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/PyType.hh" #include "include/TypeEnum.hh" diff --git a/src/StrType.cc b/src/StrType.cc index 0aae1b53..3f412301 100644 --- a/src/StrType.cc +++ b/src/StrType.cc @@ -1,3 +1,13 @@ +/** + * @file StrType.cc + * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) + * @brief Struct for representing python strings + * @date 2022-08-08 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/StrType.hh" #include "include/PyType.hh" #include "include/JSStringProxy.hh" diff --git a/src/TupleType.cc b/src/TupleType.cc index d710418d..9b2046d7 100644 --- a/src/TupleType.cc +++ b/src/TupleType.cc @@ -1,3 +1,13 @@ +/** + * @file TupleType.cc + * @author Giovanni Tedesco (giovanni@distributive.network) + * @brief Struct for representing python tuples + * @date 2022-08-19 + * + * @copyright Copyright (c) 2022 + * + */ + #include "include/TupleType.hh" #include From 7843271d79053cc33064e9da393415627702e230 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 13:28:35 -0500 Subject: [PATCH 047/170] further cleanup --- src/JSArrayProxy.cc | 1 - src/JSFunctionProxy.cc | 1 - src/JSMethodProxy.cc | 1 - src/JSObjectItemsProxy.cc | 1 - src/JSObjectKeysProxy.cc | 1 - src/JSObjectProxy.cc | 1 - src/JSObjectValuesProxy.cc | 1 - 7 files changed, 7 deletions(-) diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 00ab09e3..774eee02 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -2,7 +2,6 @@ * @file JSArrayProxy.cc * @author Philippe Laporte (philippe@distributive.network) * @brief JSArrayProxy is a custom C-implemented python type that derives from list. It acts as a proxy for JSArrays from Spidermonkey, and behaves like a list would. - * @version 0.1 * @date 2023-11-22 * * Copyright (c) 2023 Distributive Corp. diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index abf2056b..b23c35d9 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -2,7 +2,6 @@ * @file JSFunctionProxy.cc * @author Caleb Aikens (caleb@distributive.network) * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. - * @version 0.1 * @date 2023-09-28 * * Copyright (c) 2023 Distributive Corp. diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index 9a75eb3c..5df87d48 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -2,7 +2,6 @@ * @file JSMethodProxy.cc * @author Caleb Aikens (caleb@distributive.network) * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. - * @version 0.1 * @date 2023-11-14 * * Copyright (c) 2023 Distributive Corp. diff --git a/src/JSObjectItemsProxy.cc b/src/JSObjectItemsProxy.cc index b06c1c09..39877255 100644 --- a/src/JSObjectItemsProxy.cc +++ b/src/JSObjectItemsProxy.cc @@ -2,7 +2,6 @@ * @file JSObjectItemsProxy.cc * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectItemsProxy is a custom C-implemented python type that derives from dict keys - * @version 0.1 * @date 2024-01-19 * * Copyright (c) 2024 Distributive Corp. diff --git a/src/JSObjectKeysProxy.cc b/src/JSObjectKeysProxy.cc index 698b1e81..d4733c7b 100644 --- a/src/JSObjectKeysProxy.cc +++ b/src/JSObjectKeysProxy.cc @@ -2,7 +2,6 @@ * @file JSObjectKeysProxy.cc * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectKeysProxy is a custom C-implemented python type that derives from dict keys - * @version 0.1 * @date 2024-01-18 * * Copyright (c) 2024 Distributive Corp. diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 84092867..77ed0eaf 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -2,7 +2,6 @@ * @file JSObjectProxy.cc * @author Caleb Aikens (caleb@distributive.network), Tom Tang (xmader@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. - * @version 0.1 * @date 2023-06-26 * * Copyright (c) 2023-2024 Distributive Corp. diff --git a/src/JSObjectValuesProxy.cc b/src/JSObjectValuesProxy.cc index 7c7c8977..a6dc5c45 100644 --- a/src/JSObjectValuesProxy.cc +++ b/src/JSObjectValuesProxy.cc @@ -2,7 +2,6 @@ * @file JSObjectValuesProxy.cc * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectValuesProxy is a custom C-implemented python type that derives from dict values - * @version 0.1 * @date 2024-01-19 * * Copyright (c) 2024 Distributive Corp. From 809b5faf7b0ab6b98b9573f24e8c094146cacb31 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 13:37:25 -0500 Subject: [PATCH 048/170] improved comment --- src/PyListProxyHandler.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index d1a575be..436c43d2 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2052,6 +2052,7 @@ void PyListProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { * Note that self->ob_item may change, and even if newsize is less * than ob_size on entry. */ +//private static int list_resize(PyListObject *self, Py_ssize_t newsize) { From 976327bf8da940f6b01f21446909ad1ab6560bb9 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 14:03:41 -0500 Subject: [PATCH 049/170] fixed copyright headers --- include/BoolType.hh | 2 +- include/DateType.hh | 2 +- include/DictType.hh | 2 +- include/ExceptionType.hh | 2 +- include/FloatType.hh | 2 +- include/FuncType.hh | 2 +- include/IntType.hh | 2 +- include/JSObjectItemsProxy.hh | 1 - include/JSObjectIterProxy.hh | 1 - include/JSObjectKeysProxy.hh | 1 - include/JSObjectProxy.hh | 1 - include/JSObjectValuesProxy.hh | 3 +-- include/JobQueue.hh | 2 +- include/ListType.hh | 2 +- include/NoneType.hh | 2 +- include/NullType.hh | 2 +- include/PromiseType.hh | 2 +- include/PyEventLoop.hh | 2 +- include/PyType.hh | 2 +- include/StrType.hh | 2 +- include/TupleType.hh | 2 +- include/jsTypeFactory.hh | 2 +- include/modules/pythonmonkey/pythonmonkey.hh | 3 +-- include/pyTypeFactory.hh | 2 +- include/setSpiderMonkeyException.hh | 2 +- .../pythonmonkey/builtin_modules/XMLHttpRequest-internal.py | 1 + python/pythonmonkey/builtin_modules/XMLHttpRequest.js | 2 ++ python/pythonmonkey/builtin_modules/base64.py | 1 + python/pythonmonkey/builtin_modules/console.js | 2 ++ python/pythonmonkey/builtin_modules/event-target.js | 2 ++ python/pythonmonkey/builtin_modules/timers.js | 2 ++ python/pythonmonkey/builtin_modules/url.js | 2 ++ python/pythonmonkey/helpers.py | 1 + python/pythonmonkey/newfile.py | 6 ++++++ python/pythonmonkey/require.py | 1 + src/BoolType.cc | 2 +- src/DateType.cc | 2 +- src/DictType.cc | 2 +- src/ExceptionType.cc | 2 +- src/FloatType.cc | 2 +- src/FuncType.cc | 2 +- src/IntType.cc | 2 +- src/JSArrayProxy.cc | 2 +- src/JobQueue.cc | 2 +- src/NoneType.cc | 2 +- src/NullType.cc | 2 +- src/PromiseType.cc | 2 +- src/PyEventLoop.cc | 2 +- src/PyListProxyHandler.cc | 2 +- src/PyType.cc | 2 +- src/StrType.cc | 2 +- src/TupleType.cc | 2 +- src/setSpiderMonkeyException.cc | 2 +- 53 files changed, 59 insertions(+), 45 deletions(-) create mode 100644 python/pythonmonkey/newfile.py diff --git a/include/BoolType.hh b/include/BoolType.hh index 97274383..b282d70b 100644 --- a/include/BoolType.hh +++ b/include/BoolType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python bools * @date 2022-12-02 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/DateType.hh b/include/DateType.hh index 55ee1d6d..b213f2fe 100644 --- a/include/DateType.hh +++ b/include/DateType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python dates * @date 2022-12-21 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/DictType.hh b/include/DictType.hh index 1d2f0516..0f4a9415 100644 --- a/include/DictType.hh +++ b/include/DictType.hh @@ -4,7 +4,7 @@ * @brief Struct representing python dictionaries * @date 2022-08-10 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/ExceptionType.hh b/include/ExceptionType.hh index 7cff9952..f94a0fe5 100644 --- a/include/ExceptionType.hh +++ b/include/ExceptionType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing Python Exception objects from a corresponding JS Error object * @date 2023-04-11 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/FloatType.hh b/include/FloatType.hh index 079ebca2..fb0620fa 100644 --- a/include/FloatType.hh +++ b/include/FloatType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python floats * @date 2022-12-02 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/FuncType.hh b/include/FuncType.hh index 1fb7e973..2d1e3979 100644 --- a/include/FuncType.hh +++ b/include/FuncType.hh @@ -4,7 +4,7 @@ * @brief Struct representing python functions * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/IntType.hh b/include/IntType.hh index 0cf2634a..0f7bee4a 100644 --- a/include/IntType.hh +++ b/include/IntType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python ints * @date 2023-03-16 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSObjectItemsProxy.hh b/include/JSObjectItemsProxy.hh index 91c8bc72..0584ad33 100644 --- a/include/JSObjectItemsProxy.hh +++ b/include/JSObjectItemsProxy.hh @@ -2,7 +2,6 @@ * @file JSObjectItemsProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectItemsProxy is a custom C-implemented python type that derives from dict items - * @version 0.1 * @date 2024-01-19 * * Copyright (c) 2024 Distributive Corp. diff --git a/include/JSObjectIterProxy.hh b/include/JSObjectIterProxy.hh index d6fb1452..d91ec489 100644 --- a/include/JSObjectIterProxy.hh +++ b/include/JSObjectIterProxy.hh @@ -2,7 +2,6 @@ * @file JSObjectIterProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectIterProxy is a custom C-implemented python type that derives from PyDictIterKey - * @version 0.1 * @date 2024-01-17 * * Copyright (c) 2024 Distributive Corp. diff --git a/include/JSObjectKeysProxy.hh b/include/JSObjectKeysProxy.hh index 1dea37be..59ed72af 100644 --- a/include/JSObjectKeysProxy.hh +++ b/include/JSObjectKeysProxy.hh @@ -2,7 +2,6 @@ * @file JSObjectKeysProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectKeysProxy is a custom C-implemented python type that derives from dict keys - * @version 0.1 * @date 2024-01-16 * * Copyright (c) 2024 Distributive Corp. diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 9e292e6a..04da6ab0 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -2,7 +2,6 @@ * @file JSObjectProxy.hh * @author Caleb Aikens (caleb@distributive.network) & Tom Tang (xmader@distributive.network) * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. - * @version 0.1 * @date 2023-06-26 * * Copyright (c) 2023 Distributive Corp. diff --git a/include/JSObjectValuesProxy.hh b/include/JSObjectValuesProxy.hh index 238d3066..0582e25b 100644 --- a/include/JSObjectValuesProxy.hh +++ b/include/JSObjectValuesProxy.hh @@ -2,8 +2,7 @@ * @file JSObjectValuesProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectValuesProxy is a custom C-implemented python type that derives from dict values - * @version 0.1 - * @date 2023-06-26 + * @date 2024-01-17 * * Copyright (c) 2023 Distributive Corp. * diff --git a/include/JobQueue.hh b/include/JobQueue.hh index ef68bf7e..d1d0a09e 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -4,7 +4,7 @@ * @brief Implement the ECMAScript Job Queue * @date 2023-04-03 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/ListType.hh b/include/ListType.hh index 28842a66..59004458 100644 --- a/include/ListType.hh +++ b/include/ListType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python lists * @date 2022-08-18 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/NoneType.hh b/include/NoneType.hh index bfd34169..c8b8b54d 100644 --- a/include/NoneType.hh +++ b/include/NoneType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing None * @date 2023-02-22 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/NullType.hh b/include/NullType.hh index cdc5066f..35b539b7 100644 --- a/include/NullType.hh +++ b/include/NullType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing JS null in a python object * @date 2023-02-22 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/PromiseType.hh b/include/PromiseType.hh index df8becea..d6992a8f 100644 --- a/include/PromiseType.hh +++ b/include/PromiseType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing Promises * @date 2023-03-29 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/PyEventLoop.hh b/include/PyEventLoop.hh index dd833a04..3bbcbbb3 100644 --- a/include/PyEventLoop.hh +++ b/include/PyEventLoop.hh @@ -4,7 +4,7 @@ * @brief Send jobs to the Python event-loop * @date 2023-04-05 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/PyType.hh b/include/PyType.hh index e90180d4..33cc675c 100644 --- a/include/PyType.hh +++ b/include/PyType.hh @@ -4,7 +4,7 @@ * @brief Struct representing python types * @date 2022-07-27 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/StrType.hh b/include/StrType.hh index 409d52be..1e7c37dc 100644 --- a/include/StrType.hh +++ b/include/StrType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python strings * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/TupleType.hh b/include/TupleType.hh index e2b66e78..c406fc87 100644 --- a/include/TupleType.hh +++ b/include/TupleType.hh @@ -4,7 +4,7 @@ * @brief Struct for representing python tuples * @date 2022-08-19 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/jsTypeFactory.hh b/include/jsTypeFactory.hh index e3af11cc..a02dfcdb 100644 --- a/include/jsTypeFactory.hh +++ b/include/jsTypeFactory.hh @@ -4,7 +4,7 @@ * @brief * @date 2023-02-15 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/modules/pythonmonkey/pythonmonkey.hh b/include/modules/pythonmonkey/pythonmonkey.hh index 7f964bce..2c3bb2d7 100644 --- a/include/modules/pythonmonkey/pythonmonkey.hh +++ b/include/modules/pythonmonkey/pythonmonkey.hh @@ -2,10 +2,9 @@ * @file pythonmonkey.hh * @author Caleb Aikens (caleb@kingsds.network) * @brief This file defines the pythonmonkey module, along with its various functions. - * @version 0.1 * @date 2022-09-06 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ #ifndef PythonMonkey_Module_PythonMonkey diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index ecdaeac6..71c6abab 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -4,7 +4,7 @@ * @brief Function for wrapping arbitrary PyObjects into the appropriate PyType class, and coercing JS types to python types * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/include/setSpiderMonkeyException.hh b/include/setSpiderMonkeyException.hh index 5c24e794..aa388ca9 100644 --- a/include/setSpiderMonkeyException.hh +++ b/include/setSpiderMonkeyException.hh @@ -4,7 +4,7 @@ * @brief Call this function whenever a JS_* function call fails in order to set an appropriate python exception (remember to also return NULL) * @date 2023-02-28 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py b/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py index 0b19043f..7ab5ed24 100644 --- a/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py +++ b/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py @@ -2,6 +2,7 @@ # @brief internal helper functions for XMLHttpRequest # @author Tom Tang # @date August 2023 +# @copyright Copyright (c) 2023 Distributive Corp. import asyncio import aiohttp diff --git a/python/pythonmonkey/builtin_modules/XMLHttpRequest.js b/python/pythonmonkey/builtin_modules/XMLHttpRequest.js index efdea02e..03a233ae 100644 --- a/python/pythonmonkey/builtin_modules/XMLHttpRequest.js +++ b/python/pythonmonkey/builtin_modules/XMLHttpRequest.js @@ -4,6 +4,8 @@ * * @author Tom Tang * @date August 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ const { EventTarget, Event } = require('event-target'); diff --git a/python/pythonmonkey/builtin_modules/base64.py b/python/pythonmonkey/builtin_modules/base64.py index 196f48ff..7239911b 100644 --- a/python/pythonmonkey/builtin_modules/base64.py +++ b/python/pythonmonkey/builtin_modules/base64.py @@ -1,6 +1,7 @@ # @file base64.py # @author Tom Tang , Hamada Gasmallah # @date July 2023 +# @copyright Copyright (c) 2023 Distributive Corp. import base64 diff --git a/python/pythonmonkey/builtin_modules/console.js b/python/pythonmonkey/builtin_modules/console.js index 7bf6e2b4..4cdca634 100644 --- a/python/pythonmonkey/builtin_modules/console.js +++ b/python/pythonmonkey/builtin_modules/console.js @@ -2,6 +2,8 @@ * @file console.js * @author Tom Tang * @date June 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ const { customInspectSymbol, format } = require("util"); diff --git a/python/pythonmonkey/builtin_modules/event-target.js b/python/pythonmonkey/builtin_modules/event-target.js index b978c049..c2b1a7fb 100644 --- a/python/pythonmonkey/builtin_modules/event-target.js +++ b/python/pythonmonkey/builtin_modules/event-target.js @@ -4,6 +4,8 @@ * @see https://dom.spec.whatwg.org/#eventtarget * @author Tom Tang * @date August 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ /** diff --git a/python/pythonmonkey/builtin_modules/timers.js b/python/pythonmonkey/builtin_modules/timers.js index 862bccc1..51cd48c5 100644 --- a/python/pythonmonkey/builtin_modules/timers.js +++ b/python/pythonmonkey/builtin_modules/timers.js @@ -2,6 +2,8 @@ * @file timers.js * @author Tom Tang * @date May 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ const internalBinding = require('internal-binding'); diff --git a/python/pythonmonkey/builtin_modules/url.js b/python/pythonmonkey/builtin_modules/url.js index 8d13806f..1419b5b1 100644 --- a/python/pythonmonkey/builtin_modules/url.js +++ b/python/pythonmonkey/builtin_modules/url.js @@ -4,6 +4,8 @@ * * @author Tom Tang * @date August 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ // Apply polyfills from core-js diff --git a/python/pythonmonkey/helpers.py b/python/pythonmonkey/helpers.py index a491cb57..638a76a0 100644 --- a/python/pythonmonkey/helpers.py +++ b/python/pythonmonkey/helpers.py @@ -5,6 +5,7 @@ # @author Wes Garland, wes@distributive.network # @date July 2023 # +# @copyright Copyright (c) 2023 Distributive Corp. from . import pythonmonkey as pm evalOpts = { 'filename': __file__, 'fromPythonFrame': True } diff --git a/python/pythonmonkey/newfile.py b/python/pythonmonkey/newfile.py new file mode 100644 index 00000000..484eb717 --- /dev/null +++ b/python/pythonmonkey/newfile.py @@ -0,0 +1,6 @@ +import pythonmonkey as pm + +#likes = pm.eval('({"color": "blue", "fruit": "apple", "pet": "dog"})') + +CryptoJS = pm.require('crypto-js') +CryptoJS diff --git a/python/pythonmonkey/require.py b/python/pythonmonkey/require.py index a19ba2be..8f56ec16 100644 --- a/python/pythonmonkey/require.py +++ b/python/pythonmonkey/require.py @@ -22,6 +22,7 @@ # @author Wes Garland, wes@distributive.network # @date May 2023 # +# @copyright Copyright (c) 2023 Distributive Corp. import sys, os, io from typing import Union, Dict, Literal, List diff --git a/src/BoolType.cc b/src/BoolType.cc index 2e70b472..b45d5f7d 100644 --- a/src/BoolType.cc +++ b/src/BoolType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing python bools * @date 2022-12-02 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/DateType.cc b/src/DateType.cc index 9aebee8b..a2708955 100644 --- a/src/DateType.cc +++ b/src/DateType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing python dates * @date 2022-12-21 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/DictType.cc b/src/DictType.cc index eaf2ca65..c24eabf8 100644 --- a/src/DictType.cc +++ b/src/DictType.cc @@ -4,7 +4,7 @@ * @brief Struct representing python dictionaries * @date 2022-08-10 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index 6cb0b5bc..361d0d18 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing Python Exception objects from a corresponding JS Error object * @date 2023-04-11 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/FloatType.cc b/src/FloatType.cc index 1dcd4e51..bd7a74a8 100644 --- a/src/FloatType.cc +++ b/src/FloatType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing python floats * @date 2022-12-02 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/FuncType.cc b/src/FuncType.cc index 1ad47e90..fca424d5 100644 --- a/src/FuncType.cc +++ b/src/FuncType.cc @@ -4,7 +4,7 @@ * @brief Struct representing python functions * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/IntType.cc b/src/IntType.cc index b371f3b2..841e90eb 100644 --- a/src/IntType.cc +++ b/src/IntType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing python ints * @date 2023-03-16 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 774eee02..a5c555b9 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -4,7 +4,7 @@ * @brief JSArrayProxy is a custom C-implemented python type that derives from list. It acts as a proxy for JSArrays from Spidermonkey, and behaves like a list would. * @date 2023-11-22 * - * Copyright (c) 2023 Distributive Corp. + * Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/src/JobQueue.cc b/src/JobQueue.cc index ba6e5a66..6e8f6e7a 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -4,7 +4,7 @@ * @brief Implement the ECMAScript Job Queue * @date 2023-04-03 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/NoneType.cc b/src/NoneType.cc index 80ecc2b9..5c448c51 100644 --- a/src/NoneType.cc +++ b/src/NoneType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing None * @date 2023-02-22 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/NullType.cc b/src/NullType.cc index 0748fca0..a63e8e46 100644 --- a/src/NullType.cc +++ b/src/NullType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing JS null in a python object * @date 2023-02-22 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/PromiseType.cc b/src/PromiseType.cc index becf7e91..55ceefa7 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing Promises * @date 2023-03-29 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/PyEventLoop.cc b/src/PyEventLoop.cc index e9ce4df7..160105a8 100644 --- a/src/PyEventLoop.cc +++ b/src/PyEventLoop.cc @@ -4,7 +4,7 @@ * @brief Send jobs to the Python event-loop * @date 2023-04-05 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 436c43d2..dacb92f3 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2052,7 +2052,7 @@ void PyListProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { * Note that self->ob_item may change, and even if newsize is less * than ob_size on entry. */ -//private +// private static int list_resize(PyListObject *self, Py_ssize_t newsize) { diff --git a/src/PyType.cc b/src/PyType.cc index d9881c42..abbcdb9e 100644 --- a/src/PyType.cc +++ b/src/PyType.cc @@ -4,7 +4,7 @@ * @brief Struct representing python types * @date 2022-07-27 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/StrType.cc b/src/StrType.cc index 3f412301..bd2c0176 100644 --- a/src/StrType.cc +++ b/src/StrType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing python strings * @date 2022-08-08 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/TupleType.cc b/src/TupleType.cc index 9b2046d7..f3da52fe 100644 --- a/src/TupleType.cc +++ b/src/TupleType.cc @@ -4,7 +4,7 @@ * @brief Struct for representing python tuples * @date 2022-08-19 * - * @copyright Copyright (c) 2022 + * @copyright Copyright (c) 2022 Distributive Corp. * */ diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index 285f68b6..9826771f 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -4,7 +4,7 @@ * @brief Call this function whenever a JS_* function call fails in order to set an appropriate python exception (remember to also return NULL) * @date 2023-02-28 * - * @copyright Copyright (c) 2023 + * @copyright Copyright (c) 2023 Distributive Corp. * */ From 1884f79f6557da0eb30dda62f645d5fd35dbd9a4 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 14:13:44 -0500 Subject: [PATCH 050/170] uniform copyright headers --- include/JSArrayIterProxy.hh | 2 +- include/JSArrayProxy.hh | 2 +- include/JSFunctionProxy.hh | 2 +- include/JSMethodProxy.hh | 2 +- include/JSObjectItemsProxy.hh | 2 +- include/JSObjectIterProxy.hh | 2 +- include/JSObjectKeysProxy.hh | 2 +- include/JSObjectProxy.hh | 2 +- include/JSObjectValuesProxy.hh | 2 +- include/JSStringProxy.hh | 2 +- include/PyBaseProxyHandler.hh | 2 +- include/PyDictProxyHandler.hh | 2 +- include/PyListProxyHandler.hh | 2 +- include/PyObjectProxyHandler.hh | 2 +- src/JSArrayIterProxy.cc | 2 +- src/JSArrayProxy.cc | 2 +- src/JSFunctionProxy.cc | 2 +- src/JSMethodProxy.cc | 2 +- src/JSObjectItemsProxy.cc | 2 +- src/JSObjectIterProxy.cc | 2 +- src/JSObjectKeysProxy.cc | 2 +- src/JSObjectProxy.cc | 2 +- src/JSObjectValuesProxy.cc | 2 +- src/PyBaseProxyHandler.cc | 2 +- src/PyDictProxyHandler.cc | 2 +- src/PyListProxyHandler.cc | 2 +- src/PyObjectProxyHandler.cc | 2 +- src/internalBinding/timers.cc | 2 ++ src/internalBinding/utils.cc | 2 ++ 29 files changed, 31 insertions(+), 27 deletions(-) diff --git a/include/JSArrayIterProxy.hh b/include/JSArrayIterProxy.hh index a4a9e660..ff23e880 100644 --- a/include/JSArrayIterProxy.hh +++ b/include/JSArrayIterProxy.hh @@ -5,7 +5,7 @@ * @version 0.1 * @date 2024-01-15 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index ff2bb482..efc53afa 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -5,7 +5,7 @@ * @version 0.1 * @date 2023-11-22 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSFunctionProxy.hh b/include/JSFunctionProxy.hh index f0e95554..82109b63 100644 --- a/include/JSFunctionProxy.hh +++ b/include/JSFunctionProxy.hh @@ -4,7 +4,7 @@ * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. * @date 2023-09-28 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSMethodProxy.hh b/include/JSMethodProxy.hh index ffa02308..680db116 100644 --- a/include/JSMethodProxy.hh +++ b/include/JSMethodProxy.hh @@ -4,7 +4,7 @@ * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. * @date 2023-11-14 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSObjectItemsProxy.hh b/include/JSObjectItemsProxy.hh index 0584ad33..480a48ae 100644 --- a/include/JSObjectItemsProxy.hh +++ b/include/JSObjectItemsProxy.hh @@ -4,7 +4,7 @@ * @brief JSObjectItemsProxy is a custom C-implemented python type that derives from dict items * @date 2024-01-19 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectIterProxy.hh b/include/JSObjectIterProxy.hh index d91ec489..d8f46075 100644 --- a/include/JSObjectIterProxy.hh +++ b/include/JSObjectIterProxy.hh @@ -4,7 +4,7 @@ * @brief JSObjectIterProxy is a custom C-implemented python type that derives from PyDictIterKey * @date 2024-01-17 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectKeysProxy.hh b/include/JSObjectKeysProxy.hh index 59ed72af..2564bb3f 100644 --- a/include/JSObjectKeysProxy.hh +++ b/include/JSObjectKeysProxy.hh @@ -4,7 +4,7 @@ * @brief JSObjectKeysProxy is a custom C-implemented python type that derives from dict keys * @date 2024-01-16 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 04da6ab0..1bf8ef49 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -4,7 +4,7 @@ * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. * @date 2023-06-26 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSObjectValuesProxy.hh b/include/JSObjectValuesProxy.hh index 0582e25b..fbce05d1 100644 --- a/include/JSObjectValuesProxy.hh +++ b/include/JSObjectValuesProxy.hh @@ -4,7 +4,7 @@ * @brief JSObjectValuesProxy is a custom C-implemented python type that derives from dict values * @date 2024-01-17 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSStringProxy.hh b/include/JSStringProxy.hh index 35d9f7ed..8c82374c 100644 --- a/include/JSStringProxy.hh +++ b/include/JSStringProxy.hh @@ -4,7 +4,7 @@ * @brief JSStringProxy is a custom C-implemented python type that derives from str. It acts as a proxy for JSStrings from Spidermonkey, and behaves like a str would. * @date 2024-01-03 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/PyBaseProxyHandler.hh b/include/PyBaseProxyHandler.hh index 58993228..4f36188f 100644 --- a/include/PyBaseProxyHandler.hh +++ b/include/PyBaseProxyHandler.hh @@ -4,7 +4,7 @@ * @brief Structs for creating JS proxy objects. * @date 2023-04-20 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index d7843bd0..f8e1822b 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -4,7 +4,7 @@ * @brief Structs for creating JS proxy objects. Used by DictType for object coercion * @date 2023-04-20 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/include/PyListProxyHandler.hh b/include/PyListProxyHandler.hh index b94c9057..3c564165 100644 --- a/include/PyListProxyHandler.hh +++ b/include/PyListProxyHandler.hh @@ -4,7 +4,7 @@ * @brief Structs for creating JS proxy objects. Used by ListType for List coercion * @date 2023-12-01 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index ce003eea..2577d5ce 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -4,7 +4,7 @@ * @brief Structs for creating JS proxy objects. Used for default object coercion * @date 2024-01-25 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/JSArrayIterProxy.cc b/src/JSArrayIterProxy.cc index f63b278a..f7a73b36 100644 --- a/src/JSArrayIterProxy.cc +++ b/src/JSArrayIterProxy.cc @@ -4,7 +4,7 @@ * @brief JSArrayIterProxy is a custom C-implemented python type that derives from list iterator * @date 2024-01-15 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index a5c555b9..7febaa82 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -4,7 +4,7 @@ * @brief JSArrayProxy is a custom C-implemented python type that derives from list. It acts as a proxy for JSArrays from Spidermonkey, and behaves like a list would. * @date 2023-11-22 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index b23c35d9..f950b357 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -4,7 +4,7 @@ * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. * @date 2023-09-28 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index 5df87d48..ef05a497 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -4,7 +4,7 @@ * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. * @date 2023-11-14 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/JSObjectItemsProxy.cc b/src/JSObjectItemsProxy.cc index 39877255..117b7d4c 100644 --- a/src/JSObjectItemsProxy.cc +++ b/src/JSObjectItemsProxy.cc @@ -4,7 +4,7 @@ * @brief JSObjectItemsProxy is a custom C-implemented python type that derives from dict keys * @date 2024-01-19 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index 3b17d739..e720d496 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -4,7 +4,7 @@ * @brief JSObjectIterProxy is a custom C-implemented python type that derives from list iterator * @date 2024-01-17 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/src/JSObjectKeysProxy.cc b/src/JSObjectKeysProxy.cc index d4733c7b..ddb6d2b0 100644 --- a/src/JSObjectKeysProxy.cc +++ b/src/JSObjectKeysProxy.cc @@ -4,7 +4,7 @@ * @brief JSObjectKeysProxy is a custom C-implemented python type that derives from dict keys * @date 2024-01-18 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 77ed0eaf..6d476c2c 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -4,7 +4,7 @@ * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. * @date 2023-06-26 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/src/JSObjectValuesProxy.cc b/src/JSObjectValuesProxy.cc index a6dc5c45..ef83492f 100644 --- a/src/JSObjectValuesProxy.cc +++ b/src/JSObjectValuesProxy.cc @@ -4,7 +4,7 @@ * @brief JSObjectValuesProxy is a custom C-implemented python type that derives from dict values * @date 2024-01-19 * - * Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2024 Distributive Corp. * */ diff --git a/src/PyBaseProxyHandler.cc b/src/PyBaseProxyHandler.cc index 1e86eac5..6c6f33ae 100644 --- a/src/PyBaseProxyHandler.cc +++ b/src/PyBaseProxyHandler.cc @@ -4,7 +4,7 @@ * @brief Struct for creating JS proxy objects * @date 2023-04-20 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index af3902f7..1b18b822 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -4,7 +4,7 @@ * @brief Struct for creating JS proxy objects. Used by DictType for object coercion TODO * @date 2023-04-20 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index dacb92f3..3a204f64 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -4,7 +4,7 @@ * @brief Struct for creating JS proxy objects. Used by ListType for List coercion * @date 2023-12-01 * - * Copyright (c) 2023-2024 Distributive Corp. + * @copyright Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 4f523b10..1e3213f5 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -4,7 +4,7 @@ * @brief * @date 2024-01-30 * - * Copyright (c) 2023 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/src/internalBinding/timers.cc b/src/internalBinding/timers.cc index fc30df42..a37fae11 100644 --- a/src/internalBinding/timers.cc +++ b/src/internalBinding/timers.cc @@ -2,6 +2,8 @@ * @file timers.cc * @author Tom Tang (xmader@distributive.network) * @brief Implement functions in `internalBinding("timers")` + * + * @copyright Copyright (c) 2023 Distributive Corp. */ #include "include/internalBinding.hh" diff --git a/src/internalBinding/utils.cc b/src/internalBinding/utils.cc index 2104a264..ac61fc80 100644 --- a/src/internalBinding/utils.cc +++ b/src/internalBinding/utils.cc @@ -2,6 +2,8 @@ * @file utils.cc * @author Tom Tang (xmader@distributive.network) * @brief Implement functions in `internalBinding("utils")` + * + * @copyright Copyright (c) 2023 Distributive Corp. */ #include "include/internalBinding.hh" From bcf55ae709524b2bebac2e6997442c2fb736bfbd Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 14:21:07 -0500 Subject: [PATCH 051/170] last missing copyrights. license statements fixes --- include/modules/pythonmonkey/pythonmonkey.hh | 2 +- .../pythonmonkey/builtin_modules/dom-exception.js | 2 ++ python/pythonmonkey/builtin_modules/util.js | 13 ++++++------- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/modules/pythonmonkey/pythonmonkey.hh b/include/modules/pythonmonkey/pythonmonkey.hh index 2c3bb2d7..91fdf8c1 100644 --- a/include/modules/pythonmonkey/pythonmonkey.hh +++ b/include/modules/pythonmonkey/pythonmonkey.hh @@ -4,7 +4,7 @@ * @brief This file defines the pythonmonkey module, along with its various functions. * @date 2022-09-06 * - * @copyright Copyright (c) 2022 Distributive Corp. + * @copyright Copyright (c) 2022-2024 Distributive Corp. * */ #ifndef PythonMonkey_Module_PythonMonkey diff --git a/python/pythonmonkey/builtin_modules/dom-exception.js b/python/pythonmonkey/builtin_modules/dom-exception.js index d6cc2971..40cf427d 100644 --- a/python/pythonmonkey/builtin_modules/dom-exception.js +++ b/python/pythonmonkey/builtin_modules/dom-exception.js @@ -4,6 +4,8 @@ * * @author Tom Tang * @date August 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ // Apply polyfill from core-js diff --git a/python/pythonmonkey/builtin_modules/util.js b/python/pythonmonkey/builtin_modules/util.js index dbe2fccb..a482c191 100644 --- a/python/pythonmonkey/builtin_modules/util.js +++ b/python/pythonmonkey/builtin_modules/util.js @@ -5,6 +5,8 @@ * * @author Tom Tang * @date June 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ const internalBinding = require("internal-binding") @@ -84,7 +86,6 @@ function objectToString(o) { } // https://github.com/nodejs/node/blob/v8.17.0/lib/internal/util.js#L189-L202 -// MIT License function getConstructorOf(obj) { while (obj) { var descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor'); @@ -100,10 +101,8 @@ function getConstructorOf(obj) { return null; } -/*! - * Modified from https://github.com/nodejs/node/blob/v8.17.0/lib/util.js#L59-L852 - * Node.js - * MIT License, Copyright Joyent, Inc. and other Node contributors. +/*! Start verbatim Node.js + * https://github.com/nodejs/node/blob/v8.17.0/lib/util.js#L59-L852 */ const inspectDefaultOptions = Object.seal({ showHidden: false, @@ -896,11 +895,11 @@ function reduceToSingleString(ctx, output, base, braces, addLn) { } /*! - * End of Node.js code + * End of verbatim Node.js excerpt */ module.exports = exports = { customInspectSymbol, format, inspect, -}; +}; \ No newline at end of file From e2f446e59c6083c80ec851847afda3c3184cca1a Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 7 Feb 2024 16:09:49 -0500 Subject: [PATCH 052/170] improved comment --- src/PyListProxyHandler.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 3a204f64..55e32ba8 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2136,7 +2136,7 @@ bool PyListProxyHandler::defineProperty( JS::RootedValue itemV(cx, desc.value()); PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { - // expand + // we are out-of-bounds and need to expand Py_XINCREF(item); Py_ssize_t oldLen = PyList_GET_SIZE(pyObject); if (list_resize((PyListObject *)pyObject, index + 1) < 0) { @@ -2144,6 +2144,7 @@ bool PyListProxyHandler::defineProperty( return result.failBadIndex(); } PyList_SET_ITEM((PyListObject *)pyObject, index, item); + // fill the space until the inserted index for (Py_ssize_t i = oldLen; i < index; i++) { Py_INCREF(Py_None); PyList_SET_ITEM((PyListObject *)pyObject, i, Py_None); From 5153405399d17f755e422c02f12976c442f19fde Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 8 Feb 2024 09:59:06 -0500 Subject: [PATCH 053/170] logic improvement --- src/PyListProxyHandler.cc | 87 ++----------------------------------- tests/python/test_arrays.py | 14 +++--- 2 files changed, 10 insertions(+), 91 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 55e32ba8..14b0251c 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2039,82 +2039,6 @@ void PyListProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { } } -/* Ensure ob_item has room for at least newsize elements, and set - * ob_size to newsize. If newsize > ob_size on entry, the content - * of the new slots at exit is undefined heap trash; it's the caller's - * responsibility to overwrite them with sane values. - * The number of allocated elements may grow, shrink, or stay the same. - * Failure is impossible if newsize <= self.allocated on entry, although - * that partly relies on an assumption that the system realloc() never - * fails when passed a number of bytes <= the number of bytes last - * allocated (the C standard doesn't guarantee this, but it's hard to - * imagine a realloc implementation where it wouldn't be true). - * Note that self->ob_item may change, and even if newsize is less - * than ob_size on entry. - */ -// private -static int -list_resize(PyListObject *self, Py_ssize_t newsize) -{ - PyObject **items; - size_t new_allocated, num_allocated_bytes; - Py_ssize_t allocated = self->allocated; - - /* Bypass realloc() when a previous overallocation is large enough - to accommodate the newsize. If the newsize falls lower than half - the allocated size, then proceed with the realloc() to shrink the list. - */ - if (allocated >= newsize && newsize >= (allocated >> 1)) { - assert(self->ob_item != NULL || newsize == 0); - #if PY_VERSION_HEX >= 0x03090000 // 3.9 - Py_SET_SIZE(self, newsize); - #else - Py_SIZE(self) = newsize; - #endif - return 0; - } - - /* This over-allocates proportional to the list size, making room - * for additional growth. The over-allocation is mild, but is - * enough to give linear-time amortized behavior over a long - * sequence of appends() in the presence of a poorly-performing - * system realloc(). - * Add padding to make the allocated size multiple of 4. - * The growth pattern is: 0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ... - * Note: new_allocated won't overflow because the largest possible value - * is PY_SSIZE_T_MAX * (9 / 8) + 6 which always fits in a size_t. - */ - new_allocated = ((size_t)newsize + (newsize >> 3) + 6) & ~(size_t)3; - /* Do not overallocate if the new size is closer to overallocated size - * than to the old size. - */ - if (newsize - Py_SIZE(self) > (Py_ssize_t)(new_allocated - newsize)) - new_allocated = ((size_t)newsize + 3) & ~(size_t)3; - - if (newsize == 0) - new_allocated = 0; - if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) { - num_allocated_bytes = new_allocated * sizeof(PyObject *); - items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes); - } - else { - // integer overflow - items = NULL; - } - if (items == NULL) { - PyErr_NoMemory(); - return -1; - } - self->ob_item = items; - #if PY_VERSION_HEX >= 0x03090000 // 3.9 - Py_SET_SIZE(self, newsize); - #else - Py_SIZE(self) = newsize; - #endif - self->allocated = new_allocated; - return 0; -} - bool PyListProxyHandler::defineProperty( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, JS::ObjectOpResult &result @@ -2137,18 +2061,13 @@ bool PyListProxyHandler::defineProperty( PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { // we are out-of-bounds and need to expand - Py_XINCREF(item); Py_ssize_t oldLen = PyList_GET_SIZE(pyObject); - if (list_resize((PyListObject *)pyObject, index + 1) < 0) { - Py_XDECREF(item); - return result.failBadIndex(); - } - PyList_SET_ITEM((PyListObject *)pyObject, index, item); // fill the space until the inserted index for (Py_ssize_t i = oldLen; i < index; i++) { - Py_INCREF(Py_None); - PyList_SET_ITEM((PyListObject *)pyObject, i, Py_None); + PyList_Append(pyObject, Py_None); } + // insert the item + PyList_Append(pyObject, item); // clear pending exception PyErr_Clear(); diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 44bd79a8..e8bb96b4 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -1769,19 +1769,19 @@ def test_array_from(): assert result[0] is not items # bad index size expansion -def test_assign_generic_dict_bad_index(): - items = [1,2,3] - result = [] - pm.eval("(result, arr) => {result[0] = arr[Symbol.iterator]() }")(result, items) - assert repr(result) == "[]" - def test_assign_bad_index(): items = [1,2,3] result = [] pm.eval("(result, arr) => {result[0] = 4}")(result, items) assert result[0] == 4 -def test_assign_bad_index(): +def test_assign_bad_index_with_existing_next(): + items = [1,2,3] + result = [8] + pm.eval("(result, arr) => {result[1] = 4}")(result, items) + assert result == [8,4] + +def test_assign_bad_index_with_gap(): items = [1,2,3] result = [] pm.eval("(result, arr) => {result[0] = 4; result[5] = 6}")(result, items) From 454b943cd70490889f40e7af05bf19db0105b3c9 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 8 Feb 2024 10:00:17 -0500 Subject: [PATCH 054/170] improved naming --- src/PyListProxyHandler.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 14b0251c..45b62e4b 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2061,9 +2061,9 @@ bool PyListProxyHandler::defineProperty( PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { // we are out-of-bounds and need to expand - Py_ssize_t oldLen = PyList_GET_SIZE(pyObject); + Py_ssize_t len = PyList_GET_SIZE(pyObject); // fill the space until the inserted index - for (Py_ssize_t i = oldLen; i < index; i++) { + for (Py_ssize_t i = len; i < index; i++) { PyList_Append(pyObject, Py_None); } // insert the item From a26680ea1fc55cfc64da8bb7db1f93996ce2d5e7 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 8 Feb 2024 13:34:55 -0500 Subject: [PATCH 055/170] cleanup --- src/PyListProxyHandler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 45b62e4b..7bc51c28 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -2066,7 +2066,7 @@ bool PyListProxyHandler::defineProperty( for (Py_ssize_t i = len; i < index; i++) { PyList_Append(pyObject, Py_None); } - // insert the item + PyList_Append(pyObject, item); // clear pending exception From 20013a405bbd7cd803ccd165d0b108e21240adcc Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 8 Feb 2024 14:46:59 -0500 Subject: [PATCH 056/170] project at advanced stage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6586eeaa..d1e3102b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ using the Python engine to provide the Javascript host environment. We feature JavaScript Array and Object methods implemented on Python List and Dictionaries using the cPython C API, and the inverse using the Mozilla Firefox Spidermonkey JavaScript C++ API. -This product is in an intermediate stage, approximately 90% to MVP as of January 2024. It is under active development by [Distributive](https://distributive.network/). +This product is in an advanced stage, approximately 90% to MVP as of January 2024. It is under active development by [Distributive](https://distributive.network/). External contributions and feedback are welcome and encouraged. ### tl;dr From d946421c2ef75e10f7c7f527f0d473bb087b3cde Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 8 Feb 2024 15:13:29 -0500 Subject: [PATCH 057/170] copyright headers missing and documentation cleanup --- README.md | 3 --- .../XMLHttpRequest-internal.d.ts | 2 ++ .../pythonmonkey/builtin_modules/base64.d.ts | 2 ++ .../builtin_modules/dom-exception.d.ts | 5 ---- .../builtin_modules/internal-binding.d.ts | 2 ++ python/pythonmonkey/pythonmonkey.pyi | 24 +------------------ 6 files changed, 7 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index d1e3102b..7a26808b 100644 --- a/README.md +++ b/README.md @@ -149,9 +149,6 @@ These methods are exported from the pythonmonkey module. * isCompilableUnit(code) * collect() * bigint(int) -* `SpiderMonkeyError` -* `JSObjectProxy` -* `null` See definitions in [python/pythonmonkey/pythonmonkey.pyi](https://github.com/Distributive-Network/PythonMonkey/blob/main/python/pythonmonkey/pythonmonkey.pyi). diff --git a/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.d.ts b/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.d.ts index d6b88f56..bdb159b0 100644 --- a/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.d.ts +++ b/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.d.ts @@ -3,6 +3,8 @@ * @brief TypeScript type declarations for the internal XMLHttpRequest helpers * @author Tom Tang * @date August 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ /** diff --git a/python/pythonmonkey/builtin_modules/base64.d.ts b/python/pythonmonkey/builtin_modules/base64.d.ts index a1a3c38a..6575386f 100644 --- a/python/pythonmonkey/builtin_modules/base64.d.ts +++ b/python/pythonmonkey/builtin_modules/base64.d.ts @@ -3,6 +3,8 @@ * @brief TypeScript type declarations for base64.py * @author Tom Tang * @date July 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ /** diff --git a/python/pythonmonkey/builtin_modules/dom-exception.d.ts b/python/pythonmonkey/builtin_modules/dom-exception.d.ts index 849c1882..e37f747b 100644 --- a/python/pythonmonkey/builtin_modules/dom-exception.d.ts +++ b/python/pythonmonkey/builtin_modules/dom-exception.d.ts @@ -2,11 +2,6 @@ * @file dom-exception.d.ts * Type definitions for DOMException * - * @author Tom Tang - * @date August 2023 - */ - -/*! * Copied from https://www.npmjs.com/package/@types/web * Apache License 2.0 */ diff --git a/python/pythonmonkey/builtin_modules/internal-binding.d.ts b/python/pythonmonkey/builtin_modules/internal-binding.d.ts index 3fbf441e..ec01c598 100644 --- a/python/pythonmonkey/builtin_modules/internal-binding.d.ts +++ b/python/pythonmonkey/builtin_modules/internal-binding.d.ts @@ -2,6 +2,8 @@ * @file internal-binding.d.ts * @author Tom Tang * @date June 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ /** diff --git a/python/pythonmonkey/pythonmonkey.pyi b/python/pythonmonkey/pythonmonkey.pyi index 15bed09d..3c0c1c06 100644 --- a/python/pythonmonkey/pythonmonkey.pyi +++ b/python/pythonmonkey/pythonmonkey.pyi @@ -1,5 +1,4 @@ """ -stub file for type hints & documentations for the native module @see https://typing.readthedocs.io/en/latest/source/stubs.html """ @@ -48,25 +47,4 @@ def internalBinding(namespace: str) -> JSObjectProxy: def collect() -> None: """ Calls the spidermonkey garbage collector - """ - -class bigint(int): - """ - Representing JavaScript BigInt in Python - """ - -class SpiderMonkeyError(Exception): - """ - Representing a corresponding JS Error in Python - """ - -class JSObjectProxy(dict): - """ - JavaScript Object proxy dict - """ - def __init__(self) -> None: ... - -null = _typing.Annotated[ - _typing.NewType("pythonmonkey.null", object), - "Representing the JS null type in Python using a singleton object", -] + """ \ No newline at end of file From 1f42778740b89e8f6947b13b2c6b4c80267f298c Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 8 Feb 2024 15:53:15 -0500 Subject: [PATCH 058/170] further cleanup and missing copyrights --- build.py | 2 ++ python/pythonmonkey/lib/pmdb.py | 1 + python/pythonmonkey/lib/pmjs/global-init.js | 2 ++ python/pythonmonkey/pythonmonkey.pyi | 7 ------- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/build.py b/build.py index 36a0d26c..0d10b2bc 100644 --- a/build.py +++ b/build.py @@ -2,7 +2,9 @@ # Main PythonMonkey build automation script. Run with `poetry build`. # @author Hamada Gasmallah, hamada@distributive.network # @date April 2023 +# @copyright Copyright (c) 2023 Distributive Corp. # + import subprocess import os, sys import platform diff --git a/python/pythonmonkey/lib/pmdb.py b/python/pythonmonkey/lib/pmdb.py index 13a6565f..a79fe823 100644 --- a/python/pythonmonkey/lib/pmdb.py +++ b/python/pythonmonkey/lib/pmdb.py @@ -1,6 +1,7 @@ # @file pmdb - A gdb-like JavaScript debugger interface # @author Tom Tang # @date July 2023 +# @copyright Copyright (c) 2023 Distributive Corp. import pythonmonkey as pm diff --git a/python/pythonmonkey/lib/pmjs/global-init.js b/python/pythonmonkey/lib/pmjs/global-init.js index db78892c..12b6618e 100644 --- a/python/pythonmonkey/lib/pmjs/global-init.js +++ b/python/pythonmonkey/lib/pmjs/global-init.js @@ -5,6 +5,8 @@ * * @author Wes Garland, wes@distributive.network * @date June 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ 'use strict'; diff --git a/python/pythonmonkey/pythonmonkey.pyi b/python/pythonmonkey/pythonmonkey.pyi index 3c0c1c06..97bf87ea 100644 --- a/python/pythonmonkey/pythonmonkey.pyi +++ b/python/pythonmonkey/pythonmonkey.pyi @@ -37,13 +37,6 @@ def isCompilableUnit(code: str) -> bool: Hint if a string might be compilable Javascript without actual evaluation """ -def internalBinding(namespace: str) -> JSObjectProxy: - """ - INTERNAL USE ONLY - - See function declarations in ./builtin_modules/internal-binding.d.ts - """ - def collect() -> None: """ Calls the spidermonkey garbage collector From c484f7e33244f9974774239ef27c7b407c8a36d3 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 8 Feb 2024 16:02:22 -0500 Subject: [PATCH 059/170] more missing copyrights --- python/pythonmonkey/global.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/pythonmonkey/global.d.ts b/python/pythonmonkey/global.d.ts index 03321ed5..16986691 100644 --- a/python/pythonmonkey/global.d.ts +++ b/python/pythonmonkey/global.d.ts @@ -2,6 +2,8 @@ * @file global.d.ts * @author Tom Tang * @date May 2023 + * + * @copyright Copyright (c) 2023 Distributive Corp. */ declare const python: { From 7ed102284042c7bc378e4fff94d6a7828633e825 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:23:44 -0500 Subject: [PATCH 060/170] chore(JSFunctionProxy): remove unnecessary return on JSFunctionProxy_dealloc --- src/JSFunctionProxy.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index e1da2afc..11bb89bd 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -22,7 +22,6 @@ void JSFunctionProxyMethodDefinitions::JSFunctionProxy_dealloc(JSFunctionProxy *self) { delete self->jsFunc; - return; } PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) { From 968ef9c6bcb06e7b20b550bf82d58b6e6aa0cf3f Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:24:48 -0500 Subject: [PATCH 061/170] refactor(JSFunctionProxy): remove loop invariant in JSFunctionProxy_call, renaming of variables --- src/JSFunctionProxy.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index 11bb89bd..0aea3c20 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -35,12 +35,13 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_new(PyTypeObject *su PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, PyObject *args, PyObject *kwargs) { JSContext *cx = GLOBAL_CX; JS::RootedValue jsFunc(GLOBAL_CX, JS::ObjectValue(**((JSFunctionProxy *)self)->jsFunc)); - JSObject *o = jsFunc.toObjectOrNull(); - JS::RootedObject thisObj(GLOBAL_CX, JS::GetNonCCWObjectGlobal(o)); + JSObject *jsFuncObj = jsFunc.toObjectOrNull(); + JS::RootedObject thisObj(GLOBAL_CX, JS::GetNonCCWObjectGlobal(jsFuncObj)); // if jsFunc is not bound, assume `this` is `globalThis` JS::RootedVector jsArgsVector(cx); - for (size_t i = 0; i < PyTuple_Size(args); i++) { + Py_ssize_t nargs = PyTuple_Size(args); + for (size_t i = 0; i < nargs; i++) { JS::Value jsValue = jsTypeFactory(cx, PyTuple_GetItem(args, i)); if (PyErr_Occurred()) { // Check if an exception has already been set in the flow of control return NULL; // Fail-fast From 4ce69b133474db78ad349c1223ef59e198138706 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:26:50 -0500 Subject: [PATCH 062/170] refactor(PyObjectProxyHandler): remove one-use variable --- src/PyObjectProxyHandler.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index dc39c8e0..ade1ebd3 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -32,8 +32,7 @@ bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy PyObject *nonDunderKeys = PyList_New(0); for (size_t i = 0; i < keysLength; i++) { PyObject *key = PyList_GetItem(keys, i); - PyObject *isDunder = PyObject_CallMethod(key, "startswith", "(s)", "__"); - if (Py_IsFalse(isDunder)) { // if key starts with "__", ignore it + if (Py_IsFalse(PyObject_CallMethod(key, "startswith", "(s)", "__"))) { // if key starts with "__", ignore it PyList_Append(nonDunderKeys, key); } } From f4ba03ac9455e72d564d9da3c72212a9cdc40063 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:30:46 -0500 Subject: [PATCH 063/170] feat(PyObjectProxyHandler): set Object prototype on PyObjectProxyHandler so instanceOf(Object) works --- src/jsTypeFactory.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 0d1f37b3..6e95c5fc 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -230,8 +230,9 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { } else { JS::RootedValue v(cx); - JSObject *proxy; - proxy = js::NewProxyObject(cx, new PyObjectProxyHandler(object), v, NULL); + JS::RootedObject objectPrototype(cx); + JS_GetClassPrototype(cx, JSProto_Object, &objectPrototype); // so that instanceof will work, not that prototype methods will + JSObject *proxy = js::NewProxyObject(cx, new PyObjectProxyHandler(object), v, objectPrototype.get()); Py_INCREF(object); JS::SetReservedSlot(proxy, PyObjectSlot, JS::PrivateValue(object)); returnType.setObject(*proxy); From 251d9cc3166ff688f1447826ee019b92acfef8f4 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:41:44 -0500 Subject: [PATCH 064/170] fix(JSMethodProxy): correctly increment JSMethodProxy when accessing from JavaScript --- src/jsTypeFactory.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 6e95c5fc..4f0a933f 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -194,6 +194,8 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { registerArgs[1].setPrivate(object); JS::RootedValue ignoredOutVal(GLOBAL_CX); JS_CallFunctionName(GLOBAL_CX, *jsFunctionRegistry, "register", registerArgs, &ignoredOutVal); + + Py_INCREF(object); } else if (PyObject_TypeCheck(object, &JSFunctionProxyType)) { returnType.setObject(**((JSFunctionProxy *)object)->jsFunc); From 98b21d64f2a0657e1f6ae97bbec1554544402782 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:47:25 -0500 Subject: [PATCH 065/170] feat(PyObjectProxyHandler): store proxied PyObject in reserved slot --- src/PyObjectProxyHandler.cc | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index ade1ebd3..a4b067e9 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -26,7 +26,8 @@ const char PyObjectProxyHandler::family = 0; bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { - PyObject *keys = PyObject_Dir(pyObject); + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + PyObject *keys = PyObject_Dir(self); size_t keysLength = PyList_Size(keys); PyObject *nonDunderKeys = PyList_New(0); @@ -58,7 +59,8 @@ bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy bool PyObjectProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult &result) const { PyObject *attrName = idToKey(cx, id); - if (PyObject_SetAttr(pyObject, attrName, NULL) < 0) { + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (PyObject_SetAttr(self, attrName, NULL) < 0) { return result.failCantDelete(); // raises JS exception } return result.succeed(); @@ -74,7 +76,8 @@ bool PyObjectProxyHandler::getOwnPropertyDescriptor( JS::MutableHandle> desc ) const { PyObject *attrName = idToKey(cx, id); - PyObject *item = PyObject_GetAttr(pyObject, attrName); + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + PyObject *item = PyObject_GetAttr(self, attrName); if (!item) { // NULL if the key is not present desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys } else { @@ -94,7 +97,8 @@ bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::Handle JS::RootedValue *rootedV = new JS::RootedValue(cx, v); PyObject *attrName = idToKey(cx, id); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (PyObject_SetAttr(self, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); @@ -108,7 +112,8 @@ bool PyObjectProxyHandler::enumerate(JSContext *cx, JS::HandleObject proxy, bool PyObjectProxyHandler::hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const { PyObject *attrName = idToKey(cx, id); - *bp = PyObject_HasAttr(pyObject, attrName) == 1; + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + *bp = PyObject_HasAttr(self, attrName) == 1; return true; } @@ -122,8 +127,9 @@ void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { // We cannot call Py_DECREF here when shutting down as the thread state is gone. // Then, when shutting down, there is only on reference left, and we don't need // to free the object since the entire process memory is being released. - if (Py_REFCNT(pyObject) > 1) { - Py_DECREF(pyObject); + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (Py_REFCNT(self) > 1) { + Py_DECREF(self); } } From b30b57069d48ff3ef7b2dcc2bbea5bc2dbc59a90 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 9 Feb 2024 11:48:20 -0500 Subject: [PATCH 066/170] can now assign arguments directly from sys.arg --- python/pythonmonkey/cli/pmjs.py | 11 +++++------ python/pythonmonkey/lib/pmjs/global-init.js | 16 ---------------- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/python/pythonmonkey/cli/pmjs.py b/python/pythonmonkey/cli/pmjs.py index f1bd1ac9..1da0a9fa 100755 --- a/python/pythonmonkey/cli/pmjs.py +++ b/python/pythonmonkey/cli/pmjs.py @@ -2,6 +2,7 @@ # @file pmjs - PythonMonkey REPL # @author Wes Garland, wes@distributive.network # @date June 2023 +# @copyright Copyright (c) 2023 Distributive Corp. import sys, os, signal, getopt import readline @@ -300,18 +301,16 @@ def usage(): def initGlobalThis(): """ - Initialize globalThis for for pmjs use in the extra-module context (eg -r, -e, -p). This context - needs a require function which resolve modules relative to the current working directory at pmjs - launch. The global require is to the JS function using a trick iinstead of a JS-wrapped-Python-wrapped function + Initialize globalThis for pmjs use in the extra-module context (eg -r, -e, -p). This context + needs a require function which resolves modules relative to the current working directory at pmjs + launch. The global require is to the JS function using a trick instead of a JS-wrapped-Python-wrapped function """ global requirePath require = pm.createRequire(os.path.abspath(os.getcwd() + '/__pmjs_virtual__'), requirePath) globalThis.require = require globalInitModule = require(os.path.realpath(os.path.dirname(__file__) + "/../lib/pmjs/global-init")) # module load has side-effects - argvBuilder = globalInitModule.makeArgvBuilder() - for arg in sys.argv: - argvBuilder(arg); # list=>Array not working yet + globalThis.arguments = sys.argv return globalInitModule def main(): diff --git a/python/pythonmonkey/lib/pmjs/global-init.js b/python/pythonmonkey/lib/pmjs/global-init.js index 12b6618e..e2ac1497 100644 --- a/python/pythonmonkey/lib/pmjs/global-init.js +++ b/python/pythonmonkey/lib/pmjs/global-init.js @@ -17,22 +17,6 @@ for (let mid in require.cache) delete require.cache[mid]; -/** - * Set the global arguments array, which is just the program's argv. We use an argvBuilder function to - * get around PythonMonkey's missing list->Array coercion. /wg june 2023 - */ -exports.makeArgvBuilder = function pmjsRequire$$makeArgvBuilder() -{ - const argv = []; - globalThis.arguments = argv; - return argvBuilder; - - function argvBuilder(arg) - { - globalThis.arguments.push(arg) - } -} - /** * runProgramModule wants to include the require.cache from the pre-program loads (e.g. via -r or -e), but * due to current bugs in PythonMonkey, we can't access the cache property of require because it is a JS From 2f5a0c6d3fefc962e96403073f1cbe6a0d6dec6e Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:49:14 -0500 Subject: [PATCH 067/170] refactor(jsTypeFactory.cc): remove obvious comment --- src/jsTypeFactory.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 4f0a933f..6f1c2b23 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -36,7 +36,7 @@ #include #include -#include // https://docs.python.org/3/c-api/datetime.html +#include #include From 7f5bb5541c93bdd3f99c03b05eafd26eb75f80ee Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:52:09 -0500 Subject: [PATCH 068/170] refactor(JSFunctionProxy): remove extra line from JSFunctionProxy_call --- src/JSFunctionProxy.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index 0aea3c20..49325453 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -38,7 +38,6 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, JSObject *jsFuncObj = jsFunc.toObjectOrNull(); JS::RootedObject thisObj(GLOBAL_CX, JS::GetNonCCWObjectGlobal(jsFuncObj)); // if jsFunc is not bound, assume `this` is `globalThis` - JS::RootedVector jsArgsVector(cx); Py_ssize_t nargs = PyTuple_Size(args); for (size_t i = 0; i < nargs; i++) { From 519e784ae42583995f6f4f720b104adb75de2782 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 9 Feb 2024 11:58:05 -0500 Subject: [PATCH 069/170] refactor(pyTypeFactory): remove one-use variables --- src/pyTypeFactory.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index a06c9013..901c7b80 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -81,8 +81,7 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted return new FloatType(rval->toNumber()); } else if (rval->isString()) { - StrType *s = new StrType(cx, rval->toString()); - return s; + return new StrType(cx, rval->toString()); } else if (rval->isSymbol()) { printf("symbol type is not handled by PythonMonkey yet"); @@ -153,8 +152,7 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted case js::ESClass::String: { JS::RootedValue unboxed(cx); js::Unbox(cx, obj, &unboxed); - StrType *s = new StrType(cx, unboxed.toString()); - return s; + return new StrType(cx, unboxed.toString()); } case js::ESClass::Array: { return new ListType(cx, obj); From a16436d19703c8e0f31c792ae5d08efb76ef3827 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 9 Feb 2024 12:08:13 -0500 Subject: [PATCH 070/170] updates --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7a26808b..02fe5054 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ Care should be taken to ensure that only one program module is run per JS contex ## Built-In Functions See definitions in [python/pythonmonkey/global.d.ts](https://github.com/Distributive-Network/PythonMonkey/blob/main/python/pythonmonkey/global.d.ts). +Including: - `console` - `atob` @@ -324,10 +325,10 @@ in Python. Simply decorate a Dict named `exports` inside a file with a `.py` ext loaded by `require()` -- in either JavaScript or Python. ### Program Module -The program module, or main module, is a special module in CommonJS. In a program module, +The program module, or main module, is a special module in CommonJS. In a program module: - variables defined in the outermost scope are properties of `globalThis` - returning from the outermost scope is a syntax error - - the `arguments` variable in an Array-like object which holds your program's argument vector + - the `arguments` variable in an Array which holds your program's argument vector (command-line arguments) ```console @@ -391,7 +392,7 @@ List of commands: ```console $ pmjs -Welcome to PythonMonkey v0.2.0. +Welcome to PythonMonkey v0.3.0. Type ".help" for more information. > .python import sys > .python sys.path From a93fcd20f0d30fe5ed9c2a732f9f51d3e5e10523 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 9 Feb 2024 16:11:48 -0500 Subject: [PATCH 071/170] English fix and improved organization --- include/PyEventLoop.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/PyEventLoop.hh b/include/PyEventLoop.hh index 3bbcbbb3..92accbdf 100644 --- a/include/PyEventLoop.hh +++ b/include/PyEventLoop.hh @@ -215,7 +215,7 @@ public: } /** - * @brief An `asyncio.Event` instance to notify that there are no queueing asynchronous jobs + * @brief An `asyncio.Event` instance to notify that there are no queued asynchronous jobs * @see https://docs.python.org/3/library/asyncio-sync.html#asyncio.Event */ PyObject *_queueIsEmpty = nullptr; From ea2a2ec83e56d0e206fe82b744849a671ab2f93e Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 9 Feb 2024 16:32:44 -0500 Subject: [PATCH 072/170] no more leak with the second parameter of pyTypeFactory --- include/JobQueue.hh | 30 +++++------ include/pyTypeFactory.hh | 15 +++--- src/JSArrayIterProxy.cc | 6 ++- src/JSArrayProxy.cc | 64 +++++++++++++----------- src/JSFunctionProxy.cc | 2 +- src/JSMethodProxy.cc | 2 +- src/JSObjectIterProxy.cc | 8 +-- src/JSObjectProxy.cc | 30 +++++------ src/JobQueue.cc | 47 ++++++++--------- src/PromiseType.cc | 5 +- src/PyDictProxyHandler.cc | 4 +- src/PyListProxyHandler.cc | 51 +++++++++---------- src/PyObjectProxyHandler.cc | 4 +- src/internalBinding.cc | 9 ++-- src/internalBinding/timers.cc | 9 ++-- src/jsTypeFactory.cc | 4 +- src/modules/pythonmonkey/pythonmonkey.cc | 20 +++++--- src/pyTypeFactory.cc | 4 +- 18 files changed, 156 insertions(+), 158 deletions(-) diff --git a/include/JobQueue.hh b/include/JobQueue.hh index d1d0a09e..b714de74 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -1,7 +1,7 @@ /** * @file JobQueue.hh * @author Tom Tang (xmader@distributive.network) - * @brief Implement the ECMAScript Job Queue + * @brief Implements the ECMAScript Job Queue * @date 2023-04-03 * * @copyright Copyright (c) 2023 Distributive Corp. @@ -22,12 +22,17 @@ * @see https://hg.mozilla.org/releases/mozilla-esr102/file/5741ffa/js/public/Promise.h#l22 */ class JobQueue : public JS::JobQueue { -// -// JS::JobQueue methods. -// + public: ~JobQueue() = default; +/** + * @brief Initialize PythonMonkey's event-loop job queue + * @param cx - javascript context pointer + * @return success + */ +bool init(JSContext *cx); + /** * @brief Ask the embedding for the incumbent global. * @@ -72,7 +77,9 @@ void runJobs(JSContext *cx) override; */ bool empty() const override; + private: + /** * @brief Capture this JobQueue's current job queue as a SavedJobQueue and return it, * leaving the JobQueue's job queue empty. Destroying the returned object @@ -84,18 +91,6 @@ private: */ js::UniquePtr saveJobQueue(JSContext *) override; -// -// Custom methods -// -public: -/** - * @brief Initialize PythonMonkey's event-loop job queue - * @param cx - javascript context pointer - * @return success - */ -bool init(JSContext *cx); - -private: /** * @brief The callback for dispatching an off-thread promise to the event loop * see https://hg.mozilla.org/releases/mozilla-esr102/file/tip/js/public/Promise.h#l580 @@ -105,7 +100,8 @@ private: * @return not shutting down */ static bool dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable); -}; + +}; // class /** * @brief Send job to the Python event-loop on main thread diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index 71c6abab..c122161b 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -1,10 +1,10 @@ /** * @file pyTypeFactory.hh - * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) + * @author Caleb Aikens (caleb@distributive.network), Giovanni Tedesco (giovanni@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief Function for wrapping arbitrary PyObjects into the appropriate PyType class, and coercing JS types to python types * @date 2022-08-08 * - * @copyright Copyright (c) 2022 Distributive Corp. + * @copyright Copyright (c) 2022, 2023, 2024 Distributive Corp. * */ @@ -30,15 +30,16 @@ PyType *pyTypeFactory(PyObject *object); * @brief Function that takes a JS::Value and returns a corresponding PyType* object, doing shared memory management when necessary * * @param cx - Pointer to the javascript context of the JS::Value - * @param thisObj - Pointer to the JS `this` object for the value's scope - * @param rval - Pointer to the JS::Value who's type and value we wish to encapsulate + * @param thisObj - The JS `this` object for the value's scope + * @param rval - The JS::Value who's type and value we wish to encapsulate * @return PyType* - Pointer to a PyType object corresponding to the JS::Value */ -PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval); +PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval); + /** * @brief same to pyTypeFactory, but it's guaranteed that no error would be set on the Python error stack, instead - * return `pythonmonkey.null` on error + * returning `pythonmonkey.null` on error */ -PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval); +PyType *pyTypeFactorySafe(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval); #endif \ No newline at end of file diff --git a/src/JSArrayIterProxy.cc b/src/JSArrayIterProxy.cc index f7a73b36..6d5923da 100644 --- a/src/JSArrayIterProxy.cc +++ b/src/JSArrayIterProxy.cc @@ -47,16 +47,18 @@ PyObject *JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_next(JSArrayIterPr if (self->it.reversed) { if (self->it.it_index >= 0) { + JS::RootedObject thisObj(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray); JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index--, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); } } else { if (self->it.it_index < JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)seq)) { + JS::RootedObject thisObj(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray); JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index++, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSArrayProxy *)seq)->jsArray)), elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); } } diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 7febaa82..3162b8a9 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -53,7 +53,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py if (methodName == NULL || !PyUnicode_Check(key)) { // reached end of list JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); - JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsArray); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else { @@ -75,7 +75,8 @@ static PyObject *list_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihig PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jReturnedArray)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, jReturnedArray)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy *self, PyObject *key) @@ -103,7 +104,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), value)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else if (PySlice_Check(key)) { Py_ssize_t start, stop, step, slicelength, index; @@ -132,7 +134,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, jCombinedArrayValue)->getPyObject(); } } else { @@ -432,18 +435,18 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * } JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); Py_ssize_t index; /* Search for the first index where items are different */ for (index = 0; index < selfLength && index < otherLength; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *leftItem = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + PyObject *leftItem = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); PyObject *rightItem; if (PyObject_TypeCheck(other, &JSArrayProxyType)) { JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)other)->jsArray, index, &elementVal); - rightItem = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + rightItem = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); } else { rightItem = ((PyListObject *)other)->ob_item[index]; } @@ -480,7 +483,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); /* Compare the final item again using the proper operator */ - return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); + return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { @@ -503,7 +506,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { writer.min_length = 1 + 1 + (2 + 1) * (selfLength - 1) + 1; JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); if (_PyUnicodeWriter_WriteChar(&writer, '[') < 0) { goto error; @@ -523,7 +526,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { if (&elementVal.toObject() == self->jsArray.get()) { s = PyObject_Repr((PyObject *)self); } else { - s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject()); + s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject()); } if (s == NULL) { goto error; @@ -628,7 +631,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_concat(JSArrayProxy *self, JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, jCombinedArrayValue)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, Py_ssize_t n) { @@ -655,7 +659,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jCombinedArrayValue)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, jCombinedArrayValue)->getPyObject(); } int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyObject *element) { @@ -665,10 +670,10 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyO Py_ssize_t numElements = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (index = 0, cmp = 0; cmp == 0 && index < numElements; ++index) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *item = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); Py_INCREF(item); cmp = PyObject_RichCompareBool(item, element, Py_EQ); Py_DECREF(item); @@ -753,7 +758,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), jReturnedArray)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, jReturnedArray)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_append(JSArrayProxy *self, PyObject *value) { @@ -930,17 +936,18 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_pop(JSArrayProxy *self, Py JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, rootedReturnedArray, 0, &elementVal); - return pyTypeFactory(GLOBAL_CX, new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)), elementVal)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); + return pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, PyObject *value) { Py_ssize_t selfSize = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (Py_ssize_t index = 0; index < selfSize; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1003,10 +1010,10 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_index(JSArrayProxy *self, } JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (Py_ssize_t index = start; index < stop && index < selfSize; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1027,10 +1034,10 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_count(JSArrayProxy *self, Py_ssize_t length = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsArray)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (Py_ssize_t index = 0; index < length; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1076,18 +1083,17 @@ static bool sort_compare_key_func(JSContext *cx, unsigned argc, JS::Value *vp) { } bool reverse = reverseValue.toBoolean(); - - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); + JS::RootedObject thisObj(cx, JS::GetNonCCWObjectGlobal(&args.callee())); JS::RootedValue elementVal0(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, elementVal0)->getPyObject(); + PyObject *args_0 = pyTypeFactory(cx, thisObj, elementVal0)->getPyObject(); PyObject *args_0_result = PyObject_CallFunction(keyfunc, "O", args_0); if (!args_0_result) { return false; } JS::RootedValue elementVal1(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, elementVal1)->getPyObject(); + PyObject *args_1 = pyTypeFactory(cx, thisObj, elementVal1)->getPyObject(); PyObject *args_1_result = PyObject_CallFunction(keyfunc, "O", args_1); if (!args_1_result) { return false; @@ -1127,13 +1133,13 @@ static bool sort_compare_default(JSContext *cx, unsigned argc, JS::Value *vp) { } bool reverse = reverseValue.toBoolean(); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(&args.callee())); + JS::RootedObject thisObj(cx, JS::GetNonCCWObjectGlobal(&args.callee())); JS::RootedValue elementVal0(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, global, elementVal0)->getPyObject(); + PyObject *args_0 = pyTypeFactory(cx, thisObj, elementVal0)->getPyObject(); JS::RootedValue elementVal1(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, global, elementVal1)->getPyObject(); + PyObject *args_1 = pyTypeFactory(cx, thisObj, elementVal1)->getPyObject(); int cmp = PyObject_RichCompareBool(args_0, args_1, Py_LT); if (cmp > 0) { diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index f950b357..ec974012 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -60,5 +60,5 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, return NULL; } - return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); + return pyTypeFactory(cx, thisObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index ef05a497..679c04b8 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -70,6 +70,6 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO return NULL; } - JS::RootedObject *globalObj = new JS::RootedObject(cx, JS::CurrentGlobalOrNull(cx)); + JS::RootedObject globalObj(cx, JS::CurrentGlobalOrNull(cx)); return pyTypeFactory(cx, globalObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index e720d496..02c5f602 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -57,10 +57,10 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject PyObject *value; if (self->it.kind != KIND_KEYS) { - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSObjectProxy *)(self->it.di_dict))->jsObject)); + JS::RootedObject thisObj(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject); JS::RootedValue jsVal(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, global, jsVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, thisObj, jsVal)->getPyObject(); } PyObject *ret; @@ -84,10 +84,10 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject PyObject *value; if (self->it.kind != KIND_KEYS) { - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(((JSObjectProxy *)(self->it.di_dict))->jsObject)); + JS::RootedObject thisObj(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject); JS::RootedValue jsVal(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, global, jsVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, thisObj, jsVal)->getPyObject(); } PyObject *ret; diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 6d476c2c..24c76406 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -77,7 +77,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, if (methodName == NULL || !PyUnicode_Check(key)) { JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); - JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsObject); + JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else { @@ -190,8 +190,8 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr JS::RootedValue key(GLOBAL_CX); key.setString(id.toString()); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - PyObject *pyKey = pyTypeFactory(GLOBAL_CX, global, key)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); + PyObject *pyKey = pyTypeFactory(GLOBAL_CX, thisObj, key)->getPyObject(); PyObject *pyVal1 = PyObject_GetItem((PyObject *)self, pyKey); PyObject *pyVal2 = PyObject_GetItem((PyObject *)other, pyKey); if (!pyVal2) { // if other.key is NULL then not equal @@ -260,7 +260,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self PyObject *key = NULL, *value = NULL; - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); JS::RootedIdVector props(GLOBAL_CX); @@ -310,7 +310,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self if (&elementVal.toObject() == self->jsObject.get()) { value = (PyObject *)self; } else { - value = pyTypeFactory(GLOBAL_CX, global, elementVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); } Py_INCREF(value); @@ -438,11 +438,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, JS::RootedValue jValueOther(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, other)); args[2].setObject(jValueOther.toObject()); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); // call Object.assign JS::RootedValue Object(GLOBAL_CX); - if (!JS_GetProperty(GLOBAL_CX, *global, "Object", &Object)) { + if (!JS_GetProperty(GLOBAL_CX, global, "Object", &Object)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } @@ -454,7 +454,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, global, ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, rootedObject, ret)->getPyObject(); } } @@ -465,11 +465,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_ior(JSObjectProxy *self, JS::RootedValue jValueOther(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, other)); args[1].setObject(jValueOther.toObject()); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); // call Object.assign JS::RootedValue Object(GLOBAL_CX); - if (!JS_GetProperty(GLOBAL_CX, *global, "Object", &Object)) { + if (!JS_GetProperty(GLOBAL_CX, global, "Object", &Object)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } @@ -576,8 +576,8 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy JS::ObjectOpResult ignoredResult; JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); - return pyTypeFactory(GLOBAL_CX, global, value)->getPyObject(); + JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); + return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } } @@ -603,11 +603,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method(JSObjectProx args[0].setObjectOrNull(JS_NewPlainObject(GLOBAL_CX)); args[1].setObjectOrNull(self->jsObject); - JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); // call Object.assign JS::RootedValue Object(GLOBAL_CX); - if (!JS_GetProperty(GLOBAL_CX, *global, "Object", &Object)) { + if (!JS_GetProperty(GLOBAL_CX, global, "Object", &Object)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } @@ -618,7 +618,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method(JSObjectProx PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, global, ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, rootedObject, ret)->getPyObject(); } PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_update_method(JSObjectProxy *self, PyObject *args, PyObject *kwds) { diff --git a/src/JobQueue.cc b/src/JobQueue.cc index 6e8f6e7a..799c5678 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -1,7 +1,7 @@ /** * @file JobQueue.cc * @author Tom Tang (xmader@distributive.network) - * @brief Implement the ECMAScript Job Queue + * @brief Implements the ECMAScript Job Queue * @date 2023-04-03 * * @copyright Copyright (c) 2023 Distributive Corp. @@ -29,13 +29,9 @@ bool JobQueue::enqueuePromiseJob(JSContext *cx, JS::HandleObject incumbentGlobal) { // Convert the `job` JS function to a Python function for event-loop callback - MOZ_RELEASE_ASSERT(js::IsFunctionObject(job)); - // FIXME (Tom Tang): memory leak, objects not free-ed - // FIXME (Tom Tang): `job` function is going to be GC-ed ??? - auto global = new JS::RootedObject(cx, incumbentGlobal); - // auto jobv = new JS::RootedValue(cx, JS::ObjectValue(*job)); + JS::RootedObject global(cx, incumbentGlobal); JS::RootedValue jobv(cx, JS::ObjectValue(*job)); - auto callback = pyTypeFactory(cx, global, jobv)->getPyObject(); + PyObject *callback = pyTypeFactory(cx, global, jobv)->getPyObject(); // Inform the JS runtime that the job queue is no longer empty JS::JobQueueMayNotBeEmpty(cx); @@ -49,10 +45,9 @@ bool JobQueue::enqueuePromiseJob(JSContext *cx, } void JobQueue::runJobs(JSContext *cx) { - return; + // Do nothing } -// is empty bool JobQueue::empty() const { // TODO (Tom Tang): implement using `get_running_loop` and getting job count on loop??? throw std::logic_error("JobQueue::empty is not implemented\n"); @@ -65,7 +60,7 @@ js::UniquePtr JobQueue::saveJobQueue(JSContext *cx) bool JobQueue::init(JSContext *cx) { JS::SetJobQueue(cx, this); - JS::InitDispatchToEventLoop(cx, /* callback */ dispatchToEventLoop, /* closure */ cx); + JS::InitDispatchToEventLoop(cx, dispatchToEventLoop, cx); return true; } @@ -75,24 +70,9 @@ static PyObject *callDispatchFunc(PyObject *dispatchFuncTuple, PyObject *Py_UNUS dispatchable->run(cx, JS::Dispatchable::NotShuttingDown); Py_RETURN_NONE; } -static PyMethodDef callDispatchFuncDef = {"JsDispatchCallable", callDispatchFunc, METH_NOARGS, NULL}; - -bool sendJobToMainLoop(PyObject *pyFunc) { - PyGILState_STATE gstate = PyGILState_Ensure(); - - // Send job to the running Python event-loop on `cx`'s thread (the main thread) - PyEventLoop loop = PyEventLoop::getMainLoop(); - if (!loop.initialized()) { - PyGILState_Release(gstate); - return false; - } - loop.enqueue(pyFunc); - PyGILState_Release(gstate); - return true; -} +static PyMethodDef callDispatchFuncDef = {"JsDispatchCallable", callDispatchFunc, METH_NOARGS, NULL}; -/* static */ bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable) { JSContext *cx = (JSContext *)closure; // `closure` is provided in `JS::InitDispatchToEventLoop` call @@ -110,3 +90,18 @@ bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable PyGILState_Release(gstate); return true; // dispatchable must eventually run } + +bool sendJobToMainLoop(PyObject *pyFunc) { + PyGILState_STATE gstate = PyGILState_Ensure(); + + // Send job to the running Python event-loop on cx's thread (the main thread) + PyEventLoop loop = PyEventLoop::getMainLoop(); + if (!loop.initialized()) { + PyGILState_Release(gstate); + return false; + } + loop.enqueue(pyFunc); + + PyGILState_Release(gstate); + return true; +} \ No newline at end of file diff --git a/src/PromiseType.cc b/src/PromiseType.cc index 55ceefa7..d34202d2 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -35,10 +35,9 @@ static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { JS::PromiseState state = JS::GetPromiseState(promise); // Convert the Promise's result (either fulfilled resolution or rejection reason) to a Python object - // FIXME (Tom Tang): memory leak, not free-ed // The result might be another JS function, so we must keep them alive - JS::RootedObject *thisv = new JS::RootedObject(cx); - args.computeThis(cx, thisv); // thisv is the global object, not the promise + JS::RootedObject thisv(cx); + args.computeThis(cx, &thisv); // thisv is the global object, not the promise JS::RootedValue resultArg(cx, args[0]); PyObject *result = pyTypeFactory(cx, thisv, resultArg)->getPyObject(); if (state == JS::PromiseState::Rejected && !PyExceptionInstance_Check(result)) { diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index 1b18b822..08932d39 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -142,8 +142,8 @@ bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId JS::ObjectOpResult &result) const { JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + JS::RootedObject thisObj(cx, proxy); + if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, thisObj, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 7bc51c28..73d8316a 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -87,12 +87,12 @@ static bool array_push(JSContext *cx, unsigned argc, JS::Value *vp) { // surely } PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); unsigned numArgs = args.length(); JS::RootedValue elementVal(cx); for (unsigned index = 0; index < numArgs; index++) { elementVal.set(args[index].get()); - if (PyList_Append(self, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_Append(self, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { return false; } } @@ -138,11 +138,11 @@ static bool array_unshift(JSContext *cx, unsigned argc, JS::Value *vp) { // sure } PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); JS::RootedValue elementVal(cx); for (int index = args.length() - 1; index >= 0; index--) { elementVal.set(args[index].get()); - if (PyList_Insert(self, 0, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_Insert(self, 0, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { return false; } } @@ -252,9 +252,9 @@ static bool array_indexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } } - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); JS::RootedValue elementVal(cx, args[0].get()); - PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, global, elementVal)->getPyObject(), start); + PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, thisObj, elementVal)->getPyObject(), start); if (!result) { PyErr_Clear(); @@ -332,10 +332,10 @@ static bool array_splice(JSContext *cx, unsigned argc, JS::Value *vp) { } JS::RootedValue elementVal(cx); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); for (int index = 0; index < insertCount; index++) { elementVal.set(args[index + 2].get()); - if (PyList_SetItem(inserted, index, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_SetItem(inserted, index, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { return false; } } @@ -397,9 +397,9 @@ static bool array_fill(JSContext *cx, unsigned argc, JS::Value *vp) { actualEnd = uint64_t(std::min(double(relativeEnd), double(selfLength))); } - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); JS::RootedValue fillValue(cx, args[0].get()); - PyObject *fillValueItem = pyTypeFactory(cx, global, fillValue)->getPyObject(); + PyObject *fillValueItem = pyTypeFactory(cx, thisObj, fillValue)->getPyObject(); for (int index = actualStart; index < actualEnd; index++) { if (PyList_SetItem(self, index, fillValueItem) < 0) { return false; @@ -515,7 +515,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); Py_ssize_t selfSize = PyList_GET_SIZE(self); @@ -530,7 +530,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { for (unsigned index = 0; index < numArgs; index++) { elementVal.set(args[index].get()); - PyObject *item = pyTypeFactory(cx, global, elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(cx, thisObj, elementVal)->getPyObject(); if (PyObject_TypeCheck(item, &JSArrayProxyType)) { // flatten the array only a depth 1 Py_ssize_t itemLength = JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)item); @@ -538,7 +538,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { if (!JS_GetElement(cx, ((JSArrayProxy *)item)->jsArray, flatIndex, &elementVal)) { return false; } - if (PyList_Append(result, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { return false; } } @@ -553,7 +553,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } } else { - if (PyList_Append(result, pyTypeFactory(cx, global, elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { return false; } } @@ -602,9 +602,9 @@ static bool array_lastIndexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } } - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); JS::RootedValue elementVal(cx, args[0].get()); - PyObject *element = pyTypeFactory(cx, global, elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, thisObj, elementVal)->getPyObject(); for (int64_t index = start; index >= 0; index--) { PyObject *item = PyList_GetItem(self, index); Py_INCREF(item); @@ -1184,14 +1184,13 @@ static bool array_findIndex(JSContext *cx, unsigned argc, JS::Value *vp) { } // private -static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, +static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject thisObj, JSObject *retArray, PyObject *source, Py_ssize_t sourceLen, uint32_t start, uint32_t depth) { uint32_t targetIndex = start; JS::RootedValue elementVal(cx); - JS::RootedObject rootedGlobal(cx, global); for (uint32_t sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { @@ -1202,7 +1201,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); } - PyObject *element = pyTypeFactory(cx, &rootedGlobal, elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, thisObj, elementVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1220,7 +1219,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, elementLen = PyList_GET_SIZE(element); } - targetIndex = FlattenIntoArray(cx, global, + targetIndex = FlattenIntoArray(cx, thisObj, retArray, element, elementLen, @@ -1247,14 +1246,13 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject global, } // private -static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject global, +static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject thisObj, JSObject *retArray, PyObject *source, Py_ssize_t sourceLen, uint32_t start, uint32_t depth, JS::HandleValue callBack, JS::HandleObject thisArg) { uint32_t targetIndex = start; - JS::RootedObject rootedGlobal(cx, global); JS::RootedValue sourceValue(cx, jsTypeFactory(cx, source)); JS::Rooted> jArgs(cx); JS::RootedValue elementVal(cx); @@ -1275,7 +1273,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject glo return false; } - PyObject *element = pyTypeFactory(cx, &rootedGlobal, retVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, thisObj, retVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1293,7 +1291,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject glo } if (shouldFlatten) { - targetIndex = FlattenIntoArrayWithCallBack(cx, global, + targetIndex = FlattenIntoArrayWithCallBack(cx, thisObj, retArray, element, elementLen, @@ -2055,10 +2053,9 @@ bool PyListProxyHandler::defineProperty( return result.failInvalidDescriptor(); } - // FIXME (Tom Tang): memory leak - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); + JS::RootedObject thisObj(cx, proxy); JS::RootedValue itemV(cx, desc.value()); - PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); + PyObject *item = pyTypeFactory(cx, thisObj, itemV)->getPyObject(); if (PyList_SetItem(pyObject, index, item) < 0) { // we are out-of-bounds and need to expand Py_ssize_t len = PyList_GET_SIZE(pyObject); diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 1e3213f5..19edbaee 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -94,8 +94,8 @@ bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::Handle JS::ObjectOpResult &result) const { JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + JS::RootedObject thisObj(cx, proxy); + if (PyObject_SetAttr(pyObject, attrName, pyTypeFactory(cx, thisObj, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/internalBinding.cc b/src/internalBinding.cc index 410eca6d..bf789fa2 100644 --- a/src/internalBinding.cc +++ b/src/internalBinding.cc @@ -59,10 +59,7 @@ PyObject *getInternalBindingPyFn(JSContext *cx) { JSObject *jsFn = (JSObject *)createInternalBinding(cx); // Convert to a Python function - // FIXME (Tom Tang): memory leak, not free-ed - JS::RootedObject *thisObj = new JS::RootedObject(cx, nullptr); + JS::RootedObject thisObj(cx, nullptr); JS::RootedValue jsFnVal(cx, JS::ObjectValue(*jsFn)); - PyObject *pyFn = pyTypeFactory(cx, thisObj, jsFnVal)->getPyObject(); - - return pyFn; -} + return pyTypeFactory(cx, thisObj, jsFnVal)->getPyObject(); +} \ No newline at end of file diff --git a/src/internalBinding/timers.cc b/src/internalBinding/timers.cc index a37fae11..934f4f7b 100644 --- a/src/internalBinding/timers.cc +++ b/src/internalBinding/timers.cc @@ -2,7 +2,7 @@ * @file timers.cc * @author Tom Tang (xmader@distributive.network) * @brief Implement functions in `internalBinding("timers")` - * + * * @copyright Copyright (c) 2023 Distributive Corp. */ @@ -23,8 +23,7 @@ static bool enqueueWithDelay(JSContext *cx, unsigned argc, JS::Value *vp) { double delaySeconds = args.get(1).toNumber(); // Convert to a Python function - // FIXME (Tom Tang): memory leak, not free-ed - JS::RootedObject *thisv = new JS::RootedObject(cx, nullptr); + JS::RootedObject thisv(cx, nullptr); JS::RootedValue jobArg(cx, jobArgVal); PyObject *job = pyTypeFactory(cx, thisv, jobArg)->getPyObject(); @@ -56,7 +55,7 @@ static bool cancelByTimeoutId(JSContext *cx, unsigned argc, JS::Value *vp) { AsyncHandle *handle = AsyncHandle::fromId((uint32_t)timeoutID); if (!handle) return true; // does nothing on invalid timeoutID - // Cancel this job on Python event-loop + // Cancel this job on the Python event-loop handle->cancel(); return true; @@ -66,4 +65,4 @@ JSFunctionSpec InternalBinding::timers[] = { JS_FN("enqueueWithDelay", enqueueWithDelay, /* nargs */ 2, 0), JS_FN("cancelByTimeoutId", cancelByTimeoutId, 1, 0), JS_FS_END -}; +}; \ No newline at end of file diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index c0515e07..c7ce7ac1 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -276,8 +276,8 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { JS::Value pyFuncVal = js::GetFunctionNativeReserved(&(callargs.callee()), 0); PyObject *pyFunc = (PyObject *)(pyFuncVal.toPrivate()); - JS::RootedObject *thisv = new JS::RootedObject(cx); - JS_ValueToObject(cx, callargs.thisv(), thisv); + JS::RootedObject thisv(cx); + JS_ValueToObject(cx, callargs.thisv(), &thisv); unsigned int callArgsLength = callargs.length(); diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 129e9c48..e99cf366 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -356,7 +356,7 @@ static PyObject *eval(PyObject *self, PyObject *args) { } // translate to the proper python type - PyType *returnValue = pyTypeFactory(GLOBAL_CX, global, *rval); + PyType *returnValue = pyTypeFactory(GLOBAL_CX, *global, *rval); if (PyErr_Occurred()) { return NULL; } @@ -524,8 +524,11 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; pyModule = PyModule_Create(&pythonmonkey); - if (pyModule == NULL) + if (pyModule == NULL) { return NULL; + } + + // Register Types Py_INCREF(&NullType); if (PyModule_AddObject(pyModule, "null", (PyObject *)&NullType) < 0) { @@ -616,9 +619,6 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } - // Initialize event-loop shield - PyEventLoop::_locker = new PyEventLoop::Lock(); - PyObject *internalBindingPy = getInternalBindingPyFn(GLOBAL_CX); if (PyModule_AddObject(pyModule, "internalBinding", internalBindingPy) < 0) { Py_DECREF(internalBindingPy); @@ -626,7 +626,12 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } - // initialize FinalizationRegistry of JSFunctions to Python Functions + + // Initialize event-loop shield + PyEventLoop::_locker = new PyEventLoop::Lock(); + + + // Initialize FinalizationRegistry of JSFunctions to Python Functions JS::RootedValue FinalizationRegistry(GLOBAL_CX); JS::RootedObject registryObject(GLOBAL_CX); @@ -642,5 +647,6 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) jsFunctionRegistry = new JS::PersistentRootedObject(GLOBAL_CX); jsFunctionRegistry->set(registryObject); + return pyModule; -} +} \ No newline at end of file diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index 09384bfd..576bf95d 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -67,7 +67,7 @@ PyType *pyTypeFactory(PyObject *object) { return pyType; } -PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval) { +PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval) { if (rval.isUndefined()) { return new NoneType(); } @@ -179,7 +179,7 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Handle return NULL; } -PyType *pyTypeFactorySafe(JSContext *cx, JS::Rooted *thisObj, JS::HandleValue rval) { +PyType *pyTypeFactorySafe(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval) { PyType *v = pyTypeFactory(cx, thisObj, rval); if (PyErr_Occurred()) { // Clear Python error From e970277f4e6326c8a2c7b09bf41f752a73e4fe72 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Tue, 13 Feb 2024 03:15:14 -0500 Subject: [PATCH 073/170] refactor(PyBaseProxyHandler): refactor shared code between PyDictProxyHandler and PyObjectProxyHandler into a shared parent struct called PyDictOrObjectProxyHandler. Also remove pointer to the pyObject in the ProxyHandlers and use private slot instead, and only ever instantiate one instance of each ProxyHandler, rather than creating a new handler every time we make a new proxy object --- include/PyBaseProxyHandler.hh | 3 +- include/PyDictOrObjectProxyHandler.hh | 88 ++++++++++++++++++++ include/PyDictProxyHandler.hh | 6 +- include/PyListProxyHandler.hh | 2 +- include/PyObjectProxyHandler.hh | 8 +- src/PyDictOrObjectProxyHandler.cc | 115 ++++++++++++++++++++++++++ src/PyDictProxyHandler.cc | 107 ++++-------------------- src/PyListProxyHandler.cc | 14 ++-- src/PyObjectProxyHandler.cc | 42 +++------- src/jsTypeFactory.cc | 10 ++- src/pyTypeFactory.cc | 6 +- 11 files changed, 256 insertions(+), 145 deletions(-) create mode 100644 include/PyDictOrObjectProxyHandler.hh create mode 100644 src/PyDictOrObjectProxyHandler.cc diff --git a/include/PyBaseProxyHandler.hh b/include/PyBaseProxyHandler.hh index dc3700a8..353973be 100644 --- a/include/PyBaseProxyHandler.hh +++ b/include/PyBaseProxyHandler.hh @@ -23,8 +23,7 @@ */ struct PyBaseProxyHandler : public js::BaseProxyHandler { public: - PyBaseProxyHandler(PyObject *pyObj, const void *family) : js::BaseProxyHandler(family), pyObject(pyObj) {}; - PyObject *pyObject; // @TODO (Caleb Aikens) Consider putting this in a private slot + PyBaseProxyHandler(const void *family) : js::BaseProxyHandler(family) {}; bool getPrototypeIfOrdinary(JSContext *cx, JS::HandleObject proxy, bool *isOrdinary, JS::MutableHandleObject protop) const override final; bool preventExtensions(JSContext *cx, JS::HandleObject proxy, JS::ObjectOpResult &result) const override final; diff --git a/include/PyDictOrObjectProxyHandler.hh b/include/PyDictOrObjectProxyHandler.hh new file mode 100644 index 00000000..a41d81ad --- /dev/null +++ b/include/PyDictOrObjectProxyHandler.hh @@ -0,0 +1,88 @@ +/** + * @file PyDictOrObjectProxyHandler.hh + * @author Caleb Aikens (caleb@distributive.network) + * @brief Abstract class inherited by PyDictProxyHandler and PyObjectProxyHandler since they share a lot of logic + * @date 2024-02-13 + * + * Copyright (c) 2024 Distributive Corp. + * + */ + +#ifndef PythonMonkey_PyDictOrObjectProxyHandler_ +#define PythonMonkey_PyDictOrObjectProxyHandler_ + +#include "include/PyBaseProxyHandler.hh" + +#include + +#include + +struct PyDictOrObjectProxyHandler : public PyBaseProxyHandler { + PyDictOrObjectProxyHandler(const void *family) : PyBaseProxyHandler(family) {}; + + /** + * @brief Helper function used by dicts and objects for ownPropertyKeys + * + * @param cx - pointer to the JSContext + * @param keys - PyListObject containing the keys of the proxy'd dict/object + * @param length - the length of keys param + * @param props - out-param, will be a JS vector of the keys converted to JS Ids + * @return true - the function succeeded + * @return false - the function failed (an Exception should be raised) + */ + static bool handleOwnPropertyKeys(JSContext *cx, PyObject *keys, size_t length, JS::MutableHandleIdVector props); + + /** + * @brief Helper function used by dicts and objects for get OwnPropertyDescriptor + * + * @param cx - pointer to the JSContext + * @param id - id of the prop to get + * @param desc - out-param, the property descriptor + * @param item - the python object to be converted to a JS prop + * @return true - the function succeeded + * @return false - the function has failed and an exception has been raised + */ + static bool handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, + JS::MutableHandle> desc, PyObject *item); + + static void handleFinalize(JSObject *proxy); + + /** + * @brief Helper function used by dicts and objects to convert dict/object to String + * + * @param cx - pointer to the JSContext + * @param argc - unused + * @param vp - unused + * @return true - this function always returns true + */ + static bool object_toString(JSContext *cx, unsigned argc, JS::Value *vp); + + /** + * @brief Helper function used by dicts and objects to convert dict/object to LocaleString + * + * @param cx - pointer to the JSContext + * @param argc - unused + * @param vp - unused + * @return true - this function always returns true + */ + static bool object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp); + + /** + * @brief Helper function used by dicts and objects to get valueOf, just returns a new reference to `self` + * + * @param cx - pointer to the JSContext + * @param argc - unused + * @param vp - unused + * @return true - the function succeeded + * @return false - the function failed and an exception has been raised + */ + static bool object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp); + + /** + * @brief An array of method definitions for Object prototype methods + * + */ + static JSMethodDef object_methods[]; +}; + +#endif \ No newline at end of file diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index 97ebc162..36b4d82f 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -11,16 +11,16 @@ #ifndef PythonMonkey_PyDictProxy_ #define PythonMonkey_PyDictProxy_ -#include "include/PyBaseProxyHandler.hh" +#include "include/PyDictOrObjectProxyHandler.hh" /** * @brief This struct is the ProxyHandler for JS Proxy Objects pythonmonkey creates to handle coercion from python dicts to JS Objects * */ -struct PyDictProxyHandler : public PyBaseProxyHandler { +struct PyDictProxyHandler : public PyDictOrObjectProxyHandler { public: - PyDictProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; + PyDictProxyHandler() : PyDictOrObjectProxyHandler(&family) {}; static const char family; /** diff --git a/include/PyListProxyHandler.hh b/include/PyListProxyHandler.hh index faebac51..18ed3aec 100644 --- a/include/PyListProxyHandler.hh +++ b/include/PyListProxyHandler.hh @@ -20,7 +20,7 @@ */ struct PyListProxyHandler : public PyBaseProxyHandler { public: - PyListProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; + PyListProxyHandler() : PyBaseProxyHandler(&family) {}; static const char family; /** diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 5dc29737..bfd54fee 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -11,7 +11,7 @@ #ifndef PythonMonkey_PyObjectProxy_ #define PythonMonkey_PyObjectProxy_ -#include "include/PyBaseProxyHandler.hh" +#include "include/PyDictOrObjectProxyHandler.hh" #include #include @@ -21,9 +21,9 @@ * @brief This struct is the ProxyHandler for JS Proxy Objects pythonmonkey creates to handle coercion from python objects to JS Objects * */ -struct PyObjectProxyHandler : public PyBaseProxyHandler { +struct PyObjectProxyHandler : public PyDictOrObjectProxyHandler { public: - PyObjectProxyHandler(PyObject *pyObj) : PyBaseProxyHandler(pyObj, &family) {}; + PyObjectProxyHandler() : PyDictOrObjectProxyHandler(&family) {}; static const char family; /** @@ -130,6 +130,8 @@ public: JS::HandleId id, JS::Handle desc, JS::ObjectOpResult &result) const override; + + bool getBuiltinClass(JSContext *cx, JS::HandleObject proxy, js::ESClass *cls) const override; }; #endif \ No newline at end of file diff --git a/src/PyDictOrObjectProxyHandler.cc b/src/PyDictOrObjectProxyHandler.cc new file mode 100644 index 00000000..6f56dda7 --- /dev/null +++ b/src/PyDictOrObjectProxyHandler.cc @@ -0,0 +1,115 @@ +/** + * @file PyDictOrObjectProxyHandler.cc + * @author Caleb Aikens (caleb@distributive.network) + * @brief + * @date 2024-02-13 + * + * Copyright (c) 2024 Distributive Corp. + * + */ + +#include "include/PyDictOrObjectProxyHandler.hh" + +#include "include/jsTypeFactory.hh" + +#include +#include + +bool PyDictOrObjectProxyHandler::object_toString(JSContext *cx, unsigned argc, JS::Value *vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + args.rval().setString(JS_NewStringCopyZ(cx, "[object Object]")); + return true; +} + +bool PyDictOrObjectProxyHandler::object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp) { + return object_toString(cx, argc, vp); +} + +bool PyDictOrObjectProxyHandler::object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + JS::RootedObject proxy(cx, JS::ToObject(cx, args.thisv())); + if (!proxy) { + return false; + } + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + + // return ref to self + args.rval().set(jsTypeFactory(cx, self)); + return true; +} + +JSMethodDef PyDictOrObjectProxyHandler::object_methods[] = { + {"toString", PyDictOrObjectProxyHandler::object_toString, 0}, + {"toLocaleString", PyDictOrObjectProxyHandler::object_toLocaleString, 0}, + {"valueOf", PyDictOrObjectProxyHandler::object_valueOf, 0}, + {NULL, NULL, 0} +}; + + +bool PyDictOrObjectProxyHandler::handleOwnPropertyKeys(JSContext *cx, PyObject *keys, size_t length, JS::MutableHandleIdVector props) { + if (!props.reserve(length)) { + return false; // out of memory + } + + for (size_t i = 0; i < length; i++) { + PyObject *key = PyList_GetItem(keys, i); + JS::RootedId jsId(cx); + if (!keyToId(key, &jsId)) { + // TODO (Caleb Aikens): raise exception here + return false; // key is not a str or int + } + props.infallibleAppend(jsId); + } + return true; +} + +bool PyDictOrObjectProxyHandler::handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, + JS::MutableHandle> desc, PyObject *item) { + // see if we're calling a function + if (id.isString()) { + for (size_t index = 0;; index++) { + bool isThatFunction; + const char *methodName = object_methods[index].name; + if (methodName == NULL) { + break; + } + else if (JS_StringEqualsAscii(cx, id.toString(), methodName, &isThatFunction) && isThatFunction) { + JSFunction *newFunction = JS_NewFunction(cx, object_methods[index].call, object_methods[index].nargs, 0, NULL); + if (!newFunction) return false; + JS::RootedObject funObj(cx, JS_GetFunctionObject(newFunction)); + desc.set(mozilla::Some( + JS::PropertyDescriptor::Data( + JS::ObjectValue(*funObj), + {JS::PropertyAttribute::Enumerable} + ) + )); + return true; + } + } + } + + if (!item) { // NULL if the key is not present + desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys + } else { + desc.set(mozilla::Some( + JS::PropertyDescriptor::Data( + jsTypeFactory(cx, item), + {JS::PropertyAttribute::Writable, JS::PropertyAttribute::Enumerable} + ) + )); + } + return true; +} + +void PyDictOrObjectProxyHandler::handleFinalize(JSObject *proxy) { + // We cannot call Py_DECREF here when shutting down as the thread state is gone. + // Then, when shutting down, there is only on reference left, and we don't need + // to free the object since the entire process memory is being released. + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + + if (Py_REFCNT(self) > 1) { + Py_DECREF(self); + } +} \ No newline at end of file diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index e885aaa1..56a47820 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -27,64 +27,20 @@ const char PyDictProxyHandler::family = 0; - -static bool object_toString(JSContext *cx, unsigned argc, JS::Value *vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - - args.rval().setString(JS_NewStringCopyZ(cx, "[object Object]")); - return true; -} - -static bool object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp) { - return object_toString(cx, argc, vp); -} - -static bool object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - - JS::RootedObject proxy(cx, JS::ToObject(cx, args.thisv())); - if (!proxy) { - return false; - } +bool PyDictProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + PyObject *keys = PyDict_Keys(self); - // return ref to self - args.rval().set(jsTypeFactory(cx, self)); - return true; -} - - -static JSMethodDef object_methods[] = { - {"toString", object_toString, 0}, - {"toLocaleString", object_toLocaleString, 0}, - {"valueOf", object_valueOf, 0}, - {NULL, NULL, 0} -}; - - -bool PyDictProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { - PyObject *keys = PyDict_Keys(pyObject); size_t length = PyList_Size(keys); - if (!props.reserve(length)) { - return false; // out of memory - } - for (size_t i = 0; i < length; i++) { - PyObject *key = PyList_GetItem(keys, i); - JS::RootedId jsId(cx); - if (!keyToId(key, &jsId)) { - // TODO (Caleb Aikens): raise exception here - return false; // key is not a str or int - } - props.infallibleAppend(jsId); - } - return true; + return handleOwnPropertyKeys(cx, keys, length, props); } bool PyDictProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult &result) const { PyObject *attrName = idToKey(cx, id); - if (PyDict_DelItem(pyObject, attrName) < 0) { + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (PyDict_DelItem(self, attrName) < 0) { return result.failCantDelete(); // raises JS exception } return result.succeed(); @@ -99,42 +55,11 @@ bool PyDictProxyHandler::getOwnPropertyDescriptor( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::MutableHandle> desc ) const { - // see if we're calling a function - if (id.isString()) { - for (size_t index = 0;; index++) { - bool isThatFunction; - const char *methodName = object_methods[index].name; - if (methodName == NULL) { // reached end of list - break; - } - else if (JS_StringEqualsAscii(cx, id.toString(), methodName, &isThatFunction) && isThatFunction) { - JSFunction *newFunction = JS_NewFunction(cx, object_methods[index].call, object_methods[index].nargs, 0, NULL); - if (!newFunction) return false; - JS::RootedObject funObj(cx, JS_GetFunctionObject(newFunction)); - desc.set(mozilla::Some( - JS::PropertyDescriptor::Data( - JS::ObjectValue(*funObj), - {JS::PropertyAttribute::Enumerable} - ) - )); - return true; - } - } - } - PyObject *attrName = idToKey(cx, id); - PyObject *item = PyDict_GetItemWithError(pyObject, attrName); - if (!item) { // NULL if the key is not present - desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys - } else { - desc.set(mozilla::Some( - JS::PropertyDescriptor::Data( - jsTypeFactory(cx, item), - {JS::PropertyAttribute::Writable, JS::PropertyAttribute::Enumerable} - ) - )); - } - return true; + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + PyObject *item = PyDict_GetItemWithError(self, attrName); + + return handleGetOwnPropertyDescriptor(cx, id, desc, item); } bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, @@ -143,7 +68,8 @@ bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId JS::RootedValue *rootedV = new JS::RootedValue(cx, v); PyObject *attrName = idToKey(cx, id); JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - if (PyDict_SetItem(pyObject, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (PyDict_SetItem(self, attrName, pyTypeFactory(cx, global, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); @@ -157,7 +83,8 @@ bool PyDictProxyHandler::enumerate(JSContext *cx, JS::HandleObject proxy, bool PyDictProxyHandler::hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const { PyObject *attrName = idToKey(cx, id); - *bp = PyDict_Contains(pyObject, attrName) == 1; + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + *bp = PyDict_Contains(self, attrName) == 1; return true; } @@ -168,13 +95,7 @@ bool PyDictProxyHandler::getOwnEnumerablePropertyKeys( } void PyDictProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { - // We cannot call Py_DECREF here when shutting down as the thread state is gone. - // Then, when shutting down, there is only on reference left, and we don't need - // to free the object since the entire process memory is being released. - PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - if (Py_REFCNT(self) > 1) { - Py_DECREF(self); - } + return handleFinalize(proxy); } bool PyDictProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 9acb8a01..1b27aed9 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -1960,12 +1960,13 @@ bool PyListProxyHandler::getOwnPropertyDescriptor( } } + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); // "length" property bool isLengthProperty; if (id.isString() && JS_StringEqualsLiteral(cx, id.toString(), "length", &isLengthProperty) && isLengthProperty) { desc.set(mozilla::Some( JS::PropertyDescriptor::Data( - JS::Int32Value(PyList_Size(pyObject)) + JS::Int32Value(PyList_Size(self)) ) )); return true; @@ -2018,7 +2019,7 @@ bool PyListProxyHandler::getOwnPropertyDescriptor( // item Py_ssize_t index; PyObject *item; - if (idToIndex(cx, id, &index) && (item = PyList_GetItem(pyObject, index))) { + if (idToIndex(cx, id, &index) && (item = PyList_GetItem(self, index))) { desc.set(mozilla::Some( JS::PropertyDescriptor::Data( jsTypeFactory(cx, item), @@ -2061,7 +2062,8 @@ bool PyListProxyHandler::defineProperty( JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); JS::RootedValue *itemV = new JS::RootedValue(cx, desc.value()); PyObject *item = pyTypeFactory(cx, global, itemV)->getPyObject(); - if (PyList_SetItem(pyObject, index, item) < 0) { + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (PyList_SetItem(self, index, item) < 0) { return result.failBadIndex(); } return result.succeed(); @@ -2069,7 +2071,8 @@ bool PyListProxyHandler::defineProperty( bool PyListProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { // Modified from https://hg.mozilla.org/releases/mozilla-esr102/file/3b574e1/dom/base/RemoteOuterWindowProxy.cpp#l137 - int32_t length = PyList_Size(pyObject); + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + int32_t length = PyList_Size(self); if (!props.reserve(length + 1)) { return false; } @@ -2084,12 +2087,13 @@ bool PyListProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, bool PyListProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::ObjectOpResult &result) const { Py_ssize_t index; + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); if (!idToIndex(cx, id, &index)) { return result.failBadIndex(); // report failure } // Set to undefined instead of actually deleting it - if (PyList_SetItem(pyObject, index, Py_None) < 0) { + if (PyList_SetItem(self, index, Py_None) < 0) { return result.failCantDelete(); // report failure } return result.succeed(); // report success diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index a4b067e9..66c03ced 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -40,20 +40,7 @@ bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy size_t length = PyList_Size(nonDunderKeys); - if (!props.reserve(length)) { - return false; // out of memory - } - - for (size_t i = 0; i < length; i++) { - PyObject *key = PyList_GetItem(nonDunderKeys, i); - JS::RootedId jsId(cx); - if (!keyToId(key, &jsId)) { - // TODO (Caleb Aikens): raise exception here - return false; // key is not a str or int - } - props.infallibleAppend(jsId); - } - return true; + return handleOwnPropertyKeys(cx, nonDunderKeys, length, props); } bool PyObjectProxyHandler::delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, @@ -78,17 +65,8 @@ bool PyObjectProxyHandler::getOwnPropertyDescriptor( PyObject *attrName = idToKey(cx, id); PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); PyObject *item = PyObject_GetAttr(self, attrName); - if (!item) { // NULL if the key is not present - desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys - } else { - desc.set(mozilla::Some( - JS::PropertyDescriptor::Data( - jsTypeFactory(cx, item), - {JS::PropertyAttribute::Writable, JS::PropertyAttribute::Enumerable} - ) - )); - } - return true; + + return handleGetOwnPropertyDescriptor(cx, id, desc, item); } bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, @@ -124,13 +102,7 @@ bool PyObjectProxyHandler::getOwnEnumerablePropertyKeys( } void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { - // We cannot call Py_DECREF here when shutting down as the thread state is gone. - // Then, when shutting down, there is only on reference left, and we don't need - // to free the object since the entire process memory is being released. - PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - if (Py_REFCNT(self) > 1) { - Py_DECREF(self); - } + return handleFinalize(proxy); } bool PyObjectProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, @@ -139,4 +111,10 @@ bool PyObjectProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, JS::ObjectOpResult &result) const { // Block direct `Object.defineProperty` since we already have the `set` method return result.failInvalidDescriptor(); +} + +bool PyObjectProxyHandler::getBuiltinClass(JSContext *cx, JS::HandleObject proxy, + js::ESClass *cls) const { + *cls = js::ESClass::Object; + return true; } \ No newline at end of file diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 6f1c2b23..fba88e2a 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -45,6 +45,10 @@ #define LOW_SURROGATE_END 0xDFFF #define BMP_END 0x10000 +static PyDictProxyHandler pyDictProxyHandler; +static PyObjectProxyHandler pyObjectProxyHandler; +static PyListProxyHandler pyListProxyHandler; + std::unordered_map charToPyObjectMap; // a map of char16_t buffers to their corresponding PyObjects, used when finalizing JSExternalStrings @@ -209,11 +213,11 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { if (PyList_Check(object)) { JS::RootedObject arrayPrototype(cx); JS_GetClassPrototype(cx, JSProto_Array, &arrayPrototype); // so that instanceof will work, not that prototype methods will - proxy = js::NewProxyObject(cx, new PyListProxyHandler(object), v, arrayPrototype.get()); + proxy = js::NewProxyObject(cx, &pyListProxyHandler, v, arrayPrototype.get()); } else { JS::RootedObject objectPrototype(cx); JS_GetClassPrototype(cx, JSProto_Object, &objectPrototype); // so that instanceof will work, not that prototype methods will - proxy = js::NewProxyObject(cx, new PyDictProxyHandler(object), v, objectPrototype.get()); + proxy = js::NewProxyObject(cx, &pyDictProxyHandler, v, objectPrototype.get()); } Py_INCREF(object); JS::SetReservedSlot(proxy, PyObjectSlot, JS::PrivateValue(object)); @@ -234,7 +238,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { JS::RootedValue v(cx); JS::RootedObject objectPrototype(cx); JS_GetClassPrototype(cx, JSProto_Object, &objectPrototype); // so that instanceof will work, not that prototype methods will - JSObject *proxy = js::NewProxyObject(cx, new PyObjectProxyHandler(object), v, objectPrototype.get()); + JSObject *proxy = js::NewProxyObject(cx, &pyObjectProxyHandler, v, objectPrototype.get()); Py_INCREF(object); JS::SetReservedSlot(proxy, PyObjectSlot, JS::PrivateValue(object)); returnType.setObject(*proxy); diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index 901c7b80..517cfaa1 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -94,13 +94,13 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted JS_ValueToObject(cx, *rval, &obj); if (JS::GetClass(obj)->isProxyObject()) { if (js::GetProxyHandler(obj)->family() == &PyDictProxyHandler::family) { // this is one of our proxies for python dicts - return new DictType(((PyDictProxyHandler *)js::GetProxyHandler(obj))->pyObject); + return new DictType(JS::GetMaybePtrFromReservedSlot(obj, PyObjectSlot)); } if (js::GetProxyHandler(obj)->family() == &PyListProxyHandler::family) { // this is one of our proxies for python lists - return new ListType(((PyListProxyHandler *)js::GetProxyHandler(obj))->pyObject); + return new ListType(JS::GetMaybePtrFromReservedSlot(obj, PyObjectSlot)); } if (js::GetProxyHandler(obj)->family() == &PyObjectProxyHandler::family) { // this is one of our proxies for python objects - return new PyType(((PyObjectProxyHandler *)js::GetProxyHandler(obj))->pyObject); + return new PyType(JS::GetMaybePtrFromReservedSlot(obj, PyObjectSlot)); } } js::ESClass cls; From b9250f947d6a527ee1c0df56bdbe085f225a02e5 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 13 Feb 2024 15:43:26 -0500 Subject: [PATCH 074/170] fixed english --- tests/python/test_event_loop.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/python/test_event_loop.py b/tests/python/test_event_loop.py index a5d3cc83..3466857d 100644 --- a/tests/python/test_event_loop.py +++ b/tests/python/test_event_loop.py @@ -208,13 +208,13 @@ async def coro_to_throw1(): # TODO (Tom Tang): properly test unhandled rejection # await scheduled jobs on the Python event-loop - js_sleep = pm.eval("(second) => new Promise((resolve) => setTimeout(resolve, second*1000))") - def py_sleep(second): # asyncio.sleep has issues on Python 3.8 + js_sleep = pm.eval("(seconds) => new Promise((resolve) => setTimeout(resolve, seconds*1000))") + def py_sleep(seconds): # asyncio.sleep has issues on Python 3.8 loop = asyncio.get_running_loop() future = loop.create_future() - loop.call_later(second, lambda:future.set_result(None)) + loop.call_later(seconds, lambda:future.set_result(None)) return future - both_sleep = pm.eval("(js_sleep, py_sleep) => async (second) => { await js_sleep(second); await py_sleep(second) }")(js_sleep, py_sleep) + both_sleep = pm.eval("(js_sleep, py_sleep) => async (seconds) => { await js_sleep(seconds); await py_sleep(seconds) }")(js_sleep, py_sleep) await asyncio.wait_for(both_sleep(0.1), timeout=0.3) # won't be precisely 0.2s with pytest.raises(asyncio.exceptions.TimeoutError): await asyncio.wait_for(both_sleep(0.1), timeout=0.19) @@ -248,4 +248,4 @@ async def async_fn(): # making sure the async_fn is run return True - assert asyncio.run(async_fn()) + assert asyncio.run(async_fn()) \ No newline at end of file From dba06b3f32aaa5a87e84e9a3608c2873ef415b22 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 14 Feb 2024 11:27:20 -0500 Subject: [PATCH 075/170] test(PyObjectProxyHandler): write a test suit for PyObjectProxyHandler, fix bug where error was raised rather than returning undefined when accessing non-existent attribute on a python object from JavaScript --- src/PyObjectProxyHandler.cc | 3 + tests/python/test_objects.py | 138 +++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 tests/python/test_objects.py diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 66c03ced..1c548d47 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -65,6 +65,9 @@ bool PyObjectProxyHandler::getOwnPropertyDescriptor( PyObject *attrName = idToKey(cx, id); PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); PyObject *item = PyObject_GetAttr(self, attrName); + if (!item) { // clear error, we will be returning undefined in this case + PyErr_Clear(); + } return handleGetOwnPropertyDescriptor(cx, id, desc, item); } diff --git a/tests/python/test_objects.py b/tests/python/test_objects.py new file mode 100644 index 00000000..3e487554 --- /dev/null +++ b/tests/python/test_objects.py @@ -0,0 +1,138 @@ +import pythonmonkey as pm + +def test_eval_pyobjects(): + class MyClass: + pass + + o = MyClass() + proxy_o = pm.eval("(obj) => { return obj; }")(o) + assert o is proxy_o + +def test_eval_pyobjects_subobjects(): + class InnerClass: + def __init__(self): + self.c = 2 + + class OuterClass: + def __init__(self): + self.a = 1 + self.b = InnerClass() + + o = OuterClass() + + assert pm.eval("(obj) => { return obj.a; }")(o) == 1.0 + assert pm.eval("(obj) => { return obj.b; }")(o) is o.b + assert pm.eval("(obj) => { return obj.b.c; }")(o) == 2.0 + +def test_eval_pyobjects_cycle(): + class MyClass: + def __init__(self): + self.a = 1 + self.b = 2 + self.recursive = self + + o = MyClass() + + assert pm.eval("(obj) => { return obj.a; }")(o) == 1.0 + assert pm.eval("(obj) => { return obj.b; }")(o) == 2.0 + assert pm.eval("(obj) => { return obj.recursive; }")(o) is o.recursive + +def test_eval_pyobjects_proxy_get(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + assert pm.eval("(obj) => { return obj.a}")(o) == 42.0 + +def test_eval_pyobjects_proxy_set(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + pm.eval("(obj) => { obj.b = 43; }")(o) + assert o.b == 43.0 + +def test_eval_pyobjects_proxy_keys(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + assert pm.eval("(obj) => { return Object.keys(obj)[0]; }")(o) == 'a' + +def test_eval_pyobjects_proxy_delete(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + pm.eval("(obj) => { delete obj.a; }")(o) + assert not hasattr(o, 'a') + +def test_eval_pyobjects_proxy_has(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + assert pm.eval("(obj) => { return 'a' in obj; }")(o) + +def test_eval_pyobjects_proxy_not_extensible(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + assert not pm.eval("(o) => Object.isExtensible(o)")(o) + assert pm.eval("(o) => Object.preventExtensions(o) === o")(o) + +def test_instanceof_pyobject(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + assert pm.eval("(obj) => { return obj instanceof Object; }")(o) + +def test_pyobjects_not_instanceof_string(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + + assert not pm.eval("(obj) => { return obj instanceof String; }")(o) + +def test_pyobjects_valueOf(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + assert o is pm.eval("(obj) => { return obj.valueOf(); }")(o) + +def test_pyobjects_toString(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + assert '[object Object]' == pm.eval("(obj) => { return obj.toString(); }")(o) + +def test_pyobjects_toLocaleString(): + class MyClass: + def __init__(self): + self.a = 42 + + o = MyClass() + assert '[object Object]' == pm.eval("(obj) => { return obj.toLocaleString(); }")(o) + + \ No newline at end of file From 270218302df61f30d11c74c570e9fc0b5044b4de Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 14 Feb 2024 13:02:17 -0500 Subject: [PATCH 076/170] refactor(pyTypeFactory): refactor pyTypeFactory to use recursive calls for boxed objects --- src/pyTypeFactory.cc | 58 +++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index 517cfaa1..b9f05986 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -109,23 +109,20 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted cls = js::ESClass::Function; // In SpiderMonkey 115 ESR, bound function is no longer a JSFunction but a js::BoundFunctionObject. // js::ESClass::Function only assigns to JSFunction objects by JS::GetBuiltinClass. } + JS::RootedValue unboxed(cx); switch (cls) { - case js::ESClass::Boolean: { - // TODO (Caleb Aikens): refactor out all `js::Unbox` calls - // TODO (Caleb Aikens): refactor using recursive call to `pyTypeFactory` - JS::RootedValue unboxed(cx); - js::Unbox(cx, obj, &unboxed); - return new BoolType(unboxed.toBoolean()); - } - case js::ESClass::Date: { - return new DateType(cx, obj); - } - case js::ESClass::Promise: { - return new PromiseType(cx, obj); - } - case js::ESClass::Error: { - return new ExceptionType(cx, obj); - } + case js::ESClass::Boolean: + case js::ESClass::Number: + case js::ESClass::BigInt: + case js::ESClass::String: + js::Unbox(cx, obj, &unboxed); + return pyTypeFactory(cx, thisObj, &unboxed); + case js::ESClass::Date: + return new DateType(cx, obj); + case js::ESClass::Promise: + return new PromiseType(cx, obj); + case js::ESClass::Error: + return new ExceptionType(cx, obj); case js::ESClass::Function: { PyObject *pyFunc; FuncType *f; @@ -139,29 +136,12 @@ PyType *pyTypeFactory(JSContext *cx, JS::Rooted *thisObj, JS::Rooted } return f; } - case js::ESClass::Number: { - JS::RootedValue unboxed(cx); - js::Unbox(cx, obj, &unboxed); - return new FloatType(unboxed.toNumber()); - } - case js::ESClass::BigInt: { - JS::RootedValue unboxed(cx); - js::Unbox(cx, obj, &unboxed); - return new IntType(cx, unboxed.toBigInt()); - } - case js::ESClass::String: { - JS::RootedValue unboxed(cx); - js::Unbox(cx, obj, &unboxed); - return new StrType(cx, unboxed.toString()); - } - case js::ESClass::Array: { - return new ListType(cx, obj); - } - default: { - if (BufferType::isSupportedJsTypes(obj)) { // TypedArray or ArrayBuffer - // TODO (Tom Tang): ArrayBuffers have cls == js::ESClass::ArrayBuffer - return new BufferType(cx, obj); - } + case js::ESClass::Array: + return new ListType(cx, obj); + default: + if (BufferType::isSupportedJsTypes(obj)) { // TypedArray or ArrayBuffer + // TODO (Tom Tang): ArrayBuffers have cls == js::ESClass::ArrayBuffer + return new BufferType(cx, obj); } } return new DictType(cx, *rval); From 3de663de0873a2503bb0ae92e59d90dce8cf9e95 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 15 Feb 2024 09:04:52 -0500 Subject: [PATCH 077/170] chore(TODO): resolve various TODOs related to exception handling --- src/JSArrayProxy.cc | 4 ++-- src/JSObjectProxy.cc | 20 ++++++++++---------- src/PyDictOrObjectProxyHandler.cc | 3 +-- src/jsTypeFactory.cc | 2 -- tests/python/test_functions_this.py | 2 +- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 673efc22..735e614b 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -53,8 +53,8 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py { JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { - // TODO (Caleb Aikens): raise exception here - return NULL; // key is not a str or int + PyErr_SetString(PyExc_AttributeError, "JSArrayProxy property name must be of type str or int"); + return NULL; } // look through the methods for dispatch and return key if no method found diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 7e29fdd7..4a4e8b3a 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -71,7 +71,7 @@ Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *s JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { - // @TODO (Caleb Aikens) raise exception here + PyErr_Format(SpiderMonkeyError, "Failed to retrieve properties when calculating length of: %S", self); return -1; } return props.length(); @@ -81,8 +81,8 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, { JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { - // TODO (Caleb Aikens): raise exception here - return NULL; // key is not a str or int + PyErr_SetString(PyExc_AttributeError, "JSObjectProxy property name must be of type str or int"); + return NULL; } // look through the methods for dispatch @@ -93,7 +93,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); JS::RootedObject *thisObj = new JS::RootedObject(GLOBAL_CX, self->jsObject); // if value is a JSFunction, bind `this` to self - /* TODO (Caleb Aikens) its potentially problematic to bind it like this since if the function + /* (Caleb Aikens) its potentially problematic to bind it like this since if the function * ever gets assigned to another object like so: * * jsObjA.func = jsObjB.func @@ -134,8 +134,8 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, { JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { - // TODO (Caleb Aikens): raise exception here - return -1; // key is not a str or int + PyErr_SetString(PyExc_AttributeError, "JSObjectProxy property name must be of type str or int"); + return -1; } JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); @@ -146,7 +146,7 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, Py { JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { // invalid key - // TODO (Caleb Aikens): raise exception here + PyErr_SetString(PyExc_AttributeError, "JSObjectProxy property name must be of type str or int"); return -1; } @@ -208,7 +208,7 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { - // @TODO (Caleb Aikens) raise exception here + PyErr_Format(SpiderMonkeyError, "During rich comparison, failed to retrieve property keys of: %S", self); return NULL; } @@ -591,7 +591,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy skip_optional: JS::RootedId id(GLOBAL_CX); if (!keyToId(key, &id)) { - // TODO (Caleb Aikens): raise exception here PyObject *seq = PyTuple_New(length); + PyErr_SetString(PyExc_AttributeError, "JSObjectProxy property name must be of type str or int"); return NULL; } @@ -618,7 +618,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(JSObjectPro JS::RootedIdVector props(GLOBAL_CX); if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { - // @TODO (Caleb Aikens) raise exception here + PyErr_Format(SpiderMonkeyError, "During clear(), failed to retrieve property keys of: %S", self); return NULL; } diff --git a/src/PyDictOrObjectProxyHandler.cc b/src/PyDictOrObjectProxyHandler.cc index 6f56dda7..3b41b5a4 100644 --- a/src/PyDictOrObjectProxyHandler.cc +++ b/src/PyDictOrObjectProxyHandler.cc @@ -57,8 +57,7 @@ bool PyDictOrObjectProxyHandler::handleOwnPropertyKeys(JSContext *cx, PyObject * PyObject *key = PyList_GetItem(keys, i); JS::RootedId jsId(cx); if (!keyToId(key, &jsId)) { - // TODO (Caleb Aikens): raise exception here - return false; // key is not a str or int + continue; // skip over keys that are not str or int } props.infallibleAppend(jsId); } diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index fba88e2a..c89642e5 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -299,7 +299,6 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { setPyException(cx); return false; } - // @TODO (Caleb Aikens) need to check for python exceptions here callargs.rval().set(jsTypeFactory(cx, pyRval)); return true; } @@ -320,7 +319,6 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { setPyException(cx); return false; } - // @TODO (Caleb Aikens) need to check for python exceptions here callargs.rval().set(jsTypeFactory(cx, pyRval)); if (PyErr_Occurred()) { setPyException(cx); diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index c2eb0199..0d376d09 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -115,7 +115,7 @@ class Class: return jsObj.jsMethod(4); } """)(jsObj) - assert pyObj == result[0] and 4 == result[1] #TODO (Caleb Aikens) should `this` be `pyObj` or `jsObj` here? + assert pyObj == result[0] and 4 == result[1] #require def test_require_correct_this(): From cb57161b77c8c7999acc7941214d9b75c7b83b27 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 15 Feb 2024 13:50:17 -0500 Subject: [PATCH 078/170] more precise error reporting --- src/setSpiderMonkeyException.cc | 2 +- tests/python/test_event_loop.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index 9826771f..a4f8c72b 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -41,7 +41,7 @@ PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionS std::string offsetSpaces(errorReport->tokenOffset(), ' '); // number of spaces equal to tokenOffset std::string linebuf; // the offending JS line of code (can be empty) - outStrStream << "Error in file " << errorReport->filename << ", on line " << errorReport->lineno << ":\n"; + outStrStream << "Error in file " << errorReport->filename << ", on line " << errorReport->lineno << ", column " << errorReport->column << ":\n"; if (errorReport->linebuf()) { std::wstring_convert, char16_t> convert; std::u16string u16linebuf(errorReport->linebuf()); diff --git a/tests/python/test_event_loop.py b/tests/python/test_event_loop.py index 3466857d..73592aaf 100644 --- a/tests/python/test_event_loop.py +++ b/tests/python/test_event_loop.py @@ -198,7 +198,7 @@ async def coro_to_throw1(): # FIXME (Tom Tang): We currently handle Promise exceptions by converting the object thrown to a Python Exception object through `pyTypeFactory` # # await pm.eval("Promise.resolve().then(()=>{ throw {a:1,toString(){return'anything'}} })") - with pytest.raises(pm.SpiderMonkeyError, match="on line 1:\nTypeError: undefined has no properties"): # not going through the conversion + with pytest.raises(pm.SpiderMonkeyError, match="on line 1, column 31:\nTypeError: undefined has no properties"): # not going through the conversion await pm.eval("Promise.resolve().then(()=>{ (undefined).prop })") # TODO (Tom Tang): Modify this testcase once we support ES2020-style dynamic import From e28e16069423fbf382345db8b64968704dc3d89c Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 15 Feb 2024 13:51:56 -0500 Subject: [PATCH 079/170] simplification --- src/jsTypeFactory.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index c7ce7ac1..f738b658 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -223,9 +223,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { returnType.setNull(); } else if (PythonAwaitable_Check(object)) { - PromiseType *p = new PromiseType(object); - JSObject *promise = p->toJsPromise(cx); // may return null - returnType.setObjectOrNull(promise); + returnType.setObjectOrNull((new PromiseType(object))->toJsPromise(cx)); } else { JS::RootedValue v(cx); From 9416d920670322e481bbb107e91d53e963a9ad43 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 15 Feb 2024 16:17:31 -0500 Subject: [PATCH 080/170] fix(JSFunctionProxy): correctly error out if an error occurs during a call to a JSFunctionProxy --- src/JSFunctionProxy.cc | 4 ++++ src/JSMethodProxy.cc | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index 49325453..6a8c93e1 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -55,5 +55,9 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, return NULL; } + if (PyErr_Occurred()) { + return NULL; + } + return pyTypeFactory(cx, &thisObj, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index 813c6703..c86c87fe 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -66,6 +66,10 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO return NULL; } + if (PyErr_Occurred()) { + return NULL; + } + JS::RootedObject globalObj(cx, JS::CurrentGlobalOrNull(cx)); return pyTypeFactory(cx, &globalObj, jsReturnVal)->getPyObject(); } \ No newline at end of file From cdceef1b9cfc6ad467da805c96e98acb55984426 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 16 Feb 2024 11:15:48 -0500 Subject: [PATCH 081/170] crash fix --- src/PyEventLoop.cc | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/PyEventLoop.cc b/src/PyEventLoop.cc index 160105a8..e24ee3cd 100644 --- a/src/PyEventLoop.cc +++ b/src/PyEventLoop.cc @@ -17,15 +17,19 @@ * @brief Wrapper to decrement the counter of queueing event-loop jobs after the job finishes */ static PyObject *eventLoopJobWrapper(PyObject *jobFn, PyObject *Py_UNUSED(_)) { - PyObject *ret = PyObject_CallObject(jobFn, NULL); // jobFn() - Py_XDECREF(ret); // don't care about its return value + PyObject *ret = PyObject_CallObject(jobFn, NULL); PyEventLoop::_locker->decCounter(); + if (!ret) { + return NULL; + } + Py_DECREF(ret); if (PyErr_Occurred()) { return NULL; } else { Py_RETURN_NONE; } } + static PyMethodDef jobWrapperDef = {"eventLoopJobWrapper", eventLoopJobWrapper, METH_NOARGS, NULL}; PyEventLoop::AsyncHandle PyEventLoop::enqueue(PyObject *jobFn) { @@ -33,7 +37,7 @@ PyEventLoop::AsyncHandle PyEventLoop::enqueue(PyObject *jobFn) { PyObject *wrapper = PyCFunction_New(&jobWrapperDef, jobFn); // Enqueue job to the Python event-loop // https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_soon - PyObject *asyncHandle = PyObject_CallMethod(_loop, "call_soon_threadsafe", "O", wrapper); // https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue + PyObject *asyncHandle = PyObject_CallMethod(_loop, "call_soon_threadsafe", "O", wrapper); return PyEventLoop::AsyncHandle(asyncHandle); } @@ -42,7 +46,7 @@ PyEventLoop::AsyncHandle PyEventLoop::enqueueWithDelay(PyObject *jobFn, double d PyObject *wrapper = PyCFunction_New(&jobWrapperDef, jobFn); // Schedule job to the Python event-loop // https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.call_later - PyObject *asyncHandle = PyObject_CallMethod(_loop, "call_later", "dO", delaySeconds, wrapper); // https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue + PyObject *asyncHandle = PyObject_CallMethod(_loop, "call_later", "dO", delaySeconds, wrapper); if (asyncHandle == nullptr) { PyErr_Print(); // RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one } @@ -74,6 +78,7 @@ PyEventLoop::Future PyEventLoop::ensureFuture(PyObject *awaitable) { Py_DECREF(args); Py_DECREF(kwargs); + // Py_INCREF(futureObj); return PyEventLoop::Future(futureObj); } @@ -127,7 +132,7 @@ PyThreadState *PyEventLoop::_getMainThread() { // The last element in the linked-list of threads associated with the main interpreter should be the main thread // (The first element is the current thread, see https://github.com/python/cpython/blob/7cb3a44/Python/pystate.c#L291-L293) PyInterpreterState *interp = PyInterpreterState_Main(); - PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); // https://docs.python.org/3/c-api/init.html#c.PyInterpreterState_ThreadHead + PyThreadState *tstate = PyInterpreterState_ThreadHead(interp); while (PyThreadState_Next(tstate) != nullptr) { tstate = PyThreadState_Next(tstate); } @@ -138,7 +143,7 @@ PyThreadState *PyEventLoop::_getMainThread() { PyThreadState *PyEventLoop::_getCurrentThread() { // `PyThreadState_Get` is used under the hood of the Python `asyncio.get_running_loop` method, // see https://github.com/python/cpython/blob/7cb3a44/Modules/_asynciomodule.c#L234 - return PyThreadState_Get(); // https://docs.python.org/3/c-api/init.html#c.PyThreadState_Get + return PyThreadState_Get(); } /* static */ From d6b8abdf286f6bdb0e970cd4192ec55740e5c7b9 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 16 Feb 2024 12:34:11 -0500 Subject: [PATCH 082/170] fix(FinalizationRegistry): set the FinalizationRegistry cleanup callback so that it actually works, and add a test that ensures python functions passed through JS are finalized correctly --- src/modules/pythonmonkey/pythonmonkey.cc | 13 ++++++++++- tests/python/test_functions_this.py | 28 +++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 373918ba..58b0ff15 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -56,6 +56,15 @@ bool functionRegistryCallback(JSContext *cx, unsigned int argc, JS::Value *vp) { return true; } +static void cleanupFinalizationRegistry(JSFunction *callback, JSObject *global [[maybe_unused]], void *user_data [[maybe_unused]]) { + JS::ExposeObjectToActiveJS(JS_GetFunctionObject(callback)); + JS::RootedFunction rootedCallback(GLOBAL_CX, callback); + JS::RootedValue unused(GLOBAL_CX); + if (!JS_CallFunction(GLOBAL_CX, NULL, rootedCallback, JS::HandleValueArray::empty(), &unused)) { + setSpiderMonkeyException(GLOBAL_CX); + } +} + typedef struct { PyObject_HEAD } NullObject; @@ -471,7 +480,7 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) JS::RealmCreationOptions creationOptions = JS::RealmCreationOptions(); JS::RealmBehaviors behaviours = JS::RealmBehaviors(); - creationOptions.setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithCleanupSome); // enable FinalizationRegistry + creationOptions.setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithoutCleanupSome); // enable FinalizationRegistry creationOptions.setIteratorHelpersEnabled(true); JS::RealmOptions options = JS::RealmOptions(creationOptions, behaviours); static JSClass globalClass = {"global", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps}; @@ -645,5 +654,7 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) jsFunctionRegistry = new JS::PersistentRootedObject(GLOBAL_CX); jsFunctionRegistry->set(registryObject); + JS::SetHostCleanupFinalizationRegistryCallback(GLOBAL_CX, cleanupFinalizationRegistry, NULL); + return pyModule; } diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index 0d376d09..4ec6f13c 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -1,5 +1,7 @@ import pythonmonkey as pm import subprocess +import weakref +import sys def test_python_functions_self(): def pyFunc(param): @@ -170,4 +172,28 @@ class Rectangle2 { r2 = pm.new(example.Rectangle2)(1,2) assert r2.getArea() == 2 - assert r2.getThis() == r2 \ No newline at end of file + assert r2.getThis() == r2 + +def test_function_finalization(): + ref = [] + starting_ref_count = [] + + def outerScope(): + def pyFunc(): + return 42 + + ref.append(weakref.ref(pyFunc)) + starting_ref_count.append(sys.getrefcount(pyFunc)) + assert 42 == pm.eval("(func) => func()")(pyFunc) + + assert ref[0]() is pyFunc + current_ref_count = sys.getrefcount(pyFunc) + assert current_ref_count == starting_ref_count[0] + 1 + + outerScope() + pm.collect() # this should collect the JS proxy to pyFunc, which should decref pyFunc + current_ref_count = sys.getrefcount(ref[0]()) + #pytest seems to hold an additional reference on inner functions, so we assert here that the refcount + #is what it was when pyFunc was defined. In a non-test environment, pyFunc should be collected and ref[0]() should be None + #at this point + assert current_ref_count == starting_ref_count[0] \ No newline at end of file From 572f3f24c4442862af590be6e28a897e8a33f1be Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Fri, 16 Feb 2024 14:51:25 -0500 Subject: [PATCH 083/170] fix(eval): remove the need to permanently root JSFunctions returned by eval. TODO: still need to solve JSStrings being permanently rooted when returned by eval. --- src/modules/pythonmonkey/pythonmonkey.cc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 58b0ff15..0dd614de 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -373,17 +373,13 @@ static PyObject *eval(PyObject *self, PyObject *args) { return NULL; } - // TODO: Find a better way to destroy the root when necessary (when the returned Python object is GCed). + // TODO: Find a way to root strings for the lifetime of a proxying python string js::ESClass cls = js::ESClass::Other; // placeholder if `rval` is not a JSObject if (rval->isObject()) { JS::GetBuiltinClass(GLOBAL_CX, JS::RootedObject(GLOBAL_CX, &rval->toObject()), &cls); - if (JS_ObjectIsBoundFunction(&rval->toObject())) { - cls = js::ESClass::Function; // In SpiderMonkey 115 ESR, bound function is no longer a JSFunction but a js::BoundFunctionObject. - } } - bool rvalIsFunction = cls == js::ESClass::Function; // function object - bool rvalIsString = rval->isString() || cls == js::ESClass::String; // string primitive or boxed String object - if (!(rvalIsFunction || rvalIsString)) { // rval may be a JS function or string which must be kept alive. + + if (!(rval->isString() || cls == js::ESClass::String)) { // rval may be a string which must be kept alive. delete rval; } From 7a8ac9b071ae9f37f4126c691c99361fceefc4d6 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Mon, 19 Feb 2024 10:23:14 -0500 Subject: [PATCH 084/170] chore(PyListProxyHandler): remove unnecessary error check from array_sort --- src/PyListProxyHandler.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 1b27aed9..5286d274 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -1592,9 +1592,6 @@ static int invokeCallBack(PyObject *list, int index, JS::HandleValue leftValue, // Adapted from Kernigan&Ritchie's C book static void quickSort(PyObject *list, int left, int right, JSContext *cx, JS::HandleFunction callBack) { - if (PyErr_Occurred()) { - return; - } if (left >= right) { // base case From 54cb0967edce4c28b99c4171ec22492dedc80d01 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 22 Feb 2024 11:43:44 -0500 Subject: [PATCH 085/170] cleanup --- tests/python/test_dict_methods.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/python/test_dict_methods.py b/tests/python/test_dict_methods.py index dbb46539..f70c3854 100644 --- a/tests/python/test_dict_methods.py +++ b/tests/python/test_dict_methods.py @@ -176,7 +176,6 @@ def test_keys_iter(): result.append(i) assert result == ['a', 'b'] -# TODO causes crash def test_keys_iter_reverse(): obj = pm.eval("({ a: 123, b: 'test' })") result = [] From 7172cff5c80c0576c0af0deef7fdf4d0812f7a06 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 23 Feb 2024 11:08:40 -0500 Subject: [PATCH 086/170] jsObject/jsArray should be persistentrooted --- include/JSArrayProxy.hh | 2 +- include/JSObjectProxy.hh | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index efc53afa..f0e4f2b0 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -24,7 +24,7 @@ */ typedef struct { PyListObject list; - JS::RootedObject jsArray; + JS::PersistentRootedObject jsArray; } JSArrayProxy; /** diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 1bf8ef49..6386ae0e 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -23,7 +23,7 @@ */ typedef struct { PyDictObject dict; - JS::RootedObject jsObject; + JS::PersistentRootedObject jsObject; } JSObjectProxy; /** @@ -315,7 +315,6 @@ static PyMethodDef JSObjectProxy_methods[] = { {"get", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_get_method, METH_FASTCALL, dict_get__doc__}, {"setdefault", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method, METH_FASTCALL, dict_setdefault__doc__}, {"pop", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method, METH_FASTCALL, dict_pop__doc__}, - // {"popitem", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_popitem_method, METH_NOARGS, ""}, TODO not popular and quite a bit strange {"clear", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method, METH_NOARGS, copy__doc__}, {"update", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_update_method, METH_VARARGS | METH_KEYWORDS, update__doc__}, From 28fd039e3e0226dd7c11797505c698061cfdd527 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Mon, 26 Feb 2024 15:33:23 -0500 Subject: [PATCH 087/170] cleanup unused methods --- include/pyTypeFactory.hh | 14 -------------- src/pyTypeFactory.cc | 38 -------------------------------------- 2 files changed, 52 deletions(-) diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index c122161b..8c15b866 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -17,14 +17,6 @@ #include -/** @brief Function that takes an arbitrary PyObject* and returns a corresponding PyType* object - - @author Caleb Aikens - @date August 2022 - - @param object - Pointer to the PyObject who's type and value we wish to encapsulate - */ -PyType *pyTypeFactory(PyObject *object); /** * @brief Function that takes a JS::Value and returns a corresponding PyType* object, doing shared memory management when necessary @@ -36,10 +28,4 @@ PyType *pyTypeFactory(PyObject *object); */ PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval); -/** - * @brief same to pyTypeFactory, but it's guaranteed that no error would be set on the Python error stack, instead - * returning `pythonmonkey.null` on error - */ -PyType *pyTypeFactorySafe(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval); - #endif \ No newline at end of file diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index 576bf95d..323be613 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -39,33 +39,6 @@ #include -PyType *pyTypeFactory(PyObject *object) { - PyType *pyType; - - if (PyLong_Check(object)) { - pyType = new IntType(object); - } - else if (PyUnicode_Check(object)) { - pyType = new StrType(object); - } - else if (PyFunction_Check(object)) { - pyType = new FuncType(object); - } - else if (PyDict_Check(object)) { - pyType = new DictType(object); - } - else if (PyList_Check(object)) { - pyType = new ListType(object); - } - else if (PyTuple_Check(object)) { - pyType = new TupleType(object); - } - else { - return nullptr; - } - - return pyType; -} PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval) { if (rval.isUndefined()) { @@ -177,15 +150,4 @@ PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue r errorString += JS_EncodeStringToUTF8(cx, str).get(); PyErr_SetString(PyExc_TypeError, errorString.c_str()); return NULL; -} - -PyType *pyTypeFactorySafe(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval) { - PyType *v = pyTypeFactory(cx, thisObj, rval); - if (PyErr_Occurred()) { - // Clear Python error - PyErr_Clear(); - // Return `pythonmonkey.null` on error - return new NullType(); - } - return v; } \ No newline at end of file From b0a8e0f4d9623b89aedd14f40fed3aafe11beb43 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Mon, 26 Feb 2024 16:03:23 -0500 Subject: [PATCH 088/170] cleanup and minor improvements --- src/JobQueue.cc | 16 ++++++++-------- src/PromiseType.cc | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/JobQueue.cc b/src/JobQueue.cc index 799c5678..9aab22c3 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -33,12 +33,13 @@ bool JobQueue::enqueuePromiseJob(JSContext *cx, JS::RootedValue jobv(cx, JS::ObjectValue(*job)); PyObject *callback = pyTypeFactory(cx, global, jobv)->getPyObject(); - // Inform the JS runtime that the job queue is no longer empty - JS::JobQueueMayNotBeEmpty(cx); - // Send job to the running Python event-loop PyEventLoop loop = PyEventLoop::getRunningLoop(); if (!loop.initialized()) return false; + + // Inform the JS runtime that the job queue is no longer empty + JS::JobQueueMayNotBeEmpty(cx); + loop.enqueue(callback); return true; @@ -54,8 +55,7 @@ bool JobQueue::empty() const { } js::UniquePtr JobQueue::saveJobQueue(JSContext *cx) { - auto saved = js::MakeUnique(); - return saved; + return js::MakeUnique(); } bool JobQueue::init(JSContext *cx) { @@ -74,7 +74,7 @@ static PyObject *callDispatchFunc(PyObject *dispatchFuncTuple, PyObject *Py_UNUS static PyMethodDef callDispatchFuncDef = {"JsDispatchCallable", callDispatchFunc, METH_NOARGS, NULL}; bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable) { - JSContext *cx = (JSContext *)closure; // `closure` is provided in `JS::InitDispatchToEventLoop` call + JSContext *cx = (JSContext *)closure; // The `dispatchToEventLoop` function is running in a helper thread, so // we must acquire the Python GIL (global interpreter lock) @@ -84,11 +84,11 @@ bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable PyObject *dispatchFuncTuple = PyTuple_Pack(2, PyLong_FromVoidPtr(cx), PyLong_FromVoidPtr(dispatchable)); PyObject *pyFunc = PyCFunction_New(&callDispatchFuncDef, dispatchFuncTuple); - // Avoid using the JS helper thread to send jobs to event-loop as it may cause deadlock + // Avoid using the current, JS helper thread to send jobs to event-loop as it may cause deadlock PyThread_start_new_thread((void (*)(void *)) &sendJobToMainLoop, pyFunc); PyGILState_Release(gstate); - return true; // dispatchable must eventually run + return true; } bool sendJobToMainLoop(PyObject *pyFunc) { diff --git a/src/PromiseType.cc b/src/PromiseType.cc index d34202d2..e6014622 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -31,7 +31,7 @@ static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { // Get the Promise state JS::Value promiseObjVal = js::GetFunctionNativeReserved(&args.callee(), PROMISE_OBJ_SLOT); - JS::RootedObject promise = JS::RootedObject(cx, &promiseObjVal.toObject()); + JS::RootedObject promise(cx, &promiseObjVal.toObject()); JS::PromiseState state = JS::GetPromiseState(promise); // Convert the Promise's result (either fulfilled resolution or rejection reason) to a Python object @@ -77,7 +77,7 @@ PromiseType::PromiseType(JSContext *cx, JS::HandleObject promise) { // Callbacks to settle the Python asyncio.Future once the JS Promise is resolved JS::RootedObject onResolved = JS::RootedObject(cx, (JSObject *)js::NewFunctionWithReserved(cx, onResolvedCb, 1, 0, NULL)); - js::SetFunctionNativeReserved(onResolved, PY_FUTURE_OBJ_SLOT, JS::PrivateValue(future.getFutureObject())); // put the address of the Python object in private slot so we can access it later + js::SetFunctionNativeReserved(onResolved, PY_FUTURE_OBJ_SLOT, JS::PrivateValue(future.getFutureObject())); js::SetFunctionNativeReserved(onResolved, PROMISE_OBJ_SLOT, JS::ObjectValue(*promise)); AddPromiseReactions(cx, promise, onResolved, onResolved); @@ -87,7 +87,7 @@ PromiseType::PromiseType(JSContext *cx, JS::HandleObject promise) { // Callback to resolve or reject the JS Promise when the Future is done static PyObject *futureOnDoneCallback(PyObject *futureCallbackTuple, PyObject *args) { JSContext *cx = (JSContext *)PyLong_AsVoidPtr(PyTuple_GetItem(futureCallbackTuple, 0)); - auto rootedPtr = (JS::PersistentRooted *)PyLong_AsVoidPtr(PyTuple_GetItem(futureCallbackTuple, 1)); + JS::PersistentRootedObject *rootedPtr = (JS::PersistentRootedObject *)PyLong_AsVoidPtr(PyTuple_GetItem(futureCallbackTuple, 1)); JS::HandleObject promise = *rootedPtr; PyObject *futureObj = PyTuple_GetItem(args, 0); // the callback is called with the Future object as its only argument // see https://docs.python.org/3.9/library/asyncio-future.html#asyncio.Future.add_done_callback From 593da2219e62ad6587fcb1764d317741d93abfeb Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Mon, 26 Feb 2024 16:41:05 -0500 Subject: [PATCH 089/170] dict conversion no longer needed now that we have proxies --- python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py b/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py index 7ab5ed24..6477de47 100644 --- a/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py +++ b/python/pythonmonkey/builtin_modules/XMLHttpRequest-internal.py @@ -58,7 +58,6 @@ async def write(self, writer) -> None: body = bytes(body, "utf-8") # set default headers - headers=dict(headers) headers.setdefault("user-agent", f"Python/{platform.python_version()} PythonMonkey/{pm.__version__}") if timeoutMs > 0: From f7875789883a1d54092a2d020a0f8cc48dfc9e15 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 27 Feb 2024 12:54:50 -0500 Subject: [PATCH 090/170] improved setdefault performance --- include/JSObjectProxy.hh | 2 +- src/JSObjectProxy.cc | 48 ++++++++++++++++++++++++++-------------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 6386ae0e..3a2b0504 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -311,9 +311,9 @@ static PyNumberMethods JSObjectProxy_number_methods = { * */ static PyMethodDef JSObjectProxy_methods[] = { + {"setdefault", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method, METH_FASTCALL, dict_setdefault__doc__}, {"__getitem__", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_get, METH_O | METH_COEXIST, getitem__doc__}, {"get", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_get_method, METH_FASTCALL, dict_get__doc__}, - {"setdefault", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method, METH_FASTCALL, dict_setdefault__doc__}, {"pop", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method, METH_FASTCALL, dict_pop__doc__}, {"clear", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method, METH_NOARGS, clear__doc__}, {"copy", (PyCFunction)JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method, METH_NOARGS, copy__doc__}, diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 24c76406..a7baed14 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -63,14 +63,7 @@ Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *s return props.length(); } -PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, PyObject *key) -{ - JS::RootedId id(GLOBAL_CX); - if (!keyToId(key, &id)) { - // TODO (Caleb Aikens): raise exception here - return NULL; // key is not a str or int - } - +static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId id) { // look through the methods for dispatch for (size_t index = 0;; index++) { const char *methodName = JSObjectProxyType.tp_methods[index].ml_name; @@ -88,6 +81,17 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, } } +PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, PyObject *key) +{ + JS::RootedId id(GLOBAL_CX); + if (!keyToId(key, &id)) { + // TODO (Caleb Aikens): raise exception here + return NULL; // key is not a str or int + } + + return getKey(self, key, id); +} + int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, PyObject *key) { JS::RootedId id(GLOBAL_CX); @@ -100,6 +104,16 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, return value->isUndefined() ? 0 : 1; } +static inline void assignKeyValue(JSObjectProxy *self, PyObject *key, JS::HandleId id, PyObject *value) { + if (value) { // we are setting a value + JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value)); + JS_SetPropertyById(GLOBAL_CX, self->jsObject, id, jValue); + } else { // we are deleting a value + JS::ObjectOpResult ignoredResult; + JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); + } +} + int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, PyObject *key, PyObject *value) { JS::RootedId id(GLOBAL_CX); @@ -108,13 +122,7 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, Py return -1; } - if (value) { // we are setting a value - JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value)); - JS_SetPropertyById(GLOBAL_CX, self->jsObject, id, jValue); - } else { // we are deleting a value - JS::ObjectOpResult ignoredResult; - JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); - } + assignKeyValue(self, key, id, value); return 0; } @@ -531,9 +539,15 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method(JSObje skip_optional: - PyObject* value = JSObjectProxy_get(self, key); + JS::RootedId id(GLOBAL_CX); + if (!keyToId(key, &id)) { // invalid key + // TODO (Caleb Aikens): raise exception here + return NULL; + } + + PyObject *value = getKey(self, key, id); if (value == Py_None) { - JSObjectProxy_assign(self, key, default_value); + assignKeyValue(self, key, id, default_value); Py_XINCREF(default_value); return default_value; } From e248b4458a1c01e896b706f508ff6b9dccef5a84 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 27 Feb 2024 16:14:38 -0500 Subject: [PATCH 091/170] decref the result of python calls. Rooted type simplifications --- src/PyListProxyHandler.cc | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 73d8316a..745c840f 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -75,6 +75,7 @@ static bool array_pop(JSContext *cx, unsigned argc, JS::Value *vp) { } args.rval().set(jsTypeFactory(cx, result)); + Py_DECREF(result); return true; } @@ -206,6 +207,7 @@ static bool array_slice(JSContext *cx, unsigned argc, JS::Value *vp) { } args.rval().set(jsTypeFactory(cx, result)); + Py_DECREF(result); return true; } @@ -263,6 +265,7 @@ static bool array_indexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } args.rval().set(jsTypeFactory(cx, result)); + Py_DECREF(result); return true; } @@ -345,6 +348,7 @@ static bool array_splice(JSContext *cx, unsigned argc, JS::Value *vp) { } args.rval().set(jsTypeFactory(cx, deleted)); + Py_DECREF(deleted); return true; } @@ -560,6 +564,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } args.rval().set(jsTypeFactory(cx, result)); + Py_DECREF(result); return true; } @@ -906,35 +911,33 @@ static bool array_reduceRight(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedValue callBack(cx, callbackfn); JS::Rooted> jArgs(cx); - JS::RootedValue *accumulator; + JS::RootedValue accumulator(cx); Py_ssize_t len = PyList_GET_SIZE(self); if (args.length() > 1) { - accumulator = new JS::RootedValue(cx, args[1].get()); + accumulator.set(args[1].get()); } else { if (len == 0) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_EMPTY_ARRAY_REDUCE); return false; } - accumulator = new JS::RootedValue(cx, jsTypeFactory(cx, PyList_GetItem(self, len - 1))); + accumulator.set(jsTypeFactory(cx, PyList_GetItem(self, len - 1))); } for (int64_t index = args.length() > 1 ? len - 1 : len - 2; index >= 0; index--) { - jArgs[0].set(*accumulator); + jArgs[0].set(accumulator); jArgs[1].set(jsTypeFactory(cx, PyList_GetItem(self, index))); jArgs[2].setInt32(index); jArgs[3].set(selfValue); - if (!JS_CallFunctionValue(cx, nullptr, callBack, jArgs, accumulator)) { - delete accumulator; + if (!JS_CallFunctionValue(cx, nullptr, callBack, jArgs, &accumulator)) { return false; } } - args.rval().set(accumulator->get()); - delete accumulator; + args.rval().set(accumulator.get()); return true; } @@ -1436,12 +1439,12 @@ static bool array_join(JSContext *cx, unsigned argc, JS::Value *vp) { return true; } - JS::RootedString *rootedSeparator; + JS::RootedString rootedSeparator(cx); if (args.hasDefined(0)) { - rootedSeparator = new JS::RootedString(cx, JS::ToString(cx, args[0])); + rootedSeparator.set(JS::ToString(cx, args[0])); } else { - rootedSeparator = new JS::RootedString(cx, JS_NewStringCopyZ(cx, ",")); + rootedSeparator.set(JS_NewStringCopyZ(cx, ",")); } JSString *writer = JS_NewStringCopyZ(cx, ""); @@ -1450,7 +1453,7 @@ static bool array_join(JSContext *cx, unsigned argc, JS::Value *vp) { for (Py_ssize_t index = 0; index < selfLength; index++) { rootedWriter.set(writer); if (index > 0) { - writer = JS_ConcatStrings(cx, rootedWriter, *rootedSeparator); + writer = JS_ConcatStrings(cx, rootedWriter, rootedSeparator); rootedWriter.set(writer); } @@ -1461,12 +1464,10 @@ static bool array_join(JSContext *cx, unsigned argc, JS::Value *vp) { JS::RootedObject retObject(cx); if (!JS_ValueToObject(cx, element, &retObject)) { - delete rootedSeparator; return false; } if (!JS_CallFunctionName(cx, retObject, "toString", JS::HandleValueArray::empty(), &rval)) { - delete rootedSeparator; return false; } @@ -1475,8 +1476,6 @@ static bool array_join(JSContext *cx, unsigned argc, JS::Value *vp) { } } - delete rootedSeparator; - args.rval().setString(writer); return true; } From 943bb1d9a3caf06bf88956a057e52b531784a664 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 27 Feb 2024 16:33:56 -0500 Subject: [PATCH 092/170] fix file header attributions --- include/JSObjectProxy.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 3a2b0504..e1b2e9d7 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -1,6 +1,6 @@ /** * @file JSObjectProxy.hh - * @author Caleb Aikens (caleb@distributive.network) & Tom Tang (xmader@distributive.network) + * @author Caleb Aikens (caleb@distributive.network), Tom Tang (xmader@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. * @date 2023-06-26 * From 118c8ca18a7b9dc2bd81451bb327e1577d66da8e Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Tue, 27 Feb 2024 16:43:12 -0500 Subject: [PATCH 093/170] feat(PyListProxyHandler): implement and add tests for Array methods that take a thisArg argument for a callback --- src/PyListProxyHandler.cc | 55 +++- tests/python/test_arrays.py | 630 ++++++++++++++++++++++++++++-------- 2 files changed, 546 insertions(+), 139 deletions(-) diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 5286d274..35aa5e1a 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -30,7 +30,28 @@ const char PyListProxyHandler::family = 0; +// private util +// if function is a proxy for a python method, mutate it into a new python method bound to thisObject +static bool makeNewPyMethod(JSContext *cx, JS::MutableHandleValue function, JS::HandleObject thisObject) { + if (!JS_IsNativeFunction(&(function.toObject()), callPyFunc)) { + return true; // we don't need to mutate function if it is not a proxy for a python function + } + PyObject *method = (PyObject *)js::GetFunctionNativeReserved(&(function.toObject()), 0).toPrivate(); + if (!PyMethod_Check(method)) { + PyErr_Format(PyExc_TypeError, "unbound python functions do not have a 'self' to bind"); + return false; + } + + PyObject *func = PyMethod_Function(method); + JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); + JS::RootedValue thisValue(cx); + thisValue.setObject(*thisObject); + PyObject *newSelf = pyTypeFactory(cx, &global, &thisValue)->getPyObject(); + function.set(jsTypeFactory(cx, PyMethod_New(func, newSelf))); + + return true; +} static bool array_reverse(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); @@ -667,15 +688,19 @@ static bool array_forEach(JSContext *cx, unsigned argc, JS::Value *vp) { Py_ssize_t len = PyList_GET_SIZE(self); JS::RootedObject rootedThisArg(cx); + if (args.length() > 1) { JS::Value thisArg = args[1].get(); if (!thisArg.isObjectOrNull()) { JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_NOT_OBJORNULL, "'this' argument"); return false; } - // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -737,6 +762,10 @@ static bool array_map(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -796,6 +825,10 @@ static bool array_filter(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -974,6 +1007,10 @@ static bool array_some(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -1035,6 +1072,10 @@ static bool array_every(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -1096,6 +1137,10 @@ static bool array_find(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -1158,6 +1203,10 @@ static bool array_findIndex(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); @@ -1409,6 +1458,10 @@ static bool array_flatMap(JSContext *cx, unsigned argc, JS::Value *vp) { // TODO support null, currently gets TypeError rootedThisArg.set(thisArg.toObjectOrNull()); + // check if callback is a PyMethod, need to make a new method bound to thisArg + if (!makeNewPyMethod(cx, &callBack, rootedThisArg)) { + return false; + } } else { rootedThisArg.set(nullptr); diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 442471ce..2ee5ebd0 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -860,34 +860,67 @@ def test_forEach_check_this_arg_wrong_type(): # TODO python function support -# this one does not get the result into items -#def test_forEach_with_python_function(): -# def func(element, index, array): -# return "to each his own" -# items = ['Four', 'Three', 'One'] -# returnResult = [0] -# pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.forEach(func)}")(returnResult, items, func) -# assert items == ['to each his own', 'to each his own', 'to each his own'] -# assert returnResult == [None] - -#def test_forEach_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_forEach_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.forEach(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [None] + +def test_forEach_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.forEach(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.forEach(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_forEach_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.forEach(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") +def test_forEach_self_jsfunction(): + items = ['Four', 'Three', 'One'] + + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++ + } + let jsObj = {count: 0} + arr.forEach(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 # TODO should not pass def test_forEach_check_this_arg_null(): @@ -979,23 +1012,68 @@ def test_map_check_array_mutation(): assert result[0] == ['Ten', 'Three', 'One'] assert items == ['Ten', 'Three', 'One'] -#def test_map_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_map_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + return 42 + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.map(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [[42.0, 42.0, 42.0]] + +def test_map_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 + + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.map(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_map_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.map(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_map_self_jsfunction(): + items = ['Four', 'Three', 'One'] -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.map(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++ + } + let jsObj = {count: 0} + arr.map(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #filter def test_filter(): @@ -1051,23 +1129,67 @@ def test_filter_too_few_args(): assert str(type(e)) == "" assert str(e).__contains__("TypeError: filter: At least 1 argument required, but only 0 passed") -#def test_filter_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_filter_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.filter(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [[]] + +def test_filter_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 + + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.filter(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_filter_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.filter(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_filter_self_jsfunction(): + items = ['Four', 'Three', 'One'] -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.filter(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++ + } + let jsObj = {count: 0} + arr.filter(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #reduce def test_reduce(): @@ -1242,23 +1364,71 @@ def test_some_truthy_conversion(): """)(result) assert result[0] == True -#def test_some_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_some_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + return False + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.some(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [False] + +def test_some_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 + return False + + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.some(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_some_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + return False + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.some(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_some_self_jsfunction(): + items = ['Four', 'Three', 'One'] -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.some(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++; + return false; + } + let jsObj = {count: 0} + arr.some(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #every def test_every_true(): @@ -1311,23 +1481,70 @@ class Counter { )(result, items) assert result == [1] -#def test_every_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_every_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + return True + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.every(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [True] + +def test_every_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 + return True -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.every(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.every(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_every_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.every(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_every_self_jsfunction(): + items = ['Four', 'Three', 'One'] + + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++ + return true; + } + let jsObj = {count: 0} + arr.every(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #find def test_find_found_once(): @@ -1386,23 +1603,71 @@ class Counter { )(result, items) assert result == [3] -#def test_find_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_find_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + return False + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.find(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [None] + +def test_find_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 + return False + + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.find(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_find_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + return False + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.find(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_find_self_jsfunction(): + items = ['Four', 'Three', 'One'] -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.find(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++; + return false; + } + let jsObj = {count: 0} + arr.find(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #findIndex def test_findIndex_found_once(): @@ -1461,23 +1726,67 @@ class Counter { )(result, items) assert result == [3] -#def test_findIndex_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_findIndex_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.findIndex(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [-1] + +def test_findIndex_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.findIndex(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.findIndex(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_findIndex_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.findIndex(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_findIndex_self_jsfunction(): + items = ['Four', 'Three', 'One'] + + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++ + } + let jsObj = {count: 0} + arr.findIndex(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #flat def test_flat(): @@ -1594,23 +1903,68 @@ class Counter { )(result, items) assert result == [3] -#def test_flatMap_self(): -# items = ['Four', 'Three', 'One'] -# class Counter: -# def __init__(self): -# self.count = 0 -# def increment(self): -# self.count += 1 +def test_flatMap_with_python_function(): + def func(element, index, array): + array[int(index)] = "to each his own" + return 42 + items = ['Four', 'Three', 'One'] + returnResult = [0] + pm.eval("(returnResult, arr, func) => {returnResult[0] = arr.flatMap(func)}")(returnResult, items, func) + assert items == ['to each his own', 'to each his own', 'to each his own'] + assert returnResult == [[42.0, 42.0, 42.0]] + +def test_flatMap_self_pymethod(): + items = ['Four', 'Three', 'One'] + class Counter: + def __init__(self): + self.count = 0 + def increment(self, element, index, array): + self.count += 1 -# obj = Counter() -# result = pm.eval(""" -# (arr, increment, result) => { -# let jsObj = {count: 0} -# arr.flatMap(increment, jsObj); -# return jsObj.count; -# } -# """)(items, obj.increment) -# assert result == 3 + obj = Counter() + assert obj.count == 0 + result = pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.flatMap(increment, jsObj); + return jsObj.count; + } + """)(items, obj.increment) + assert obj.count == 0 + assert result == 3 + +def test_flatMap_self_pyfunction(): + items = ['Four', 'Three', 'One'] + def increment(self, element, index, array): + self.count += 1 + + try: + pm.eval(""" + (arr, increment, result) => { + let jsObj = {count: 0} + arr.flatMap(increment, jsObj); + return jsObj.count; + } + """)(items, increment) + assert False + except Exception as e: + assert type(e) is TypeError + assert str(e).__contains__("unbound python functions do not have a 'self' to bind") + +def test_flatMap_self_jsfunction(): + items = ['Four', 'Three', 'One'] + + result = pm.eval(""" + (arr) => { + function increment(element, index, array) { + this.count++ + } + let jsObj = {count: 0} + arr.flatMap(increment, jsObj); + return jsObj.count; + } + """)(items) + assert result == 3 #valueOf def test_valueOf(): From 850508c4f0a470a2bb759e22bc89d56257f38835 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 27 Feb 2024 17:37:50 -0500 Subject: [PATCH 094/170] improved clear methods and related naming, variable folding --- include/JSArrayProxy.hh | 6 +++--- src/JSArrayProxy.cc | 6 +++--- src/JSObjectProxy.cc | 4 +--- src/modules/pythonmonkey/pythonmonkey.cc | 17 ++++++----------- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index f0e4f2b0..f6875954 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -162,7 +162,7 @@ public: * @param self - The JSArrayProxy * @return None */ - static PyObject *JSArrayProxy_clear(JSArrayProxy *self); + static PyObject *JSArrayProxy_clear_method(JSArrayProxy *self); /** * @brief .tp_clear method @@ -170,7 +170,7 @@ public: * @param self - The JSArrayProxy * @return 0 on success */ - static int JSArrayProxy_clear_slot(JSArrayProxy *self); + static int JSArrayProxy_clear(JSArrayProxy *self); /** * @brief .tp_traverse method @@ -393,7 +393,7 @@ PyDoc_STRVAR(list___reversed____doc__, */ static PyMethodDef JSArrayProxy_methods[] = { {"__reversed__", (PyCFunction)JSArrayProxyMethodDefinitions::JSArrayProxy_iter_reverse, METH_NOARGS, list___reversed____doc__}, - {"clear", (PyCFunction)JSArrayProxyMethodDefinitions::JSArrayProxy_clear, METH_NOARGS, py_list_clear__doc__}, + {"clear", (PyCFunction)JSArrayProxyMethodDefinitions::JSArrayProxy_clear_method, METH_NOARGS, py_list_clear__doc__}, {"copy", (PyCFunction)JSArrayProxyMethodDefinitions::JSArrayProxy_copy, METH_NOARGS, list_copy__doc__}, {"append", (PyCFunction)JSArrayProxyMethodDefinitions::JSArrayProxy_append, METH_O, list_append__doc__}, {"insert", (PyCFunction)JSArrayProxyMethodDefinitions::JSArrayProxy_insert, METH_FASTCALL, list_insert__doc__}, diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 3162b8a9..9a18d185 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -734,13 +734,13 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_inplace_repeat(JSArrayProx return (PyObject *)self; } -PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_clear(JSArrayProxy *self) { +PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_clear_method(JSArrayProxy *self) { JS::SetArrayLength(GLOBAL_CX, self->jsArray, 0); Py_RETURN_NONE; } -int JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot(JSArrayProxy *self) { - JSArrayProxyMethodDefinitions::JSArrayProxy_clear(self); +int JSArrayProxyMethodDefinitions::JSArrayProxy_clear(JSArrayProxy *self) { + // Nothing to be done return 0; } diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index a7baed14..49af3d1e 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -133,9 +133,7 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_traverse(JSObjectProxy *self, } int JSObjectProxyMethodDefinitions::JSObjectProxy_clear(JSObjectProxy *self) { - if (!JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(self)) { - return -1; - } + // Nothing to be done return 0; } diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index e99cf366..cb88845d 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -145,7 +145,7 @@ PyTypeObject JSArrayProxyType = { .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS | Py_TPFLAGS_HAVE_GC, .tp_doc = PyDoc_STR("Javascript Array proxy list"), .tp_traverse = (traverseproc)JSArrayProxyMethodDefinitions::JSArrayProxy_traverse, - .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot, + .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear, .tp_richcompare = (richcmpfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare, .tp_iter = (getiterfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_iter, .tp_methods = JSArrayProxy_methods, @@ -257,27 +257,21 @@ static PyObject *collect(PyObject *self, PyObject *args) { } static bool getEvalOption(PyObject *evalOptions, const char *optionName, const char **s_p) { - PyObject *value; - - value = PyDict_GetItemString(evalOptions, optionName); + PyObject *value = PyDict_GetItemString(evalOptions, optionName); if (value) *s_p = PyUnicode_AsUTF8(value); return value != NULL; } static bool getEvalOption(PyObject *evalOptions, const char *optionName, unsigned long *l_p) { - PyObject *value; - - value = PyDict_GetItemString(evalOptions, optionName); + PyObject *value = PyDict_GetItemString(evalOptions, optionName); if (value) *l_p = PyLong_AsUnsignedLong(value); return value != NULL; } static bool getEvalOption(PyObject *evalOptions, const char *optionName, bool *b_p) { - PyObject *value; - - value = PyDict_GetItemString(evalOptions, optionName); + PyObject *value = PyDict_GetItemString(evalOptions, optionName); if (value) *b_p = PyObject_IsTrue(value) == 1 ? true : false; return value != NULL; @@ -340,7 +334,8 @@ static PyObject *eval(PyObject *self, PyObject *args) { } /* filename */ } /* fromPythonFrame */ } /* eval options */ - // initialize JS context + + // initialize JS context JS::SourceText source; if (!source.init(GLOBAL_CX, code->getValue(), strlen(code->getValue()), JS::SourceOwnership::Borrowed)) { setSpiderMonkeyException(GLOBAL_CX); From 66448e2e0538035bd2decff62414226ebfbde827 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Tue, 27 Feb 2024 18:22:23 -0500 Subject: [PATCH 095/170] docs(JSMethodProxy): write docs describing how to use JSMethodProxy to implement methods on python classes --- README.md | 1 + python/pythonmonkey/pythonmonkey.pyi | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/README.md b/README.md index 2ac8db92..4c727c31 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ These methods are exported from the pythonmonkey module. * bigint(int) * `SpiderMonkeyError` * `JSObjectProxy` +* `JSMethodProxy` * `null` See definitions in [python/pythonmonkey/pythonmonkey.pyi](https://github.com/Distributive-Network/PythonMonkey/blob/main/python/pythonmonkey/pythonmonkey.pyi). diff --git a/python/pythonmonkey/pythonmonkey.pyi b/python/pythonmonkey/pythonmonkey.pyi index 15bed09d..a9db3bc2 100644 --- a/python/pythonmonkey/pythonmonkey.pyi +++ b/python/pythonmonkey/pythonmonkey.pyi @@ -66,6 +66,31 @@ class JSObjectProxy(dict): """ def __init__(self) -> None: ... +class JSFunctionProxy(): + """ + JavaScript Function proxy + """ +class JSMethodProxy(JSFunctionProxy, object): + """ + JavaScript Method proxy + This constructs a callable object based on the first argument, bound to the second argument + Useful when you wish to implement a method on a class object with JavaScript + Example: + import pythonmonkey as pm + + jsFunc = pm.eval("(function(value) { this.value = value})") + class Class: + def __init__(self): + self.value = 0 + self.setValue = pm.JSMethodProxy(jsFunc, self) #setValue will be bound to self, so `this` will always be `self` + + myObject = Class() + print(myObject.value) # 0 + myObject.setValue(42) + print(myObject.value) # 42.0 + """ + def __init__(self) -> None: ... + null = _typing.Annotated[ _typing.NewType("pythonmonkey.null", object), "Representing the JS null type in Python using a singleton object", From 763bd44819c4985122e7094d4b5d1a97e866cebf Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Tue, 27 Feb 2024 18:52:36 -0500 Subject: [PATCH 096/170] fix on clear method, incref in object.get --- src/JSArrayProxy.cc | 4 ++-- src/JSObjectProxy.cc | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 9a18d185..0dfa8afe 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -210,7 +210,7 @@ static int list_ass_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihigh, if (selfLength + d == 0) { Py_XDECREF(v_as_SF); - JSArrayProxyMethodDefinitions::JSArrayProxy_clear(self); + JSArrayProxyMethodDefinitions::JSArrayProxy_clear_method(self); return 0; } @@ -709,7 +709,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_inplace_repeat(JSArrayProx } if (n < 1) { - JSArrayProxy_clear(self); + JSArrayProxy_clear_method(self); Py_INCREF(self); return (PyObject *)self; } diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 49af3d1e..bac8cf73 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -71,7 +71,9 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + PyObject *ret = pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + Py_INCREF(ret); + return ret; } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -550,7 +552,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method(JSObje return default_value; } - Py_XINCREF(value); + // no need to incref since done in getKey return value; } From ee5c7383194a77446d01b8e16388c8ef689eaae3 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 28 Feb 2024 11:53:32 -0500 Subject: [PATCH 097/170] heap-allocated root should be persistent --- include/JSObjectIterProxy.hh | 2 +- src/JSObjectItemsProxy.cc | 4 ++-- src/JSObjectKeysProxy.cc | 4 ++-- src/JSObjectProxy.cc | 9 +++------ src/JSObjectValuesProxy.cc | 4 ++-- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/include/JSObjectIterProxy.hh b/include/JSObjectIterProxy.hh index d8f46075..37b1876c 100644 --- a/include/JSObjectIterProxy.hh +++ b/include/JSObjectIterProxy.hh @@ -28,7 +28,7 @@ typedef struct { PyObject_HEAD - JS::RootedIdVector *props; // not conceptually the best use of the Rooted type but it works. There is no easy inter-operation with a JS::Heap type + JS::PersistentRootedIdVector *props; int it_index; bool reversed; int kind; diff --git a/src/JSObjectItemsProxy.cc b/src/JSObjectItemsProxy.cc index 117b7d4c..fcc47b0c 100644 --- a/src/JSObjectItemsProxy.cc +++ b/src/JSObjectItemsProxy.cc @@ -56,7 +56,7 @@ PyObject *JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_iter(JSObjectI iterator->it.kind = KIND_ITEMS; Py_INCREF(self->dv.dv_dict); iterator->it.di_dict = self->dv.dv_dict; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { return NULL; @@ -75,7 +75,7 @@ PyObject *JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_iter_reverse(J iterator->it.kind = KIND_ITEMS; Py_INCREF(self->dv.dv_dict); iterator->it.di_dict = self->dv.dv_dict; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { return NULL; diff --git a/src/JSObjectKeysProxy.cc b/src/JSObjectKeysProxy.cc index ddb6d2b0..cb2f6a51 100644 --- a/src/JSObjectKeysProxy.cc +++ b/src/JSObjectKeysProxy.cc @@ -167,7 +167,7 @@ PyObject *JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_iter(JSObjectKey iterator->it.kind = KIND_KEYS; Py_INCREF(self->dv.dv_dict); iterator->it.di_dict = self->dv.dv_dict; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { return NULL; @@ -186,7 +186,7 @@ PyObject *JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_iter_reverse(JSO iterator->it.kind = KIND_KEYS; Py_INCREF(self->dv.dv_dict); iterator->it.di_dict = self->dv.dv_dict; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { return NULL; diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index bac8cf73..f251dc40 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -71,9 +71,7 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); - PyObject *ret = pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); - Py_INCREF(ret); - return ret; + return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -236,7 +234,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter(JSObjectProxy *self iterator->it.kind = KIND_KEYS; Py_INCREF(self); iterator->it.di_dict = (PyDictObject *)self; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + 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; @@ -519,7 +517,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get_method(JSObjectProxy if (value == Py_None) { value = default_value; } - Py_XINCREF(value); + return value; } @@ -552,7 +550,6 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method(JSObje return default_value; } - // no need to incref since done in getKey return value; } diff --git a/src/JSObjectValuesProxy.cc b/src/JSObjectValuesProxy.cc index ef83492f..86ab3220 100644 --- a/src/JSObjectValuesProxy.cc +++ b/src/JSObjectValuesProxy.cc @@ -95,7 +95,7 @@ PyObject *JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_iter(JSObjec iterator->it.kind = KIND_VALUES; Py_INCREF(self->dv.dv_dict); iterator->it.di_dict = self->dv.dv_dict; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { return NULL; @@ -114,7 +114,7 @@ PyObject *JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_iter_reverse iterator->it.kind = KIND_VALUES; Py_INCREF(self->dv.dv_dict); iterator->it.di_dict = self->dv.dv_dict; - iterator->it.props = new JS::RootedIdVector(GLOBAL_CX); + iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { return NULL; From 0af763cd9276f252a0aaac49047a3b123695a3de Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 28 Feb 2024 12:42:55 -0500 Subject: [PATCH 098/170] Revert "chore(meta): update all top-of-file comments" This reverts commit 5ab92c8b771726a8b5f19d8a30314d596b83f0fb. --- .vscode/c_cpp_properties.json | 3 +-- CMakeLists.txt | 2 +- LICENSE | 2 +- cmake/externals/autopep8/CMakeLists.txt | 2 +- cmake/externals/uncrustify/CMakeLists.txt | 2 +- cmake/format/CMakeLists.txt | 2 +- cmake/format/uncrustify.cfg | 2 +- include/BoolType.hh | 3 ++- include/BufferType.hh | 3 ++- include/DateType.hh | 3 ++- include/DictType.hh | 3 ++- include/ExceptionType.hh | 3 ++- include/FloatType.hh | 3 ++- include/FuncType.hh | 3 ++- include/IntType.hh | 3 ++- include/JSArrayIterProxy.hh | 3 ++- include/JSArrayProxy.hh | 3 ++- include/JSFunctionProxy.hh | 3 ++- include/JSMethodProxy.hh | 3 ++- include/JSObjectItemsProxy.hh | 3 ++- include/JSObjectIterProxy.hh | 3 ++- include/JSObjectKeysProxy.hh | 3 ++- include/JSObjectProxy.hh | 3 ++- include/JSObjectValuesProxy.hh | 3 ++- include/JSStringProxy.hh | 3 ++- include/JobQueue.hh | 3 ++- include/ListType.hh | 3 ++- include/NoneType.hh | 3 ++- include/NullType.hh | 3 ++- include/PromiseType.hh | 3 ++- include/PyBaseProxyHandler.hh | 3 ++- include/PyDictProxyHandler.hh | 2 +- include/PyEventLoop.hh | 3 ++- include/PyListProxyHandler.hh | 3 ++- include/PyObjectProxyHandler.hh | 3 ++- include/PyType.hh | 3 ++- include/StrType.hh | 3 ++- include/TupleType.hh | 3 ++- include/TypeEnum.hh | 3 ++- include/internalBinding.hh | 3 ++- include/jsTypeFactory.hh | 3 ++- include/modules/pythonmonkey/pythonmonkey.hh | 3 ++- include/pyTypeFactory.hh | 3 ++- include/setSpiderMonkeyException.hh | 3 ++- peter-jr | 2 +- src/BufferType.cc | 3 ++- src/JSArrayIterProxy.cc | 2 +- src/JSArrayProxy.cc | 3 ++- src/JSFunctionProxy.cc | 3 ++- src/JSMethodProxy.cc | 3 ++- src/JSObjectItemsProxy.cc | 3 ++- src/JSObjectIterProxy.cc | 2 +- src/JSObjectKeysProxy.cc | 3 ++- src/JSObjectProxy.cc | 3 ++- src/JSObjectValuesProxy.cc | 3 ++- src/NoneType.cc | 3 ++- src/NullType.cc | 3 ++- src/PromiseType.cc | 3 ++- src/PyBaseProxyHandler.cc | 2 +- src/PyDictProxyHandler.cc | 2 +- src/PyListProxyHandler.cc | 2 +- src/PyObjectProxyHandler.cc | 3 ++- src/TupleType.cc | 3 ++- src/internalBinding.cc | 3 +-- src/jsTypeFactory.cc | 3 ++- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- src/pyTypeFactory.cc | 3 ++- src/setSpiderMonkeyException.cc | 3 ++- 68 files changed, 120 insertions(+), 70 deletions(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index a3eea6a2..d1ac024f 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -3,8 +3,7 @@ { "name": "Linux", "includePath": [ - "${workspaceFolder}/include/**", - "/usr/include/python3.11" + "${workspaceFolder}/include/**" ], "defines": [], "compilerPath": "/usr/bin/clang", diff --git a/CMakeLists.txt b/CMakeLists.txt index f9ec11f5..3cbe02d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Distributive Corp. All Rights Reserved. +# Copyright (c) 2022 Distributive Inc. All Rights Reserved. cmake_minimum_required(VERSION 3.25) # Set minimum cmake version diff --git a/LICENSE b/LICENSE index 94696de0..085d2547 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022-2024 Distributive Corp. +Copyright (c) 2023-2024 Distributive Corp. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/cmake/externals/autopep8/CMakeLists.txt b/cmake/externals/autopep8/CMakeLists.txt index 092d07db..ebc95787 100644 --- a/cmake/externals/autopep8/CMakeLists.txt +++ b/cmake/externals/autopep8/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Distributive Corp. All Rights Reserved. +# Copyright (c) 2022 Distributive Inc. All Rights Reserved. set(AUTOPEP8_ROOT "${CMAKE_CURRENT_BINARY_DIR}/install" CACHE PATH "The autopep8 root directory." diff --git a/cmake/externals/uncrustify/CMakeLists.txt b/cmake/externals/uncrustify/CMakeLists.txt index 080f93b6..ba95422b 100644 --- a/cmake/externals/uncrustify/CMakeLists.txt +++ b/cmake/externals/uncrustify/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Distributive Corp. All Rights Reserved. +# Copyright (c) 2022 Distributive Inc. All Rights Reserved. set(UNCRUSTIFY_ROOT "${CMAKE_CURRENT_BINARY_DIR}/install" CACHE PATH "The Uncrustify root directory." diff --git a/cmake/format/CMakeLists.txt b/cmake/format/CMakeLists.txt index 24e34517..19d8c7ce 100644 --- a/cmake/format/CMakeLists.txt +++ b/cmake/format/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Distributive Corp. All Rights Reserved. +# Copyright (c) 2022 Distributive Inc. All Rights Reserved. if(NOT UNCRUSTIFY_EXECUTABLE) if(UNCRUSTIFY_ROOT STREQUAL "") diff --git a/cmake/format/uncrustify.cfg b/cmake/format/uncrustify.cfg index 5de0790b..56d94610 100644 --- a/cmake/format/uncrustify.cfg +++ b/cmake/format/uncrustify.cfg @@ -1,4 +1,4 @@ -# Copyright (c) 2024 Distributive Corp. All Rights Reserved. +# Copyright (c) 2019 Kings Distributed Systems. All Rights Reserved. file_ext CPP .cc .hh diff --git a/include/BoolType.hh b/include/BoolType.hh index 951bbf5e..5e2cc05c 100644 --- a/include/BoolType.hh +++ b/include/BoolType.hh @@ -2,9 +2,10 @@ * @file BoolType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python bools + * @version 0.1 * @date 2022-12-02 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/BufferType.hh b/include/BufferType.hh index 73fc5483..ea2bd954 100644 --- a/include/BufferType.hh +++ b/include/BufferType.hh @@ -2,9 +2,10 @@ * @file BufferType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing ArrayBuffers + * @version 0.1 * @date 2023-04-27 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/DateType.hh b/include/DateType.hh index f7830ede..23f59707 100644 --- a/include/DateType.hh +++ b/include/DateType.hh @@ -2,9 +2,10 @@ * @file DateType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python dates + * @version 0.1 * @date 2022-12-21 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/DictType.hh b/include/DictType.hh index 27d6e413..780a0e73 100644 --- a/include/DictType.hh +++ b/include/DictType.hh @@ -2,9 +2,10 @@ * @file DictType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python dictionaries + * @version 0.1 * @date 2022-08-10 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/ExceptionType.hh b/include/ExceptionType.hh index bb71e2d7..86045e8f 100644 --- a/include/ExceptionType.hh +++ b/include/ExceptionType.hh @@ -2,9 +2,10 @@ * @file ExceptionType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Python Exception objects from a corresponding JS Error object + * @version 0.1 * @date 2023-04-11 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/FloatType.hh b/include/FloatType.hh index 499b2bc6..2544c251 100644 --- a/include/FloatType.hh +++ b/include/FloatType.hh @@ -2,9 +2,10 @@ * @file FloatType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing python floats + * @version 0.1 * @date 2022-12-02 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/FuncType.hh b/include/FuncType.hh index 391d5bfc..af2d2dfb 100644 --- a/include/FuncType.hh +++ b/include/FuncType.hh @@ -2,9 +2,10 @@ * @file FuncType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python functions + * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ #ifndef PythonMonkey_FuncType_ diff --git a/include/IntType.hh b/include/IntType.hh index efbb431e..81111216 100644 --- a/include/IntType.hh +++ b/include/IntType.hh @@ -2,9 +2,10 @@ * @file IntType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) * @brief Struct for representing python ints + * @version 0.2 * @date 2023-03-16 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/JSArrayIterProxy.hh b/include/JSArrayIterProxy.hh index fc4a93f7..a4a9e660 100644 --- a/include/JSArrayIterProxy.hh +++ b/include/JSArrayIterProxy.hh @@ -2,9 +2,10 @@ * @file JSArrayIterProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSArrayIterProxy is a custom C-implemented python type that derives from PyListIter + * @version 0.1 * @date 2024-01-15 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index ce4ce26e..911e5edf 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -2,9 +2,10 @@ * @file JSArrayProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSArrayProxy is a custom C-implemented python type that derives from list. It acts as a proxy for JSArrays from Spidermonkey, and behaves like a list would. + * @version 0.1 * @date 2023-11-22 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSFunctionProxy.hh b/include/JSFunctionProxy.hh index a46980ef..a73ae6ea 100644 --- a/include/JSFunctionProxy.hh +++ b/include/JSFunctionProxy.hh @@ -2,9 +2,10 @@ * @file JSFunctionProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSFunctionProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would. + * @version 0.1 * @date 2023-09-28 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSMethodProxy.hh b/include/JSMethodProxy.hh index 009229fb..f08bd81b 100644 --- a/include/JSMethodProxy.hh +++ b/include/JSMethodProxy.hh @@ -2,9 +2,10 @@ * @file JSMethodProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSMethodProxy is a custom C-implemented python type. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a method would, treating `self` as `this`. + * @version 0.1 * @date 2023-11-14 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSObjectItemsProxy.hh b/include/JSObjectItemsProxy.hh index 480a48ae..91c8bc72 100644 --- a/include/JSObjectItemsProxy.hh +++ b/include/JSObjectItemsProxy.hh @@ -2,9 +2,10 @@ * @file JSObjectItemsProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectItemsProxy is a custom C-implemented python type that derives from dict items + * @version 0.1 * @date 2024-01-19 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectIterProxy.hh b/include/JSObjectIterProxy.hh index d8f46075..d6fb1452 100644 --- a/include/JSObjectIterProxy.hh +++ b/include/JSObjectIterProxy.hh @@ -2,9 +2,10 @@ * @file JSObjectIterProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectIterProxy is a custom C-implemented python type that derives from PyDictIterKey + * @version 0.1 * @date 2024-01-17 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectKeysProxy.hh b/include/JSObjectKeysProxy.hh index 2564bb3f..1dea37be 100644 --- a/include/JSObjectKeysProxy.hh +++ b/include/JSObjectKeysProxy.hh @@ -2,9 +2,10 @@ * @file JSObjectKeysProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectKeysProxy is a custom C-implemented python type that derives from dict keys + * @version 0.1 * @date 2024-01-16 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index 54eb38f2..920a03d5 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -2,9 +2,10 @@ * @file JSObjectProxy.hh * @author Caleb Aikens (caleb@distributive.network) & Tom Tang (xmader@distributive.network) * @brief JSObjectProxy is a custom C-implemented python type that derives from dict. It acts as a proxy for JSObjects from Spidermonkey, and behaves like a dict would. + * @version 0.1 * @date 2023-06-26 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSObjectValuesProxy.hh b/include/JSObjectValuesProxy.hh index c2b3be59..238d3066 100644 --- a/include/JSObjectValuesProxy.hh +++ b/include/JSObjectValuesProxy.hh @@ -2,9 +2,10 @@ * @file JSObjectValuesProxy.hh * @author Philippe Laporte (philippe@distributive.network) * @brief JSObjectValuesProxy is a custom C-implemented python type that derives from dict values + * @version 0.1 * @date 2023-06-26 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/JSStringProxy.hh b/include/JSStringProxy.hh index 8c82374c..135eac6f 100644 --- a/include/JSStringProxy.hh +++ b/include/JSStringProxy.hh @@ -2,9 +2,10 @@ * @file JSStringProxy.hh * @author Caleb Aikens (caleb@distributive.network) * @brief JSStringProxy is a custom C-implemented python type that derives from str. It acts as a proxy for JSStrings from Spidermonkey, and behaves like a str would. + * @version 0.1 * @date 2024-01-03 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2024 Distributive Corp. * */ diff --git a/include/JobQueue.hh b/include/JobQueue.hh index 183bcf9f..5d10e9a7 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -2,9 +2,10 @@ * @file JobQueue.hh * @author Tom Tang (xmader@distributive.network) * @brief Implement the ECMAScript Job Queue + * @version 0.1 * @date 2023-04-03 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/ListType.hh b/include/ListType.hh index d5112057..df5ba9ac 100644 --- a/include/ListType.hh +++ b/include/ListType.hh @@ -2,9 +2,10 @@ * @file ListType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python lists + * @version 0.1 * @date 2022-08-18 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/NoneType.hh b/include/NoneType.hh index abfbacd2..a2f7a82d 100644 --- a/include/NoneType.hh +++ b/include/NoneType.hh @@ -2,9 +2,10 @@ * @file NoneType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing None + * @version 0.1 * @date 2023-02-22 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/NullType.hh b/include/NullType.hh index 9dbf68c0..80b91e20 100644 --- a/include/NullType.hh +++ b/include/NullType.hh @@ -2,9 +2,10 @@ * @file NullType.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Struct for representing JS null in a python object + * @version 0.1 * @date 2023-02-22 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/PromiseType.hh b/include/PromiseType.hh index e2c7bc3e..cc8381f1 100644 --- a/include/PromiseType.hh +++ b/include/PromiseType.hh @@ -2,9 +2,10 @@ * @file PromiseType.hh * @author Tom Tang (xmader@distributive.network) * @brief Struct for representing Promises + * @version 0.1 * @date 2023-03-29 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/PyBaseProxyHandler.hh b/include/PyBaseProxyHandler.hh index 353973be..08bc0203 100644 --- a/include/PyBaseProxyHandler.hh +++ b/include/PyBaseProxyHandler.hh @@ -2,9 +2,10 @@ * @file PyBaseProxyHandler.hh * @author Caleb Aikens (caleb@distributive.network) and Philippe Laporte (philippe@distributive.network) * @brief Structs for creating JS proxy objects. + * @version 0.1 * @date 2023-04-20 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index 36b4d82f..3784076e 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -4,7 +4,7 @@ * @brief Structs for creating JS proxy objects. Used by DictType for object coercion * @date 2023-04-20 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/include/PyEventLoop.hh b/include/PyEventLoop.hh index 8c623479..36fdb03c 100644 --- a/include/PyEventLoop.hh +++ b/include/PyEventLoop.hh @@ -2,9 +2,10 @@ * @file PyEventLoop.hh * @author Tom Tang (xmader@distributive.network) * @brief Send jobs to the Python event-loop + * @version 0.1 * @date 2023-04-05 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/PyListProxyHandler.hh b/include/PyListProxyHandler.hh index 18ed3aec..bc1f82b7 100644 --- a/include/PyListProxyHandler.hh +++ b/include/PyListProxyHandler.hh @@ -2,9 +2,10 @@ * @file PyListProxyHandler.hh * @author Philippe Laporte (philippe@distributive.network) * @brief Structs for creating JS proxy objects. Used by ListType for List coercion + * @version 0.1 * @date 2023-12-01 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023-2024 Distributive Corp. * */ diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index bfd54fee..9e71b18d 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -2,9 +2,10 @@ * @file PyObjectProxyHandler.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Structs for creating JS proxy objects. Used for default object coercion + * @version 0.1 * @date 2024-01-25 * - * @copyright Copyright (c) 2024 Distributive Corp. + * Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/PyType.hh b/include/PyType.hh index 382c4729..b75d7f1b 100644 --- a/include/PyType.hh +++ b/include/PyType.hh @@ -2,9 +2,10 @@ * @file PyType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct representing python types + * @version 0.1 * @date 2022-07-27 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/StrType.hh b/include/StrType.hh index af67ca71..b6f5e16c 100644 --- a/include/StrType.hh +++ b/include/StrType.hh @@ -2,9 +2,10 @@ * @file StrType.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python strings + * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/TupleType.hh b/include/TupleType.hh index 783b8549..c1209cb4 100644 --- a/include/TupleType.hh +++ b/include/TupleType.hh @@ -2,9 +2,10 @@ * @file TupleType.hh * @author Giovanni Tedesco (giovanni@distributive.network) * @brief Struct for representing python tuples + * @version 0.1 * @date 2022-08-19 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/TypeEnum.hh b/include/TypeEnum.hh index 580bf6f7..a2d2c875 100644 --- a/include/TypeEnum.hh +++ b/include/TypeEnum.hh @@ -2,9 +2,10 @@ * @file TypeEnum.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) & Tom Tang (xmader@distributive.network) * @brief Enum for every PyType + * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/internalBinding.hh b/include/internalBinding.hh index c29aa79b..24c00082 100644 --- a/include/internalBinding.hh +++ b/include/internalBinding.hh @@ -2,9 +2,10 @@ * @file internalBinding.hh * @author Tom Tang (xmader@distributive.network) * @brief + * @version 0.1 * @date 2023-05-16 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 Distributive Corp. * */ diff --git a/include/jsTypeFactory.hh b/include/jsTypeFactory.hh index 6150ceaa..6ec68412 100644 --- a/include/jsTypeFactory.hh +++ b/include/jsTypeFactory.hh @@ -2,9 +2,10 @@ * @file jsTypeFactory.hh * @author Caleb Aikens (caleb@distributive.network) * @brief + * @version 0.1 * @date 2023-02-15 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/include/modules/pythonmonkey/pythonmonkey.hh b/include/modules/pythonmonkey/pythonmonkey.hh index 3e8c9424..7f964bce 100644 --- a/include/modules/pythonmonkey/pythonmonkey.hh +++ b/include/modules/pythonmonkey/pythonmonkey.hh @@ -2,9 +2,10 @@ * @file pythonmonkey.hh * @author Caleb Aikens (caleb@kingsds.network) * @brief This file defines the pythonmonkey module, along with its various functions. + * @version 0.1 * @date 2022-09-06 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ #ifndef PythonMonkey_Module_PythonMonkey diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index c2d0353e..3b79cbdd 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -2,9 +2,10 @@ * @file pyTypeFactory.hh * @author Caleb Aikens (caleb@distributive.network) & Giovanni Tedesco (giovanni@distributive.network) * @brief Function for wrapping arbitrary PyObjects into the appropriate PyType class, and coercing JS types to python types + * @version 0.1 * @date 2022-08-08 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2022 * */ diff --git a/include/setSpiderMonkeyException.hh b/include/setSpiderMonkeyException.hh index fcaf8141..f838e5e4 100644 --- a/include/setSpiderMonkeyException.hh +++ b/include/setSpiderMonkeyException.hh @@ -2,9 +2,10 @@ * @file setSpiderMonkeyException.hh * @author Caleb Aikens (caleb@distributive.network) * @brief Call this function whenever a JS_* function call fails in order to set an appropriate python exception (remember to also return NULL) + * @version 0.1 * @date 2023-02-28 * - * @copyright Copyright (c) 2024 Distributive Corp. + * @copyright Copyright (c) 2023 * */ diff --git a/peter-jr b/peter-jr index 1bdbcfe9..873a85f6 100755 --- a/peter-jr +++ b/peter-jr @@ -27,7 +27,7 @@ if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then cat < Date: Wed, 28 Feb 2024 13:25:47 -0500 Subject: [PATCH 099/170] remove unused second parameter to pyTypeFactory --- include/pyTypeFactory.hh | 3 +- src/JSArrayIterProxy.cc | 6 +-- src/JSArrayProxy.cc | 58 ++++++++---------------- src/JSFunctionProxy.cc | 2 +- src/JSMethodProxy.cc | 3 +- src/JSObjectIterProxy.cc | 6 +-- src/JSObjectProxy.cc | 19 +++----- src/JobQueue.cc | 3 +- src/PromiseType.cc | 4 +- src/PyDictProxyHandler.cc | 3 +- src/PyListProxyHandler.cc | 53 ++++++++-------------- src/PyObjectProxyHandler.cc | 3 +- src/internalBinding.cc | 3 +- src/internalBinding/timers.cc | 3 +- src/jsTypeFactory.cc | 5 +- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- src/pyTypeFactory.cc | 4 +- 17 files changed, 63 insertions(+), 117 deletions(-) diff --git a/include/pyTypeFactory.hh b/include/pyTypeFactory.hh index 8c15b866..fc8afe7a 100644 --- a/include/pyTypeFactory.hh +++ b/include/pyTypeFactory.hh @@ -22,10 +22,9 @@ * @brief Function that takes a JS::Value and returns a corresponding PyType* object, doing shared memory management when necessary * * @param cx - Pointer to the javascript context of the JS::Value - * @param thisObj - The JS `this` object for the value's scope * @param rval - The JS::Value who's type and value we wish to encapsulate * @return PyType* - Pointer to a PyType object corresponding to the JS::Value */ -PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval); +PyType *pyTypeFactory(JSContext *cx, JS::HandleValue rval); #endif \ No newline at end of file diff --git a/src/JSArrayIterProxy.cc b/src/JSArrayIterProxy.cc index 6d5923da..f6da0719 100644 --- a/src/JSArrayIterProxy.cc +++ b/src/JSArrayIterProxy.cc @@ -47,18 +47,16 @@ PyObject *JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_next(JSArrayIterPr if (self->it.reversed) { if (self->it.it_index >= 0) { - JS::RootedObject thisObj(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray); JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index--, &elementVal); - return pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } } else { if (self->it.it_index < JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)seq)) { - JS::RootedObject thisObj(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray); JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index++, &elementVal); - return pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } } diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 3fcb7c9b..aa27fb05 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -53,8 +53,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py if (methodName == NULL || !PyUnicode_Check(key)) { // reached end of list JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -75,8 +74,7 @@ static PyObject *list_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihig PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, jReturnedArray)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, jReturnedArray)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy *self, PyObject *key) @@ -104,8 +102,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } else if (PySlice_Check(key)) { Py_ssize_t start, stop, step, slicelength, index; @@ -134,8 +131,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, jCombinedArrayValue)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, jCombinedArrayValue)->getPyObject(); } } else { @@ -435,18 +431,17 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * } JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); Py_ssize_t index; /* Search for the first index where items are different */ for (index = 0; index < selfLength && index < otherLength; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *leftItem = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + PyObject *leftItem = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); PyObject *rightItem; if (PyObject_TypeCheck(other, &JSArrayProxyType)) { JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)other)->jsArray, index, &elementVal); - rightItem = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + rightItem = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } else { rightItem = ((PyListObject *)other)->ob_item[index]; } @@ -483,7 +478,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); /* Compare the final item again using the proper operator */ - return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); + return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { @@ -506,7 +501,6 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { writer.min_length = 1 + 1 + (2 + 1) * (selfLength - 1) + 1; JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); if (_PyUnicodeWriter_WriteChar(&writer, '[') < 0) { goto error; @@ -526,7 +520,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { if (&elementVal.toObject() == self->jsArray.get()) { s = PyObject_Repr((PyObject *)self); } else { - s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject()); + s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject()); } if (s == NULL) { goto error; @@ -631,8 +625,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_concat(JSArrayProxy *self, JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, jCombinedArrayValue)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, jCombinedArrayValue)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, Py_ssize_t n) { @@ -659,8 +652,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, JS::RootedValue jCombinedArrayValue(GLOBAL_CX); jCombinedArrayValue.setObjectOrNull(jCombinedArray); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, jCombinedArrayValue)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, jCombinedArrayValue)->getPyObject(); } int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyObject *element) { @@ -670,10 +662,9 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyO Py_ssize_t numElements = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (index = 0, cmp = 0; cmp == 0 && index < numElements; ++index) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *item = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(item); cmp = PyObject_RichCompareBool(item, element, Py_EQ); Py_DECREF(item); @@ -758,8 +749,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, jReturnedArray)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, jReturnedArray)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_append(JSArrayProxy *self, PyObject *value) { @@ -936,18 +926,16 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_pop(JSArrayProxy *self, Py JS::RootedValue elementVal(GLOBAL_CX); JS_GetElement(GLOBAL_CX, rootedReturnedArray, 0, &elementVal); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); - return pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, PyObject *value) { Py_ssize_t selfSize = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (Py_ssize_t index = 0; index < selfSize; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1010,10 +998,9 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_index(JSArrayProxy *self, } JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (Py_ssize_t index = start; index < stop && index < selfSize; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1034,10 +1021,9 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_count(JSArrayProxy *self, Py_ssize_t length = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); - JS::RootedObject thisObj(GLOBAL_CX, self->jsArray); for (Py_ssize_t index = 0; index < length; index++) { JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - PyObject *obj = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + PyObject *obj = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); Py_DECREF(obj); @@ -1083,17 +1069,15 @@ static bool sort_compare_key_func(JSContext *cx, unsigned argc, JS::Value *vp) { } bool reverse = reverseValue.toBoolean(); - JS::RootedObject thisObj(cx, JS::GetNonCCWObjectGlobal(&args.callee())); - JS::RootedValue elementVal0(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, thisObj, elementVal0)->getPyObject(); + PyObject *args_0 = pyTypeFactory(cx, elementVal0)->getPyObject(); PyObject *args_0_result = PyObject_CallFunction(keyfunc, "O", args_0); if (!args_0_result) { return false; } JS::RootedValue elementVal1(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, thisObj, elementVal1)->getPyObject(); + PyObject *args_1 = pyTypeFactory(cx, elementVal1)->getPyObject(); PyObject *args_1_result = PyObject_CallFunction(keyfunc, "O", args_1); if (!args_1_result) { return false; @@ -1133,13 +1117,11 @@ static bool sort_compare_default(JSContext *cx, unsigned argc, JS::Value *vp) { } bool reverse = reverseValue.toBoolean(); - JS::RootedObject thisObj(cx, JS::GetNonCCWObjectGlobal(&args.callee())); - JS::RootedValue elementVal0(cx, args[0]); - PyObject *args_0 = pyTypeFactory(cx, thisObj, elementVal0)->getPyObject(); + PyObject *args_0 = pyTypeFactory(cx, elementVal0)->getPyObject(); JS::RootedValue elementVal1(cx, args[1]); - PyObject *args_1 = pyTypeFactory(cx, thisObj, elementVal1)->getPyObject(); + PyObject *args_1 = pyTypeFactory(cx, elementVal1)->getPyObject(); int cmp = PyObject_RichCompareBool(args_0, args_1, Py_LT); if (cmp > 0) { diff --git a/src/JSFunctionProxy.cc b/src/JSFunctionProxy.cc index db63e147..be660abc 100644 --- a/src/JSFunctionProxy.cc +++ b/src/JSFunctionProxy.cc @@ -59,5 +59,5 @@ PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(PyObject *self, return NULL; } - return pyTypeFactory(cx, thisObj, jsReturnVal)->getPyObject(); + return pyTypeFactory(cx, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSMethodProxy.cc b/src/JSMethodProxy.cc index 679c04b8..5ce05bd1 100644 --- a/src/JSMethodProxy.cc +++ b/src/JSMethodProxy.cc @@ -70,6 +70,5 @@ PyObject *JSMethodProxyMethodDefinitions::JSMethodProxy_call(PyObject *self, PyO return NULL; } - JS::RootedObject globalObj(cx, JS::CurrentGlobalOrNull(cx)); - return pyTypeFactory(cx, globalObj, jsReturnVal)->getPyObject(); + return pyTypeFactory(cx, jsReturnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index 02c5f602..a8bc1c9c 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -57,10 +57,9 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject PyObject *value; if (self->it.kind != KIND_KEYS) { - JS::RootedObject thisObj(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject); JS::RootedValue jsVal(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, thisObj, jsVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, jsVal)->getPyObject(); } PyObject *ret; @@ -84,10 +83,9 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject PyObject *value; if (self->it.kind != KIND_KEYS) { - JS::RootedObject thisObj(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject); JS::RootedValue jsVal(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); - value = pyTypeFactory(GLOBAL_CX, thisObj, jsVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, jsVal)->getPyObject(); } PyObject *ret; diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 920563d8..1cefdc84 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -70,7 +70,6 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId if (methodName == NULL || !PyUnicode_Check(key)) { JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); - JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); // if value is a JSFunction, bind `this` to self /* (Caleb Aikens) its potentially problematic to bind it like this since if the function * ever gets assigned to another object like so: @@ -78,7 +77,7 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId * jsObjA.func = jsObjB.func * jsObjA.func() # `this` will be jsObjB not jsObjA * - * it will be bound to the wrong object, however I can't find a better way to do this, + * It will be bound to the wrong object, however I can't find a better way to do this, * and even pyodide works this way weirdly enough: * https://github.com/pyodide/pyodide/blob/ee863a7f7907dfb6ee4948bde6908453c9d7ac43/src/core/jsproxy.c#L388 * @@ -99,7 +98,7 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId } } - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { @@ -224,8 +223,7 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr JS::RootedValue key(GLOBAL_CX); key.setString(id.toString()); - JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); - PyObject *pyKey = pyTypeFactory(GLOBAL_CX, thisObj, key)->getPyObject(); + PyObject *pyKey = pyTypeFactory(GLOBAL_CX, key)->getPyObject(); PyObject *pyVal1 = PyObject_GetItem((PyObject *)self, pyKey); PyObject *pyVal2 = PyObject_GetItem((PyObject *)other, pyKey); if (!pyVal2) { // if other.key is NULL then not equal @@ -294,8 +292,6 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self PyObject *key = NULL, *value = NULL; - JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); - JS::RootedIdVector props(GLOBAL_CX); if (_PyUnicodeWriter_WriteChar(&writer, '{') < 0) { @@ -344,7 +340,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self if (&elementVal.toObject() == self->jsObject.get()) { value = (PyObject *)self; } else { - value = pyTypeFactory(GLOBAL_CX, thisObj, elementVal)->getPyObject(); + value = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } Py_INCREF(value); @@ -488,7 +484,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, rootedObject, ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, ret)->getPyObject(); } } @@ -615,8 +611,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy JS::ObjectOpResult ignoredResult; JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); - JS::RootedObject thisObj(GLOBAL_CX, self->jsObject); - return pyTypeFactory(GLOBAL_CX, thisObj, value)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } } @@ -657,7 +652,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method(JSObjectProx PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; } - return pyTypeFactory(GLOBAL_CX, rootedObject, ret)->getPyObject(); + return pyTypeFactory(GLOBAL_CX, ret)->getPyObject(); } PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_update_method(JSObjectProxy *self, PyObject *args, PyObject *kwds) { diff --git a/src/JobQueue.cc b/src/JobQueue.cc index 9aab22c3..c88ed589 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -29,9 +29,8 @@ bool JobQueue::enqueuePromiseJob(JSContext *cx, JS::HandleObject incumbentGlobal) { // Convert the `job` JS function to a Python function for event-loop callback - JS::RootedObject global(cx, incumbentGlobal); JS::RootedValue jobv(cx, JS::ObjectValue(*job)); - PyObject *callback = pyTypeFactory(cx, global, jobv)->getPyObject(); + PyObject *callback = pyTypeFactory(cx, jobv)->getPyObject(); // Send job to the running Python event-loop PyEventLoop loop = PyEventLoop::getRunningLoop(); diff --git a/src/PromiseType.cc b/src/PromiseType.cc index e6014622..224b9141 100644 --- a/src/PromiseType.cc +++ b/src/PromiseType.cc @@ -36,10 +36,8 @@ static bool onResolvedCb(JSContext *cx, unsigned argc, JS::Value *vp) { // Convert the Promise's result (either fulfilled resolution or rejection reason) to a Python object // The result might be another JS function, so we must keep them alive - JS::RootedObject thisv(cx); - args.computeThis(cx, &thisv); // thisv is the global object, not the promise JS::RootedValue resultArg(cx, args[0]); - PyObject *result = pyTypeFactory(cx, thisv, resultArg)->getPyObject(); + PyObject *result = pyTypeFactory(cx, resultArg)->getPyObject(); if (state == JS::PromiseState::Rejected && !PyExceptionInstance_Check(result)) { // Wrap the result object into a SpiderMonkeyError object // because only *Exception objects can be thrown in Python `raise` statement and alike diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index 25fc0628..ae47c238 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -68,9 +68,8 @@ bool PyDictProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::HandleId JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - if (PyDict_SetItem(self, attrName, pyTypeFactory(cx, *global, rootedV)->getPyObject())) { + if (PyDict_SetItem(self, attrName, pyTypeFactory(cx, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 3036d5cd..2e69f111 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -44,10 +44,9 @@ static bool makeNewPyMethod(JSContext *cx, JS::MutableHandleValue function, JS:: } PyObject *func = PyMethod_Function(method); - JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); JS::RootedValue thisValue(cx); thisValue.setObject(*thisObject); - PyObject *newSelf = pyTypeFactory(cx, global, thisValue)->getPyObject(); + PyObject *newSelf = pyTypeFactory(cx, thisValue)->getPyObject(); function.set(jsTypeFactory(cx, PyMethod_New(func, newSelf))); return true; @@ -109,12 +108,11 @@ static bool array_push(JSContext *cx, unsigned argc, JS::Value *vp) { // surely } PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - JS::RootedObject thisObj(cx, proxy); unsigned numArgs = args.length(); JS::RootedValue elementVal(cx); for (unsigned index = 0; index < numArgs; index++) { elementVal.set(args[index].get()); - if (PyList_Append(self, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { + if (PyList_Append(self, pyTypeFactory(cx, elementVal)->getPyObject()) < 0) { return false; } } @@ -160,11 +158,10 @@ static bool array_unshift(JSContext *cx, unsigned argc, JS::Value *vp) { // sure } PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - JS::RootedObject thisObj(cx, proxy); JS::RootedValue elementVal(cx); for (int index = args.length() - 1; index >= 0; index--) { elementVal.set(args[index].get()); - if (PyList_Insert(self, 0, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { + if (PyList_Insert(self, 0, pyTypeFactory(cx, elementVal)->getPyObject()) < 0) { return false; } } @@ -275,9 +272,8 @@ static bool array_indexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } } - JS::RootedObject thisObj(cx, proxy); JS::RootedValue elementVal(cx, args[0].get()); - PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, thisObj, elementVal)->getPyObject(), start); + PyObject *result = PyObject_CallMethod(self, "index", "Oi", pyTypeFactory(cx, elementVal)->getPyObject(), start); if (!result) { PyErr_Clear(); @@ -356,10 +352,9 @@ static bool array_splice(JSContext *cx, unsigned argc, JS::Value *vp) { } JS::RootedValue elementVal(cx); - JS::RootedObject thisObj(cx, proxy); for (int index = 0; index < insertCount; index++) { elementVal.set(args[index + 2].get()); - if (PyList_SetItem(inserted, index, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { + if (PyList_SetItem(inserted, index, pyTypeFactory(cx, elementVal)->getPyObject()) < 0) { return false; } } @@ -422,9 +417,8 @@ static bool array_fill(JSContext *cx, unsigned argc, JS::Value *vp) { actualEnd = uint64_t(std::min(double(relativeEnd), double(selfLength))); } - JS::RootedObject thisObj(cx, proxy); JS::RootedValue fillValue(cx, args[0].get()); - PyObject *fillValueItem = pyTypeFactory(cx, thisObj, fillValue)->getPyObject(); + PyObject *fillValueItem = pyTypeFactory(cx, fillValue)->getPyObject(); for (int index = actualStart; index < actualEnd; index++) { if (PyList_SetItem(self, index, fillValueItem) < 0) { return false; @@ -540,8 +534,6 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - JS::RootedObject thisObj(cx, proxy); - Py_ssize_t selfSize = PyList_GET_SIZE(self); PyObject *result = PyList_New(selfSize); @@ -555,7 +547,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { for (unsigned index = 0; index < numArgs; index++) { elementVal.set(args[index].get()); - PyObject *item = pyTypeFactory(cx, thisObj, elementVal)->getPyObject(); + PyObject *item = pyTypeFactory(cx, elementVal)->getPyObject(); if (PyObject_TypeCheck(item, &JSArrayProxyType)) { // flatten the array only a depth 1 Py_ssize_t itemLength = JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)item); @@ -563,7 +555,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { if (!JS_GetElement(cx, ((JSArrayProxy *)item)->jsArray, flatIndex, &elementVal)) { return false; } - if (PyList_Append(result, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, elementVal)->getPyObject()) < 0) { return false; } } @@ -578,7 +570,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { } } else { - if (PyList_Append(result, pyTypeFactory(cx, thisObj, elementVal)->getPyObject()) < 0) { + if (PyList_Append(result, pyTypeFactory(cx, elementVal)->getPyObject()) < 0) { return false; } } @@ -628,9 +620,8 @@ static bool array_lastIndexOf(JSContext *cx, unsigned argc, JS::Value *vp) { } } - JS::RootedObject thisObj(cx, proxy); JS::RootedValue elementVal(cx, args[0].get()); - PyObject *element = pyTypeFactory(cx, thisObj, elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, elementVal)->getPyObject(); for (int64_t index = start; index >= 0; index--) { PyObject *item = PyList_GetItem(self, index); Py_INCREF(item); @@ -1236,7 +1227,7 @@ static bool array_findIndex(JSContext *cx, unsigned argc, JS::Value *vp) { } // private -static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject thisObj, +static uint32_t FlattenIntoArray(JSContext *cx, JSObject *retArray, PyObject *source, Py_ssize_t sourceLen, uint32_t start, uint32_t depth) { @@ -1253,7 +1244,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject thisObj, elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); } - PyObject *element = pyTypeFactory(cx, thisObj, elementVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, elementVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1271,7 +1262,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject thisObj, elementLen = PyList_GET_SIZE(element); } - targetIndex = FlattenIntoArray(cx, thisObj, + targetIndex = FlattenIntoArray(cx, retArray, element, elementLen, @@ -1298,7 +1289,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, JS::HandleObject thisObj, } // private -static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject thisObj, +static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JSObject *retArray, PyObject *source, Py_ssize_t sourceLen, uint32_t start, uint32_t depth, JS::HandleValue callBack, JS::HandleObject thisArg) { @@ -1325,7 +1316,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject thi return false; } - PyObject *element = pyTypeFactory(cx, thisObj, retVal)->getPyObject(); + PyObject *element = pyTypeFactory(cx, retVal)->getPyObject(); bool shouldFlatten; if (depth > 0) { @@ -1343,7 +1334,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::HandleObject thi } if (shouldFlatten) { - targetIndex = FlattenIntoArrayWithCallBack(cx, thisObj, + targetIndex = FlattenIntoArrayWithCallBack(cx, retArray, element, elementLen, @@ -1413,11 +1404,9 @@ static bool array_flat(JSContext *cx, unsigned argc, JS::Value *vp) { depthNum = 1; } - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - JSObject *retArray = JS::NewArrayObject(cx, sourceLen); // min end length - FlattenIntoArray(cx, *global, retArray, self, sourceLen, 0, depthNum); + FlattenIntoArray(cx, retArray, self, sourceLen, 0, depthNum); args.rval().setObject(*retArray); return true; @@ -1466,11 +1455,9 @@ static bool array_flatMap(JSContext *cx, unsigned argc, JS::Value *vp) { rootedThisArg.set(nullptr); } - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); - JSObject *retArray = JS::NewArrayObject(cx, sourceLen); // min end length - FlattenIntoArrayWithCallBack(cx, *global, retArray, self, sourceLen, 0, 1, callBack, rootedThisArg); + FlattenIntoArrayWithCallBack(cx, retArray, self, sourceLen, 0, 1, callBack, rootedThisArg); args.rval().setObject(*retArray); return true; @@ -2103,10 +2090,8 @@ bool PyListProxyHandler::defineProperty( return result.failInvalidDescriptor(); } - // FIXME (Tom Tang): memory leak - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); JS::RootedValue itemV(cx, desc.value()); - PyObject *item = pyTypeFactory(cx, *global, itemV)->getPyObject(); + PyObject *item = pyTypeFactory(cx, itemV)->getPyObject(); PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); if (PyList_SetItem(self, index, item) < 0) { // we are out-of-bounds and need to expand diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 93f21072..593187ea 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -78,9 +78,8 @@ bool PyObjectProxyHandler::set(JSContext *cx, JS::HandleObject proxy, JS::Handle JS::RootedValue rootedV(cx, v); PyObject *attrName = idToKey(cx, id); - JS::RootedObject *global = new JS::RootedObject(cx, JS::GetNonCCWObjectGlobal(proxy)); PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - if (PyObject_SetAttr(self, attrName, pyTypeFactory(cx, *global, rootedV)->getPyObject())) { + if (PyObject_SetAttr(self, attrName, pyTypeFactory(cx, rootedV)->getPyObject())) { return result.failCantSetInterposed(); // raises JS exception } return result.succeed(); diff --git a/src/internalBinding.cc b/src/internalBinding.cc index bf789fa2..41b13ff1 100644 --- a/src/internalBinding.cc +++ b/src/internalBinding.cc @@ -59,7 +59,6 @@ PyObject *getInternalBindingPyFn(JSContext *cx) { JSObject *jsFn = (JSObject *)createInternalBinding(cx); // Convert to a Python function - JS::RootedObject thisObj(cx, nullptr); JS::RootedValue jsFnVal(cx, JS::ObjectValue(*jsFn)); - return pyTypeFactory(cx, thisObj, jsFnVal)->getPyObject(); + return pyTypeFactory(cx, jsFnVal)->getPyObject(); } \ No newline at end of file diff --git a/src/internalBinding/timers.cc b/src/internalBinding/timers.cc index 0ffa3e72..63ac9b45 100644 --- a/src/internalBinding/timers.cc +++ b/src/internalBinding/timers.cc @@ -23,9 +23,8 @@ static bool enqueueWithDelay(JSContext *cx, unsigned argc, JS::Value *vp) { double delaySeconds = args.get(1).toNumber(); // Convert to a Python function - JS::RootedObject thisv(cx, nullptr); JS::RootedValue jobArg(cx, jobArgVal); - PyObject *job = pyTypeFactory(cx, thisv, jobArg)->getPyObject(); + PyObject *job = pyTypeFactory(cx, jobArg)->getPyObject(); // Schedule job to the running Python event-loop PyEventLoop loop = PyEventLoop::getRunningLoop(); diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 100e847c..97322297 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -281,9 +281,6 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { JS::Value pyFuncVal = js::GetFunctionNativeReserved(&(callargs.callee()), 0); PyObject *pyFunc = (PyObject *)(pyFuncVal.toPrivate()); - JS::RootedObject thisv(cx); - JS_ValueToObject(cx, callargs.thisv(), &thisv); - unsigned int callArgsLength = callargs.length(); if (!callArgsLength) { @@ -304,7 +301,7 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { PyObject *pyArgs = PyTuple_New(callArgsLength); for (size_t i = 0; i < callArgsLength; i++) { JS::RootedValue jsArg(cx, callargs[i]); - PyType *pyArg = pyTypeFactory(cx, thisv, jsArg); + PyType *pyArg = pyTypeFactory(cx, jsArg); if (!pyArg) return false; // error occurred PyObject *pyArgObj = pyArg->getPyObject(); if (!pyArgObj) return false; // error occurred diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 8ae8e075..ee1458d0 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -360,7 +360,7 @@ static PyObject *eval(PyObject *self, PyObject *args) { } // translate to the proper python type - PyType *returnValue = pyTypeFactory(GLOBAL_CX, *global, *rval); + PyType *returnValue = pyTypeFactory(GLOBAL_CX, *rval); if (PyErr_Occurred()) { return NULL; } diff --git a/src/pyTypeFactory.cc b/src/pyTypeFactory.cc index ab772496..e9aa3a14 100644 --- a/src/pyTypeFactory.cc +++ b/src/pyTypeFactory.cc @@ -40,7 +40,7 @@ #include -PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue rval) { +PyType *pyTypeFactory(JSContext *cx, JS::HandleValue rval) { if (rval.isUndefined()) { return new NoneType(); } @@ -89,7 +89,7 @@ PyType *pyTypeFactory(JSContext *cx, JS::HandleObject thisObj, JS::HandleValue r case js::ESClass::BigInt: case js::ESClass::String: js::Unbox(cx, obj, &unboxed); - return pyTypeFactory(cx, thisObj, unboxed); + return pyTypeFactory(cx, unboxed); case js::ESClass::Date: return new DateType(cx, obj); case js::ESClass::Promise: From 4ba97f7cdedb77493fe55135abd52b971ed62abe Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 28 Feb 2024 13:52:06 -0500 Subject: [PATCH 100/170] increment firefox version --- .github/workflows/test-and-publish.yaml | 6 +++--- setup.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 279360f9..71d2d4e1 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -41,7 +41,7 @@ jobs: with: path: | ./_spidermonkey_install/* - key: spidermonkey115.7.0-${{ runner.os }}-${{ runner.arch }} + key: spidermonkey115.8.0-${{ runner.os }}-${{ runner.arch }} lookup-only: true # skip download - name: Setup XCode if: ${{ (matrix.os == 'macos-13' || matrix.os == 'macos-14') && steps.cache-spidermonkey.outputs.cache-hit != 'true' }} @@ -61,7 +61,7 @@ jobs: with: path: | ./_spidermonkey_install/* - key: spidermonkey115.7.0-${{ runner.os }}-${{ runner.arch }} + key: spidermonkey115.8.0-${{ runner.os }}-${{ runner.arch }} lookup-only: true # skip download - name: Install dependencies if: ${{ steps.cache-spidermonkey.outputs.cache-hit != 'true' }} @@ -142,7 +142,7 @@ jobs: with: path: | ./_spidermonkey_install/* - key: spidermonkey115.7.0-${{ runner.os }}-${{ runner.arch }} + key: spidermonkey115.8.0-${{ runner.os }}-${{ runner.arch }} fail-on-cache-miss: true # SpiderMonkey is expected to be cached in its dedicated job - name: Build pminit run: | diff --git a/setup.sh b/setup.sh index b148d583..93d13ccc 100755 --- a/setup.sh +++ b/setup.sh @@ -39,9 +39,9 @@ $POETRY_BIN self add 'poetry-dynamic-versioning[plugin]' echo "Done installing dependencies" echo "Downloading spidermonkey source code" -wget -c -q https://ftp.mozilla.org/pub/firefox/releases/115.7.0esr/source/firefox-115.7.0esr.source.tar.xz +wget -c -q https://ftp.mozilla.org/pub/firefox/releases/115.8.0esr/source/firefox-115.8.0esr.source.tar.xz mkdir -p firefox-source -tar xf firefox-115.7.0esr.source.tar.xz -C firefox-source --strip-components=1 # strip the root folder +tar xf firefox-115.8.0esr.source.tar.xz -C firefox-source --strip-components=1 # strip the root folder echo "Done downloading spidermonkey source code" echo "Building spidermonkey" From 4df71eeca88a4b42732c358ba45958afd0979a64 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 28 Feb 2024 14:08:12 -0500 Subject: [PATCH 101/170] version update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b2f8f7e4..3013dfb5 100644 --- a/README.md +++ b/README.md @@ -396,7 +396,7 @@ List of commands: ```console $ pmjs -Welcome to PythonMonkey v0.3.0. +Welcome to PythonMonkey v0.3.1. Type ".help" for more information. > .python import sys > .python sys.path From 85dd1eeffdef782eac05a3ce1ee9b5a187a1ab6b Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 10:53:34 -0500 Subject: [PATCH 102/170] chore(PyDictOrObjectProxyHandler): remove blank line --- src/PyDictOrObjectProxyHandler.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PyDictOrObjectProxyHandler.cc b/src/PyDictOrObjectProxyHandler.cc index 3b41b5a4..4e835fed 100644 --- a/src/PyDictOrObjectProxyHandler.cc +++ b/src/PyDictOrObjectProxyHandler.cc @@ -107,7 +107,6 @@ void PyDictOrObjectProxyHandler::handleFinalize(JSObject *proxy) { // Then, when shutting down, there is only on reference left, and we don't need // to free the object since the entire process memory is being released. PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - if (Py_REFCNT(self) > 1) { Py_DECREF(self); } From c4afd1029ed0090947976fa8caf2b695ceeb884b Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 11:03:59 -0500 Subject: [PATCH 103/170] refactor(PyDictOrObjectProxyHandler): refactor the finalize method out to the parent class since its identical for both child classes --- include/PyDictOrObjectProxyHandler.hh | 8 +++++++- include/PyDictProxyHandler.hh | 7 ------- include/PyObjectProxyHandler.hh | 7 ------- src/PyDictOrObjectProxyHandler.cc | 2 +- src/PyDictProxyHandler.cc | 4 ---- src/PyObjectProxyHandler.cc | 4 ---- 6 files changed, 8 insertions(+), 24 deletions(-) diff --git a/include/PyDictOrObjectProxyHandler.hh b/include/PyDictOrObjectProxyHandler.hh index a41d81ad..0cb340eb 100644 --- a/include/PyDictOrObjectProxyHandler.hh +++ b/include/PyDictOrObjectProxyHandler.hh @@ -45,7 +45,13 @@ struct PyDictOrObjectProxyHandler : public PyBaseProxyHandler { static bool handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, JS::MutableHandle> desc, PyObject *item); - static void handleFinalize(JSObject *proxy); + /** + * @brief Handles python object reference count when JS Proxy object is finalized + * + * @param gcx pointer to JS::GCContext + * @param proxy the proxy object being finalized + */ + void finalize(JS::GCContext *gcx, JSObject *proxy) const override; /** * @brief Helper function used by dicts and objects to convert dict/object to String diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index 3784076e..7ddac82b 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -115,13 +115,6 @@ public: bool getOwnEnumerablePropertyKeys( JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const override; - /** - * @brief Handles python object reference count when JS Proxy object is finalized - * - * @param gcx pointer to JS::GCContext - * @param proxy the proxy object being finalized - */ - void finalize(JS::GCContext *gcx, JSObject *proxy) const override; bool getOwnPropertyDescriptor( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 9e71b18d..0cb83eee 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -114,13 +114,6 @@ public: bool getOwnEnumerablePropertyKeys( JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const override; - /** - * @brief Handles python object reference count when JS Proxy object is finalized - * - * @param gcx pointer to JS::GCContext - * @param proxy the proxy object being finalized - */ - void finalize(JS::GCContext *gcx, JSObject *proxy) const override; bool getOwnPropertyDescriptor( JSContext *cx, JS::HandleObject proxy, JS::HandleId id, diff --git a/src/PyDictOrObjectProxyHandler.cc b/src/PyDictOrObjectProxyHandler.cc index 4e835fed..cf7bbcb7 100644 --- a/src/PyDictOrObjectProxyHandler.cc +++ b/src/PyDictOrObjectProxyHandler.cc @@ -102,7 +102,7 @@ bool PyDictOrObjectProxyHandler::handleGetOwnPropertyDescriptor(JSContext *cx, J return true; } -void PyDictOrObjectProxyHandler::handleFinalize(JSObject *proxy) { +void PyDictOrObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { // We cannot call Py_DECREF here when shutting down as the thread state is gone. // Then, when shutting down, there is only on reference left, and we don't need // to free the object since the entire process memory is being released. diff --git a/src/PyDictProxyHandler.cc b/src/PyDictProxyHandler.cc index 146f9af2..e9807f4a 100644 --- a/src/PyDictProxyHandler.cc +++ b/src/PyDictProxyHandler.cc @@ -94,10 +94,6 @@ bool PyDictProxyHandler::getOwnEnumerablePropertyKeys( return this->ownPropertyKeys(cx, proxy, props); } -void PyDictProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { - return handleFinalize(proxy); -} - bool PyDictProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 9a287858..005090d0 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -105,10 +105,6 @@ bool PyObjectProxyHandler::getOwnEnumerablePropertyKeys( return this->ownPropertyKeys(cx, proxy, props); } -void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { - return handleFinalize(proxy); -} - bool PyObjectProxyHandler::defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, JS::Handle desc, From edb9f69765557b6a77a01be4c205a409deb5bb32 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 11:14:36 -0500 Subject: [PATCH 104/170] chore(PyProxyHandlers): remove TODO comments about proxy object traps, as their implementations are correct. --- include/PyDictProxyHandler.hh | 30 +++++++++++++----------------- include/PyObjectProxyHandler.hh | 26 +++++++++++++------------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index 7ddac82b..35351ee5 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -40,7 +40,7 @@ public: * @param cx - pointer to JSContext * @param proxy - The proxy object who's property we wish to delete * @param id - The key we wish to delete - * @param result - @TODO (Caleb Aikens) read up on JS::ObjectOpResult + * @param result - whether the call succeeded or not * @return true - call succeeded * @return false - call failed and an exception has been raised */ @@ -64,8 +64,8 @@ public: * @param proxy The proxy object who's property we wish to set * @param id Key of the property we wish to set * @param v Value that we wish to set the property to - * @param receiver @TODO (Caleb Aikens) read ECMAScript docs about this - * @param result @TODO (Caleb Aikens) read ECMAScript docs about this + * @param receiver The `this` value to use when executing any code + * @param result whether or not the call succeeded * @return true call succeed * @return false call failed and an exception has been raised */ @@ -79,17 +79,15 @@ public: * @param proxy - The proxy object who's keys we output * @param props - out-parameter of object IDsoverride; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more - /** + /** * @return true - call succeeded * @return false - call failed and an exception has been raised */ bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const override; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly + * @brief Returns true if `id` is in `proxy`, false otherwise * * @param cx pointer to JSContext * @param proxy The proxy object who's property we wish to check @@ -100,17 +98,15 @@ public: */ bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const override; - /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly - * - * @param cx - pointer to JSContext - * @param proxy - The proxy object who's keys we outputoverride; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more - /** - * @param props - out-parameter of object IDs - * @return true - call succeeded - * @return false - call failed and an exception has been raised + /** + * @brief Returns vector of proxy's own keys + * + * @param cx - Pointer to the JSContext + * @param proxy - the proxy object + * @param props - out parameter, the vector of proxy's own keys + * @return true - the call succeeded + * @return false - the call failed and an exception has been raised */ bool getOwnEnumerablePropertyKeys( JSContext *cx, JS::HandleObject proxy, diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 0cb83eee..460caded 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -44,7 +44,7 @@ public: * @param cx - pointer to JSContext * @param proxy - The proxy object who's property we wish to delete * @param id - The key we wish to delete - * @param result - @TODO (Caleb Aikens) read up on JS::ObjectOpResult + * @param result - whether the call succeeded or not * @return true - call succeeded * @return false - call failed and an exception has been raised */ @@ -69,8 +69,8 @@ public: * @param proxy The proxy object who's property we wish to set * @param id Key of the property we wish to set * @param v Value that we wish to set the property to - * @param receiver @TODO (Caleb Aikens) read ECMAScript docs about this - * @param result @TODO (Caleb Aikens) read ECMAScript docs about this + * @param receiver The `this` value to use when executing any code + * @param result whether or not the call succeeded * @return true call succeed * @return false call failed and an exception has been raised */ @@ -89,9 +89,8 @@ public: bool enumerate(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const override; - // @TODO (Caleb Aikens) The following are Spidermonkey-unique extensions, need to read into them more /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly + * @brief Returns true if `id` is in `proxy`, false otherwise * * @param cx pointer to JSContext * @param proxy The proxy object who's property we wish to check @@ -102,14 +101,15 @@ public: */ bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const override; - /** - * @brief @TODO (Caleb Aikens) read up on what this trap does exactly - * - * @param cx - pointer to JSContext - * @param proxy - The proxy object who's keys we output - * @param props - out-parameter of object IDs - * @return true - call succeeded - * @return false - call failed and an exception has been raised + + /** + * @brief Returns vector of proxy's own keys + * + * @param cx - Pointer to the JSContext + * @param proxy - the proxy object + * @param props - out parameter, the vector of proxy's own keys + * @return true - the call succeeded + * @return false - the call failed and an exception has been raised */ bool getOwnEnumerablePropertyKeys( JSContext *cx, JS::HandleObject proxy, From 61b2875ae4a43cecd1e54b7c7e50fe396d4683c0 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 11:18:14 -0500 Subject: [PATCH 105/170] chore(tests): remove extra empty lines from test_objects.py --- tests/python/test_objects.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/python/test_objects.py b/tests/python/test_objects.py index 3e487554..8256c243 100644 --- a/tests/python/test_objects.py +++ b/tests/python/test_objects.py @@ -133,6 +133,4 @@ def __init__(self): self.a = 42 o = MyClass() - assert '[object Object]' == pm.eval("(obj) => { return obj.toLocaleString(); }")(o) - - \ No newline at end of file + assert '[object Object]' == pm.eval("(obj) => { return obj.toLocaleString(); }")(o) \ No newline at end of file From ba1afb88494620d9bf603770f5d3ac1be63f18b0 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 29 Feb 2024 11:18:59 -0500 Subject: [PATCH 106/170] use PersistentRooted pointer instead of inline in struct for Object and Array proxies --- include/JSArrayProxy.hh | 2 +- include/JSObjectProxy.hh | 2 +- src/DictType.cc | 3 +- src/JSArrayIterProxy.cc | 4 +- src/JSArrayProxy.cc | 114 +++++++++++++++++++------------------ src/JSObjectItemsProxy.cc | 4 +- src/JSObjectIterProxy.cc | 4 +- src/JSObjectKeysProxy.cc | 4 +- src/JSObjectProxy.cc | 59 ++++++++++--------- src/JSObjectValuesProxy.cc | 4 +- src/ListType.cc | 3 +- src/PyListProxyHandler.cc | 8 +-- src/jsTypeFactory.cc | 12 +++- 13 files changed, 119 insertions(+), 104 deletions(-) diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index f6875954..592778d7 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -24,7 +24,7 @@ */ typedef struct { PyListObject list; - JS::PersistentRootedObject jsArray; + JS::PersistentRootedObject *jsArray; } JSArrayProxy; /** diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index e1b2e9d7..d54c8867 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -23,7 +23,7 @@ */ typedef struct { PyDictObject dict; - JS::PersistentRootedObject jsObject; + JS::PersistentRootedObject *jsObject; } JSObjectProxy; /** diff --git a/src/DictType.cc b/src/DictType.cc index c24eabf8..b5b36d30 100644 --- a/src/DictType.cc +++ b/src/DictType.cc @@ -31,7 +31,8 @@ DictType::DictType(JSContext *cx, JS::Handle jsObject) { if (proxy != NULL) { JS::RootedObject obj(cx); JS_ValueToObject(cx, jsObject, &obj); - proxy->jsObject.set(obj); + proxy->jsObject = new JS::PersistentRootedObject(cx); + proxy->jsObject->set(obj); this->pyObject = (PyObject *)proxy; } } \ No newline at end of file diff --git a/src/JSArrayIterProxy.cc b/src/JSArrayIterProxy.cc index f6da0719..8d090571 100644 --- a/src/JSArrayIterProxy.cc +++ b/src/JSArrayIterProxy.cc @@ -48,14 +48,14 @@ PyObject *JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_next(JSArrayIterPr if (self->it.reversed) { if (self->it.it_index >= 0) { JS::RootedValue elementVal(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index--, &elementVal); + JS_GetElement(GLOBAL_CX, *(((JSArrayProxy *)seq)->jsArray), self->it.it_index--, &elementVal); return pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } } else { if (self->it.it_index < JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)seq)) { JS::RootedValue elementVal(GLOBAL_CX); - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)seq)->jsArray, self->it.it_index++, &elementVal); + JS_GetElement(GLOBAL_CX, *(((JSArrayProxy *)seq)->jsArray), self->it.it_index++, &elementVal); return pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } } diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index aa27fb05..d552f4be 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -27,7 +27,8 @@ void JSArrayProxyMethodDefinitions::JSArrayProxy_dealloc(JSArrayProxy *self) { - self->jsArray.set(nullptr); + self->jsArray->set(nullptr); + delete self->jsArray; PyObject_GC_UnTrack(self); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -35,7 +36,7 @@ void JSArrayProxyMethodDefinitions::JSArrayProxy_dealloc(JSArrayProxy *self) Py_ssize_t JSArrayProxyMethodDefinitions::JSArrayProxy_length(JSArrayProxy *self) { uint32_t length; - JS::GetArrayLength(GLOBAL_CX, self->jsArray, &length); + JS::GetArrayLength(GLOBAL_CX, *(self->jsArray), &length); return (Py_ssize_t)length; } @@ -52,7 +53,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py const char *methodName = JSArrayProxyType.tp_methods[index].ml_name; if (methodName == NULL || !PyUnicode_Check(key)) { // reached end of list JS::RootedValue value(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); + JS_GetPropertyById(GLOBAL_CX, *(self->jsArray), id, &value); return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } else { @@ -70,7 +71,7 @@ static PyObject *list_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihig jArgs[0].setInt32(ilow); jArgs[1].setInt32(ihigh); JS::RootedValue jReturnedArray(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "slice", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "slice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -100,7 +101,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS_IndexToId(GLOBAL_CX, index, &id); JS::RootedValue value(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsArray, id, &value); + JS_GetPropertyById(GLOBAL_CX, *(self->jsArray), id, &value); return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } @@ -124,7 +125,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get_subscript(JSArrayProxy JS::RootedValue elementVal(GLOBAL_CX); for (size_t cur = start, index = 0; index < slicelength; cur += (size_t)step, index++) { - JS_GetElement(GLOBAL_CX, self->jsArray, cur, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), cur, &elementVal); JS_SetElement(GLOBAL_CX, jCombinedArray, index, elementVal); } @@ -213,30 +214,30 @@ static int list_ass_slice(JSArrayProxy *self, Py_ssize_t ilow, Py_ssize_t ihigh, if (d < 0) { /* Delete -d items */ JS::RootedValue elementVal(GLOBAL_CX); for (size_t index = ihigh, count = 0; count < selfLength - ihigh; index++, count++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - JS_SetElement(GLOBAL_CX, self->jsArray, index+d, elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), index+d, elementVal); } - JS::SetArrayLength(GLOBAL_CX, self->jsArray, selfLength + d); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), selfLength + d); } else if (d > 0) { /* Insert d items */ k = selfLength; - JS::SetArrayLength(GLOBAL_CX, self->jsArray, k + d); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), k + d); selfLength = k + d; JS::RootedValue elementVal(GLOBAL_CX); for (size_t index = ihigh, count = 0; count < k - ihigh; index++, count++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - JS_SetElement(GLOBAL_CX, self->jsArray, index+d, elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), index+d, elementVal); } } JS::RootedValue elementVal(GLOBAL_CX); for (k = 0; k < n; k++, ilow++) { elementVal.set(jsTypeFactory(GLOBAL_CX, vitem[k])); - JS_SetElement(GLOBAL_CX, self->jsArray, ilow, elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), ilow, elementVal); } result = 0; @@ -269,10 +270,10 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_assign_key(JSArrayProxy *self, P if (value) { // we are setting a value JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value)); - JS_SetPropertyById(GLOBAL_CX, self->jsArray, id, jValue); + JS_SetPropertyById(GLOBAL_CX, *(self->jsArray), id, jValue); } else { // we are deleting a value JS::ObjectOpResult ignoredResult; - JS_DeletePropertyById(GLOBAL_CX, self->jsArray, id, ignoredResult); + JS_DeletePropertyById(GLOBAL_CX, *(self->jsArray), id, ignoredResult); } return 0; @@ -328,8 +329,8 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_assign_key(JSArrayProxy *self, P } for (size_t index = cur, count = 0; count < lim; index++, count++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index + 1, &elementVal); - JS_SetElement(GLOBAL_CX, self->jsArray, index - i, elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index + 1, &elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), index - i, elementVal); } } @@ -337,12 +338,12 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_assign_key(JSArrayProxy *self, P if (cur < (size_t)selfSize) { for (size_t index = cur, count = 0; count < selfSize - cur; index++, count++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); - JS_SetElement(GLOBAL_CX, self->jsArray, index - slicelength, elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), index - slicelength, elementVal); } } - JS::SetArrayLength(GLOBAL_CX, self->jsArray, selfSize - slicelength); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), selfSize - slicelength); return 0; } @@ -382,7 +383,7 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_assign_key(JSArrayProxy *self, P JS::RootedValue elementVal(GLOBAL_CX); for (cur = start, i = 0; i < slicelength; cur += (size_t)step, i++) { elementVal.set(jsTypeFactory(GLOBAL_CX, seqitems[i])); - JS_SetElement(GLOBAL_CX, self->jsArray, cur, elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), cur, elementVal); } Py_DECREF(seq); @@ -435,12 +436,12 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * Py_ssize_t index; /* Search for the first index where items are different */ for (index = 0; index < selfLength && index < otherLength; index++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); PyObject *leftItem = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); PyObject *rightItem; if (PyObject_TypeCheck(other, &JSArrayProxyType)) { - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)other)->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(((JSArrayProxy *)other)->jsArray), index, &elementVal); rightItem = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); } else { rightItem = ((PyListObject *)other)->ob_item[index]; @@ -476,7 +477,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare(JSArrayProxy * Py_RETURN_TRUE; } - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); /* Compare the final item again using the proper operator */ return PyObject_RichCompare(pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(), ((PyListObject *)other)->ob_item[index], op); } @@ -514,10 +515,10 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repr(JSArrayProxy *self) { } } - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); PyObject *s; - if (&elementVal.toObject() == self->jsArray.get()) { + if (&elementVal.toObject() == (*(self->jsArray)).get()) { s = PyObject_Repr((PyObject *)self); } else { s = PyObject_Repr(pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject()); @@ -605,13 +606,13 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_concat(JSArrayProxy *self, JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t inputIdx = 0; inputIdx < sizeSelf; inputIdx++) { - JS_GetElement(GLOBAL_CX, self->jsArray, inputIdx, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), inputIdx, &elementVal); JS_SetElement(GLOBAL_CX, jCombinedArray, inputIdx, elementVal); } if (PyObject_TypeCheck(value, &JSArrayProxyType)) { for (Py_ssize_t inputIdx = 0; inputIdx < sizeValue; inputIdx++) { - JS_GetElement(GLOBAL_CX, ((JSArrayProxy *)value)->jsArray, inputIdx, &elementVal); + JS_GetElement(GLOBAL_CX, *(((JSArrayProxy *)value)->jsArray), inputIdx, &elementVal); JS_SetElement(GLOBAL_CX, jCombinedArray, sizeSelf + inputIdx, elementVal); } } else { @@ -643,7 +644,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_repeat(JSArrayProxy *self, // one might think of using copyWithin but in SpiderMonkey it's implemented in JS! JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t inputIdx = 0; inputIdx < input_size; inputIdx++) { - JS_GetElement(GLOBAL_CX, self->jsArray, inputIdx, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), inputIdx, &elementVal); for (Py_ssize_t repeatIdx = 0; repeatIdx < n; repeatIdx++) { JS_SetElement(GLOBAL_CX, jCombinedArray, repeatIdx * input_size + inputIdx, elementVal); } @@ -663,7 +664,7 @@ int JSArrayProxyMethodDefinitions::JSArrayProxy_contains(JSArrayProxy *self, PyO JS::RootedValue elementVal(GLOBAL_CX); for (index = 0, cmp = 0; cmp == 0 && index < numElements; ++index) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); PyObject *item = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(item); cmp = PyObject_RichCompareBool(item, element, Py_EQ); @@ -677,7 +678,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_inplace_concat(JSArrayProx Py_ssize_t valueLength = Py_SIZE(value); // allocate extra spacePy_SIZE - JS::SetArrayLength(GLOBAL_CX, self->jsArray, selfLength + valueLength); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), selfLength + valueLength); JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value)); JS::RootedObject jRootedValue = JS::RootedObject(GLOBAL_CX, jValue.toObjectOrNull()); @@ -685,7 +686,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_inplace_concat(JSArrayProx JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t inputIdx = 0; inputIdx < valueLength; inputIdx++) { JS_GetElement(GLOBAL_CX, jRootedValue, inputIdx, &elementVal); - JS_SetElement(GLOBAL_CX, self->jsArray, selfLength + inputIdx, elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), selfLength + inputIdx, elementVal); } Py_INCREF(self); @@ -709,15 +710,15 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_inplace_repeat(JSArrayProx return PyErr_NoMemory(); } - JS::SetArrayLength(GLOBAL_CX, self->jsArray, input_size * n); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), input_size * n); // repeat within self // one might think of using copyWithin but in SpiderMonkey it's implemented in JS! JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t inputIdx = 0; inputIdx < input_size; inputIdx++) { - JS_GetElement(GLOBAL_CX, self->jsArray, inputIdx, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), inputIdx, &elementVal); for (Py_ssize_t repeatIdx = 0; repeatIdx < n; repeatIdx++) { - JS_SetElement(GLOBAL_CX, self->jsArray, repeatIdx * input_size + inputIdx, elementVal); + JS_SetElement(GLOBAL_CX, *(self->jsArray), repeatIdx * input_size + inputIdx, elementVal); } } @@ -726,17 +727,18 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_inplace_repeat(JSArrayProx } PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_clear_method(JSArrayProxy *self) { - JS::SetArrayLength(GLOBAL_CX, self->jsArray, 0); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), 0); Py_RETURN_NONE; } int JSArrayProxyMethodDefinitions::JSArrayProxy_clear(JSArrayProxy *self) { - // Nothing to be done + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), 0); return 0; } int JSArrayProxyMethodDefinitions::JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg) { // Nothing to be done + // TODO do we need to iterate through the list and call traverse on proxied PyObjects? return 0; } @@ -745,7 +747,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { jArgs[0].setInt32(0); jArgs[1].setInt32(JSArrayProxy_length(self)); JS::RootedValue jReturnedArray(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "slice", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "slice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -755,9 +757,9 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_append(JSArrayProxy *self, PyObject *value) { Py_ssize_t len = JSArrayProxy_length(self); - JS::SetArrayLength(GLOBAL_CX, self->jsArray, len + 1); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), len + 1); JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value)); - JS_SetElement(GLOBAL_CX, self->jsArray, len, jValue); + JS_SetElement(GLOBAL_CX, *(self->jsArray), len, jValue); Py_RETURN_NONE; } @@ -804,7 +806,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_insert(JSArrayProxy *self, jArgs[2].set(jsTypeFactory(GLOBAL_CX, value)); JS::RootedValue jReturnedArray(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "splice", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "splice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -827,14 +829,14 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_extend(JSArrayProxy *self, Py_ssize_t m = JSArrayProxy_length(self); - JS::SetArrayLength(GLOBAL_CX, self->jsArray, m + n); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), m + n); // populate the end of self with iterable's items. PyObject **src = PySequence_Fast_ITEMS(iterable); for (Py_ssize_t i = 0; i < n; i++) { PyObject *o = src[i]; JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, o)); - JS_SetElement(GLOBAL_CX, self->jsArray, m + i, jValue); + JS_SetElement(GLOBAL_CX, *(self->jsArray), m + i, jValue); } Py_DECREF(iterable); @@ -863,9 +865,9 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_extend(JSArrayProxy *self, break; } - JS::SetArrayLength(GLOBAL_CX, self->jsArray, len + 1); + JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), len + 1); JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, item)); - JS_SetElement(GLOBAL_CX, self->jsArray, len, jValue); + JS_SetElement(GLOBAL_CX, *(self->jsArray), len, jValue); len++; } @@ -916,7 +918,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_pop(JSArrayProxy *self, Py jArgs[1].setInt32(1); JS::RootedValue jReturnedArray(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "splice", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "splice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -934,7 +936,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t index = 0; index < selfSize; index++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); PyObject *obj = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); @@ -944,7 +946,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_remove(JSArrayProxy *self, jArgs[0].setInt32(index); jArgs[1].setInt32(1); JS::RootedValue jReturnedArray(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "splice", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "splice", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -999,7 +1001,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_index(JSArrayProxy *self, JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t index = start; index < stop && index < selfSize; index++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); PyObject *obj = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); @@ -1022,7 +1024,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_count(JSArrayProxy *self, Py_ssize_t length = JSArrayProxy_length(self); JS::RootedValue elementVal(GLOBAL_CX); for (Py_ssize_t index = 0; index < length; index++) { - JS_GetElement(GLOBAL_CX, self->jsArray, index, &elementVal); + JS_GetElement(GLOBAL_CX, *(self->jsArray), index, &elementVal); PyObject *obj = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); Py_INCREF(obj); int cmp = PyObject_RichCompareBool(obj, value, Py_EQ); @@ -1040,7 +1042,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_count(JSArrayProxy *self, PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_reverse(JSArrayProxy *self) { if (JSArrayProxy_length(self) > 1) { JS::RootedValue jReturnedArray(GLOBAL_CX); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "reverse", JS::HandleValueArray::empty(), &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "reverse", JS::HandleValueArray::empty(), &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -1225,7 +1227,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_sort(JSArrayProxy *self, P JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setObject(*funObj); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "sort", jArgs, &jReturnedArray)) { if (!PyErr_Occurred()) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); } @@ -1247,7 +1249,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_sort(JSArrayProxy *self, P // two-arg js-style JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].set(jsTypeFactory(GLOBAL_CX, keyfunc)); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "sort", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -1260,7 +1262,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_sort(JSArrayProxy *self, P else if (PyObject_TypeCheck(keyfunc, &JSFunctionProxyType)) { JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setObject(**((JSFunctionProxy *)keyfunc)->jsFunc); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "sort", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } @@ -1287,7 +1289,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_sort(JSArrayProxy *self, P JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setObject(*funObj); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "sort", jArgs, &jReturnedArray)) { if (!PyErr_Occurred()) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); } @@ -1313,7 +1315,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_sort(JSArrayProxy *self, P JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setObject(*funObj); - if (!JS_CallFunctionName(GLOBAL_CX, self->jsArray, "sort", jArgs, &jReturnedArray)) { + if (!JS_CallFunctionName(GLOBAL_CX, *(self->jsArray), "sort", jArgs, &jReturnedArray)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSArrayProxyType.tp_name); return NULL; } diff --git a/src/JSObjectItemsProxy.cc b/src/JSObjectItemsProxy.cc index fcc47b0c..0a500213 100644 --- a/src/JSObjectItemsProxy.cc +++ b/src/JSObjectItemsProxy.cc @@ -58,7 +58,7 @@ PyObject *JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_iter(JSObjectI iterator->it.di_dict = self->dv.dv_dict; iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(((JSObjectProxy *)(self->dv.dv_dict))->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); @@ -77,7 +77,7 @@ PyObject *JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_iter_reverse(J iterator->it.di_dict = self->dv.dv_dict; iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(((JSObjectProxy *)(self->dv.dv_dict))->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); diff --git a/src/JSObjectIterProxy.cc b/src/JSObjectIterProxy.cc index a8bc1c9c..4c900d4f 100644 --- a/src/JSObjectIterProxy.cc +++ b/src/JSObjectIterProxy.cc @@ -58,7 +58,7 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject if (self->it.kind != KIND_KEYS) { JS::RootedValue jsVal(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); + JS_GetPropertyById(GLOBAL_CX, *(((JSObjectProxy *)(self->it.di_dict))->jsObject), id, &jsVal); value = pyTypeFactory(GLOBAL_CX, jsVal)->getPyObject(); } @@ -84,7 +84,7 @@ PyObject *JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_nextkey(JSObject if (self->it.kind != KIND_KEYS) { JS::RootedValue jsVal(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, ((JSObjectProxy *)(self->it.di_dict))->jsObject, id, &jsVal); + JS_GetPropertyById(GLOBAL_CX, *(((JSObjectProxy *)(self->it.di_dict))->jsObject), id, &jsVal); value = pyTypeFactory(GLOBAL_CX, jsVal)->getPyObject(); } diff --git a/src/JSObjectKeysProxy.cc b/src/JSObjectKeysProxy.cc index cb2f6a51..5168d9ad 100644 --- a/src/JSObjectKeysProxy.cc +++ b/src/JSObjectKeysProxy.cc @@ -169,7 +169,7 @@ PyObject *JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_iter(JSObjectKey iterator->it.di_dict = self->dv.dv_dict; iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(((JSObjectProxy *)(self->dv.dv_dict))->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); @@ -188,7 +188,7 @@ PyObject *JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_iter_reverse(JSO iterator->it.di_dict = self->dv.dv_dict; iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(((JSObjectProxy *)(self->dv.dv_dict))->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 1cefdc84..384cfb4d 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -47,7 +47,8 @@ bool keyToId(PyObject *key, JS::MutableHandleId idp) { void JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc(JSObjectProxy *self) { - self->jsObject.set(nullptr); + self->jsObject->set(nullptr); + delete self->jsObject; PyObject_GC_UnTrack(self); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -55,7 +56,7 @@ void JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc(JSObjectProxy *self) Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *self) { JS::RootedIdVector props(GLOBAL_CX); - if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, &props)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return -1; @@ -69,7 +70,7 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId const char *methodName = JSObjectProxyType.tp_methods[index].ml_name; if (methodName == NULL || !PyUnicode_Check(key)) { JS::RootedValue value(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); + JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); // if value is a JSFunction, bind `this` to self /* (Caleb Aikens) its potentially problematic to bind it like this since if the function * ever gets assigned to another object like so: @@ -91,9 +92,12 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId JS::GetBuiltinClass(GLOBAL_CX, valueObject, &cls); if (cls == js::ESClass::Function) { JS::Rooted> args(GLOBAL_CX); - args[0].setObject(*(self->jsObject)); - JS::Rooted boundFunction(GLOBAL_CX); - JS_CallFunctionName(GLOBAL_CX, valueObject, "bind", args, &boundFunction); + args[0].setObject(*((*(self->jsObject)).get())); + JS::RootedValue boundFunction(GLOBAL_CX); + if (!JS_CallFunctionName(GLOBAL_CX, valueObject, "bind", args, &boundFunction)) { + PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); + return NULL; + } value.set(boundFunction); } } @@ -127,17 +131,17 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, return -1; } JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, value); + JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, value); return value->isUndefined() ? 0 : 1; } static inline void assignKeyValue(JSObjectProxy *self, PyObject *key, JS::HandleId id, PyObject *value) { if (value) { // we are setting a value JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value)); - JS_SetPropertyById(GLOBAL_CX, self->jsObject, id, jValue); + JS_SetPropertyById(GLOBAL_CX, *(self->jsObject), id, jValue); } else { // we are deleting a value JS::ObjectOpResult ignoredResult; - JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); + JS_DeletePropertyById(GLOBAL_CX, *(self->jsObject), id, ignoredResult); } } @@ -156,11 +160,12 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, Py int JSObjectProxyMethodDefinitions::JSObjectProxy_traverse(JSObjectProxy *self, visitproc visit, void *arg) { // Nothing to be done + // TODO do we need to iterate through the dict and call traverse on proxied PyObjects? return 0; } int JSObjectProxyMethodDefinitions::JSObjectProxy_clear(JSObjectProxy *self) { - // Nothing to be done + JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(self); return 0; } @@ -200,8 +205,8 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr visited.insert({{(PyObject *)self, other}}); if (Py_TYPE((PyObject *)self) == Py_TYPE(other)) { - JS::RootedValue selfVal(GLOBAL_CX, JS::ObjectValue(*self->jsObject)); - JS::RootedValue otherVal(GLOBAL_CX, JS::ObjectValue(*(*(JSObjectProxy *)other).jsObject)); + JS::RootedValue selfVal(GLOBAL_CX, JS::ObjectValue(**(self->jsObject))); + JS::RootedValue otherVal(GLOBAL_CX, JS::ObjectValue(**(*(JSObjectProxy *)other).jsObject)); if (selfVal.asRawBits() == otherVal.asRawBits()) { return true; } @@ -209,7 +214,7 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr } JS::RootedIdVector props(GLOBAL_CX); - if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, &props)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; @@ -262,7 +267,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter(JSObjectProxy *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)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); @@ -301,7 +306,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self /* Do repr() on each key+value pair, and insert ": " between them. Note that repr may mutate the dict. */ // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, &props)) { return NULL; } @@ -335,9 +340,9 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self } JS::RootedValue elementVal(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &elementVal); + JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &elementVal); - if (&elementVal.toObject() == self->jsObject.get()) { + if (&elementVal.toObject() == (*(self->jsObject)).get()) { value = (PyObject *)self; } else { value = pyTypeFactory(GLOBAL_CX, elementVal)->getPyObject(); @@ -464,11 +469,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, else { JS::Rooted> args(GLOBAL_CX); args[0].setObjectOrNull(JS_NewPlainObject(GLOBAL_CX)); - args[1].setObjectOrNull(self->jsObject); // this is null is left operand is real dict + args[1].setObjectOrNull(*(self->jsObject)); // this is null is left operand is real dict JS::RootedValue jValueOther(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, other)); args[2].setObject(jValueOther.toObject()); - JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(*(self->jsObject))); // call Object.assign JS::RootedValue Object(GLOBAL_CX); @@ -491,11 +496,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_or(JSObjectProxy *self, PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_ior(JSObjectProxy *self, PyObject *other) { if (PyDict_Check(other)) { JS::Rooted> args(GLOBAL_CX); - args[0].setObjectOrNull(self->jsObject); + args[0].setObjectOrNull(*(self->jsObject)); JS::RootedValue jValueOther(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, other)); args[1].setObject(jValueOther.toObject()); - JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(*(self->jsObject))); // call Object.assign JS::RootedValue Object(GLOBAL_CX); @@ -598,7 +603,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy } JS::RootedValue value(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, self->jsObject, id, &value); + JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); if (value.isUndefined()) { if (default_value != NULL) { Py_INCREF(default_value); @@ -609,7 +614,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy } else { JS::ObjectOpResult ignoredResult; - JS_DeletePropertyById(GLOBAL_CX, self->jsObject, id, ignoredResult); + JS_DeletePropertyById(GLOBAL_CX, *(self->jsObject), id, ignoredResult); return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } @@ -617,7 +622,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method(JSObjectProxy PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(JSObjectProxy *self) { JS::RootedIdVector props(GLOBAL_CX); - if (!js::GetPropertyKeys(GLOBAL_CX, self->jsObject, JSITER_OWNONLY, &props)) + if (!js::GetPropertyKeys(GLOBAL_CX, *(self->jsObject), JSITER_OWNONLY, &props)) { PyErr_Format(PyExc_SystemError, "%s JSAPI call failed", JSObjectProxyType.tp_name); return NULL; @@ -626,7 +631,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(JSObjectPro JS::ObjectOpResult ignoredResult; size_t length = props.length(); for (size_t index = 0; index < length; index++) { - JS_DeletePropertyById(GLOBAL_CX, self->jsObject, props[index], ignoredResult); + JS_DeletePropertyById(GLOBAL_CX, *(self->jsObject), props[index], ignoredResult); } Py_RETURN_NONE; @@ -635,9 +640,9 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(JSObjectPro PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method(JSObjectProxy *self) { JS::Rooted> args(GLOBAL_CX); args[0].setObjectOrNull(JS_NewPlainObject(GLOBAL_CX)); - args[1].setObjectOrNull(self->jsObject); + args[1].setObjectOrNull(*(self->jsObject)); - JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(self->jsObject)); + JS::RootedObject global(GLOBAL_CX, JS::GetNonCCWObjectGlobal(*(self->jsObject))); // call Object.assign JS::RootedValue Object(GLOBAL_CX); diff --git a/src/JSObjectValuesProxy.cc b/src/JSObjectValuesProxy.cc index 86ab3220..5196f403 100644 --- a/src/JSObjectValuesProxy.cc +++ b/src/JSObjectValuesProxy.cc @@ -97,7 +97,7 @@ PyObject *JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_iter(JSObjec iterator->it.di_dict = self->dv.dv_dict; iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(((JSObjectProxy *)(self->dv.dv_dict))->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); @@ -116,7 +116,7 @@ PyObject *JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_iter_reverse iterator->it.di_dict = self->dv.dv_dict; iterator->it.props = new JS::PersistentRootedIdVector(GLOBAL_CX); // Get **enumerable** own properties - if (!js::GetPropertyKeys(GLOBAL_CX, ((JSObjectProxy *)(self->dv.dv_dict))->jsObject, JSITER_OWNONLY, iterator->it.props)) { + if (!js::GetPropertyKeys(GLOBAL_CX, *(((JSObjectProxy *)(self->dv.dv_dict))->jsObject), JSITER_OWNONLY, iterator->it.props)) { return NULL; } PyObject_GC_Track(iterator); diff --git a/src/ListType.cc b/src/ListType.cc index f5f8df67..7cdea586 100644 --- a/src/ListType.cc +++ b/src/ListType.cc @@ -23,7 +23,8 @@ ListType::ListType(PyObject *object) : PyType(object) {} ListType::ListType(JSContext *cx, JS::HandleObject jsArrayObj) { JSArrayProxy *proxy = (JSArrayProxy *)PyObject_CallObject((PyObject *)&JSArrayProxyType, NULL); if (proxy != NULL) { - proxy->jsArray.set(jsArrayObj); + proxy->jsArray = new JS::PersistentRootedObject(cx); + proxy->jsArray->set(jsArrayObj); this->pyObject = (PyObject *)proxy; } } \ No newline at end of file diff --git a/src/PyListProxyHandler.cc b/src/PyListProxyHandler.cc index 2e69f111..2eb1ba27 100644 --- a/src/PyListProxyHandler.cc +++ b/src/PyListProxyHandler.cc @@ -552,7 +552,7 @@ static bool array_concat(JSContext *cx, unsigned argc, JS::Value *vp) { // flatten the array only a depth 1 Py_ssize_t itemLength = JSArrayProxyMethodDefinitions::JSArrayProxy_length((JSArrayProxy *)item); for (Py_ssize_t flatIndex = 0; flatIndex < itemLength; flatIndex++) { - if (!JS_GetElement(cx, ((JSArrayProxy *)item)->jsArray, flatIndex, &elementVal)) { + if (!JS_GetElement(cx, *(((JSArrayProxy *)item)->jsArray), flatIndex, &elementVal)) { return false; } if (PyList_Append(result, pyTypeFactory(cx, elementVal)->getPyObject()) < 0) { @@ -1238,7 +1238,7 @@ static uint32_t FlattenIntoArray(JSContext *cx, for (uint32_t sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { if (PyObject_TypeCheck(source, &JSArrayProxyType)) { - JS_GetElement(cx, ((JSArrayProxy *)source)->jsArray, sourceIndex, &elementVal); + JS_GetElement(cx, *(((JSArrayProxy *)source)->jsArray), sourceIndex, &elementVal); } else if (PyObject_TypeCheck(source, &PyList_Type)) { elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); @@ -1303,7 +1303,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, for (uint32_t sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) { if (PyObject_TypeCheck(source, &JSArrayProxyType)) { - JS_GetElement(cx, ((JSArrayProxy *)source)->jsArray, sourceIndex, &elementVal); + JS_GetElement(cx, *(((JSArrayProxy *)source)->jsArray), sourceIndex, &elementVal); } else if (PyObject_TypeCheck(source, &PyList_Type)) { elementVal.set(jsTypeFactory(cx, PyList_GetItem(source, sourceIndex))); @@ -1355,7 +1355,7 @@ static uint32_t FlattenIntoArrayWithCallBack(JSContext *cx, JS::RootedValue elementIndexVal(cx); for (uint32_t elementIndex = 0; elementIndex < elementLen; elementIndex++, targetIndex++) { if (PyObject_TypeCheck(element, &JSArrayProxyType)) { - JS_GetElement(cx, ((JSArrayProxy *)element)->jsArray, elementIndex, &elementIndexVal); + JS_GetElement(cx, *(((JSArrayProxy *)element)->jsArray), elementIndex, &elementIndexVal); } else { elementIndexVal.set(jsTypeFactory(cx, PyList_GetItem(element, elementIndex))); diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 97322297..a4621708 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -55,7 +55,13 @@ std::unordered_map charToPyObjectMap; // a map of char16 struct PythonExternalString : public JSExternalStringCallbacks { void finalize(char16_t *chars) const override { - Py_DECREF(charToPyObjectMap[chars]); + // We cannot call Py_DECREF here when shutting down as the thread state is gone. + // Then, when shutting down, there is only on reference left, and we don't need + // to free the object since the entire process memory is being released. + PyObject *object = charToPyObjectMap[chars]; + if (Py_REFCNT(object) > 1) { + Py_DECREF(object); + } } size_t sizeOfBuffer(const char16_t *chars, mozilla::MallocSizeOf mallocSizeOf) const override { return 0; @@ -180,7 +186,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { returnType.setObjectOrNull(typedArray); } else if (PyObject_TypeCheck(object, &JSObjectProxyType)) { - returnType.setObject(*((JSObjectProxy *)object)->jsObject); + returnType.setObject(**((JSObjectProxy *)object)->jsObject); } else if (PyObject_TypeCheck(object, &JSMethodProxyType)) { JS::RootedObject func(cx, *((JSMethodProxy *)object)->jsFunc); @@ -204,7 +210,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { returnType.setObject(**((JSFunctionProxy *)object)->jsFunc); } else if (PyObject_TypeCheck(object, &JSArrayProxyType)) { - returnType.setObject(*((JSArrayProxy *)object)->jsArray); + returnType.setObject(**((JSArrayProxy *)object)->jsArray); } else if (PyDict_Check(object) || PyList_Check(object)) { JS::RootedValue v(cx); From a0fea0c8a8e20eacdd581e62d1f633db5940948e Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 11:21:45 -0500 Subject: [PATCH 107/170] chore(jsTypeFactory): remove extra blank lines from jsTypeFactory --- src/jsTypeFactory.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 8e322f28..a72a73a7 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -50,8 +50,6 @@ static PyDictProxyHandler pyDictProxyHandler; static PyObjectProxyHandler pyObjectProxyHandler; static PyListProxyHandler pyListProxyHandler; - - std::unordered_map charToPyObjectMap; // a map of char16_t buffers to their corresponding PyObjects, used when finalizing JSExternalStrings struct PythonExternalString : public JSExternalStringCallbacks { From ccbd1c558e15b54dea1dde651ca39c3b417cf8cb Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 11:39:18 -0500 Subject: [PATCH 108/170] chore(FinalizationRegistry): change jsFunctionRegistry from a pointer to an object, and initialize it in PyInit_pythonmonkey --- include/PyDictProxyHandler.hh | 4 ++-- include/PyObjectProxyHandler.hh | 6 +++--- include/modules/pythonmonkey/pythonmonkey.hh | 2 +- src/jsTypeFactory.cc | 6 ++++-- src/modules/pythonmonkey/pythonmonkey.cc | 7 +++---- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index 35351ee5..0e3128d1 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -79,7 +79,7 @@ public: * @param proxy - The proxy object who's keys we output * @param props - out-parameter of object IDsoverride; - /** + /** * @return true - call succeeded * @return false - call failed and an exception has been raised */ @@ -101,7 +101,7 @@ public: /** * @brief Returns vector of proxy's own keys - * + * * @param cx - Pointer to the JSContext * @param proxy - the proxy object * @param props - out parameter, the vector of proxy's own keys diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index 460caded..acefa531 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -101,10 +101,10 @@ public: */ bool hasOwn(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp) const override; - - /** + + /** * @brief Returns vector of proxy's own keys - * + * * @param cx - Pointer to the JSContext * @param proxy - the proxy object * @param props - out parameter, the vector of proxy's own keys diff --git a/include/modules/pythonmonkey/pythonmonkey.hh b/include/modules/pythonmonkey/pythonmonkey.hh index 7f964bce..3b3a51e3 100644 --- a/include/modules/pythonmonkey/pythonmonkey.hh +++ b/include/modules/pythonmonkey/pythonmonkey.hh @@ -24,7 +24,7 @@ #define PythonMonkey_BigInt PyObject_GetAttrString(PyState_FindModule(&pythonmonkey), "bigint") /**< macro for pythonmonkey.bigint class object */ extern JSContext *GLOBAL_CX; /**< pointer to PythonMonkey's JSContext */ -extern JS::PersistentRootedObject *jsFunctionRegistry; /** *global; /**< pointer to the global object of PythonMonkey's JSContext */ static JSAutoRealm *autoRealm; /**< pointer to PythonMonkey's AutoRealm */ static JobQueue *JOB_QUEUE; /**< pointer to PythonMonkey's event-loop job queue */ diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index a72a73a7..a5b89cfc 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -164,7 +164,8 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { registerArgs[0].setObject(*jsFuncObject); registerArgs[1].setPrivate(object); JS::RootedValue ignoredOutVal(GLOBAL_CX); - JS_CallFunctionName(GLOBAL_CX, *jsFunctionRegistry, "register", registerArgs, &ignoredOutVal); + JS::RootedObject registry(GLOBAL_CX, jsFunctionRegistry); + JS_CallFunctionName(GLOBAL_CX, registry, "register", registerArgs, &ignoredOutVal); } else if (PyExceptionInstance_Check(object)) { JSObject *error = ExceptionType(object).toJsError(cx); @@ -196,7 +197,8 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { registerArgs[0].set(boundFunction); registerArgs[1].setPrivate(object); JS::RootedValue ignoredOutVal(GLOBAL_CX); - JS_CallFunctionName(GLOBAL_CX, *jsFunctionRegistry, "register", registerArgs, &ignoredOutVal); + JS::RootedObject registry(GLOBAL_CX, jsFunctionRegistry); + JS_CallFunctionName(GLOBAL_CX, registry, "register", registerArgs, &ignoredOutVal); Py_INCREF(object); } diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index e4a0ba28..e99c2e0a 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -48,7 +48,7 @@ #include #include -JS::PersistentRootedObject *jsFunctionRegistry; +JS::PersistentRootedObject jsFunctionRegistry; bool functionRegistryCallback(JSContext *cx, unsigned int argc, JS::Value *vp) { JS::CallArgs callargs = JS::CallArgsFromVp(argc, vp); @@ -255,7 +255,6 @@ PyTypeObject JSObjectItemsProxyType = { }; static void cleanup() { - delete jsFunctionRegistry; delete autoRealm; delete global; delete JOB_QUEUE; @@ -647,8 +646,8 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) setSpiderMonkeyException(GLOBAL_CX); return NULL; } - jsFunctionRegistry = new JS::PersistentRootedObject(GLOBAL_CX); - jsFunctionRegistry->set(registryObject); + jsFunctionRegistry.init(GLOBAL_CX); + jsFunctionRegistry.set(registryObject); JS::SetHostCleanupFinalizationRegistryCallback(GLOBAL_CX, cleanupFinalizationRegistry, NULL); From b4d33b435747a371260d7341af294d0c8b129ed6 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 29 Feb 2024 11:43:16 -0500 Subject: [PATCH 109/170] feat(jsTypeFactory): correctly check the result of JS_CallFunctionName to see if an exception should be raised --- src/jsTypeFactory.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index a5b89cfc..65b4ebac 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -29,6 +29,7 @@ #include "include/DateType.hh" #include "include/ExceptionType.hh" #include "include/BufferType.hh" +#include "include/setSpiderMonkeyException.hh" #include #include @@ -165,7 +166,10 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { registerArgs[1].setPrivate(object); JS::RootedValue ignoredOutVal(GLOBAL_CX); JS::RootedObject registry(GLOBAL_CX, jsFunctionRegistry); - JS_CallFunctionName(GLOBAL_CX, registry, "register", registerArgs, &ignoredOutVal); + if (!JS_CallFunctionName(GLOBAL_CX, registry, "register", registerArgs, &ignoredOutVal)) { + setSpiderMonkeyException(GLOBAL_CX); + return returnType; + } } else if (PyExceptionInstance_Check(object)) { JSObject *error = ExceptionType(object).toJsError(cx); @@ -190,7 +194,10 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { JS::Rooted> args(cx); args[0].set(jsTypeFactory(cx, self)); JS::Rooted boundFunction(cx); - JS_CallFunctionName(cx, func, "bind", args, &boundFunction); + if (!JS_CallFunctionName(cx, func, "bind", args, &boundFunction)) { + setSpiderMonkeyException(GLOBAL_CX); + return returnType; + } returnType.set(boundFunction); // add function to jsFunctionRegistry, to DECREF the PyObject when the JSFunction is finalized JS::RootedValueArray<2> registerArgs(GLOBAL_CX); @@ -198,7 +205,10 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { registerArgs[1].setPrivate(object); JS::RootedValue ignoredOutVal(GLOBAL_CX); JS::RootedObject registry(GLOBAL_CX, jsFunctionRegistry); - JS_CallFunctionName(GLOBAL_CX, registry, "register", registerArgs, &ignoredOutVal); + if (!JS_CallFunctionName(GLOBAL_CX, registry, "register", registerArgs, &ignoredOutVal)) { + setSpiderMonkeyException(GLOBAL_CX); + return returnType; + } Py_INCREF(object); } From 30c120d17b847d313ef726fc3ffb72dd8355a1af Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 29 Feb 2024 12:31:05 -0500 Subject: [PATCH 110/170] tp_name must be same as that of super type --- src/JSArrayProxy.cc | 5 +++++ src/JSObjectProxy.cc | 4 ++++ src/modules/pythonmonkey/pythonmonkey.cc | 4 ++-- tests/python/test_dicts.py | 7 ++++++- tests/python/test_lists.py | 7 ++++++- 5 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index d552f4be..a3caa54a 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -54,6 +54,11 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py if (methodName == NULL || !PyUnicode_Check(key)) { // reached end of list JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, *(self->jsArray), id, &value); + if (value.isUndefined()) { + if (strcmp("__class__", PyUnicode_AsUTF8(key)) == 0) { + return PyObject_GenericGetAttr((PyObject *)self, key); + } + } return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); } else { diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 384cfb4d..5a788aeb 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -100,6 +100,10 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId } value.set(boundFunction); } + } else if (value.isUndefined()) { + if (strcmp("__class__", PyUnicode_AsUTF8(key)) == 0) { + return PyObject_GenericGetAttr((PyObject *)self, key); + } } return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index ee1458d0..81d821f0 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -88,7 +88,7 @@ static PyTypeObject BigIntType = { PyTypeObject JSObjectProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectProxy", + .tp_name = PyDict_Type.tp_name, .tp_basicsize = sizeof(JSObjectProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc, @@ -143,7 +143,7 @@ PyTypeObject JSMethodProxyType = { PyTypeObject JSArrayProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSArrayProxy", + .tp_name = PyList_Type.tp_name, .tp_basicsize = sizeof(JSArrayProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSArrayProxyMethodDefinitions::JSArrayProxy_dealloc, diff --git a/tests/python/test_dicts.py b/tests/python/test_dicts.py index df7efbb2..b09849cb 100644 --- a/tests/python/test_dicts.py +++ b/tests/python/test_dicts.py @@ -325,4 +325,9 @@ def test_repr_max_recursion_depth(): except Exception as e: assert str(type(e)) == "" assert str(e) == "maximum recursion depth exceeded while getting the repr of an object" - subprocess.check_call('npm uninstall crypto-js', shell=True) \ No newline at end of file + subprocess.check_call('npm uninstall crypto-js', shell=True) + +#__class__ +def test___class__attribute(): + items = pm.eval("({'a': 10})") + assert repr(items.__class__) == "" \ No newline at end of file diff --git a/tests/python/test_lists.py b/tests/python/test_lists.py index 6cf817b7..42671dc6 100644 --- a/tests/python/test_lists.py +++ b/tests/python/test_lists.py @@ -1050,4 +1050,9 @@ def test_slice_assign_pm_array_step_2(): a = pm.eval("([1,2,3,4,5,6])") b = pm.eval("([1,2,3])") a[0:10:2] = b - assert a == [1, 2, 2, 4, 3, 6] \ No newline at end of file + assert a == [1, 2, 2, 4, 3, 6] + +#__class__ +def test___class__attribute(): + items = pm.eval("([1,2,3,4,5,6])") + assert repr(items.__class__) == "" \ No newline at end of file From f8a12175b3b34e0259f4a579e16c9870b624f19f Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 29 Feb 2024 14:40:28 -0500 Subject: [PATCH 111/170] finished details of JSObjectProxy.repr: skip over the $super key to avoid infinite recur --- src/JSObjectProxy.cc | 5 +++++ tests/python/test_dicts.py | 9 ++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 5a788aeb..75b9d817 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -324,6 +324,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_repr(JSObjectProxy *self JS::HandleId id = props[index]; key = idToKey(GLOBAL_CX, id); + // escape infinite recur on superclass reference + if (strcmp(PyUnicode_AsUTF8(key), "$super") == 0) { + continue; + } + // Prevent repr from deleting key or value during key format. Py_INCREF(key); diff --git a/tests/python/test_dicts.py b/tests/python/test_dicts.py index b09849cb..65112e4b 100644 --- a/tests/python/test_dicts.py +++ b/tests/python/test_dicts.py @@ -319,13 +319,8 @@ def test_toLocaleString(): def test_repr_max_recursion_depth(): subprocess.check_call('npm install crypto-js', shell=True) CryptoJS = pm.require('crypto-js') - try: - repr(CryptoJS) - assert (False) - except Exception as e: - assert str(type(e)) == "" - assert str(e) == "maximum recursion depth exceeded while getting the repr of an object" - subprocess.check_call('npm uninstall crypto-js', shell=True) + assert str(CryptoJS).__contains__("{'lib': {'Base': {'extend':") + #__class__ def test___class__attribute(): From 97339020de7c784e2896223bd70337e54ba00d4a Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 29 Feb 2024 15:34:21 -0500 Subject: [PATCH 112/170] next version will be 0.4.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3013dfb5..a8c8cca2 100644 --- a/README.md +++ b/README.md @@ -396,7 +396,7 @@ List of commands: ```console $ pmjs -Welcome to PythonMonkey v0.3.1. +Welcome to PythonMonkey v0.4.0. Type ".help" for more information. > .python import sys > .python sys.path From c4dc2bd4896daab07b9425f74a7922cb26e36985 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 1 Mar 2024 16:20:32 -0500 Subject: [PATCH 113/170] hash not implemented entry not required --- src/modules/pythonmonkey/pythonmonkey.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 165246e1..239a6a4b 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -96,7 +96,6 @@ PyTypeObject JSObjectProxyType = { .tp_as_number = &JSObjectProxy_number_methods, .tp_as_sequence = &JSObjectProxy_sequence_methods, .tp_as_mapping = &JSObjectProxy_mapping_methods, - .tp_hash = PyObject_HashNotImplemented, .tp_getattro = (getattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get, .tp_setattro = (setattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DICT_SUBCLASS | Py_TPFLAGS_HAVE_GC, From 40bdbb35e6b2a723f79bb6310391f3f6bf2d5857 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Fri, 1 Mar 2024 16:20:39 -0500 Subject: [PATCH 114/170] test fix --- tests/python/test_list_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/test_list_array.py b/tests/python/test_list_array.py index 476c3adf..20f80c87 100644 --- a/tests/python/test_list_array.py +++ b/tests/python/test_list_array.py @@ -7,7 +7,7 @@ def test_eval_array_is_list(): # extra nice but not necessary def test_eval_array_is_list_type_string(): pythonListTypeString = str(type(pm.eval('[]'))) - assert pythonListTypeString == "" + assert pythonListTypeString == "" def test_eval_list_is_array(): items = [1, 2, 3] From 4097f3641ab772af88245e44016176a29e6243a7 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Mon, 4 Mar 2024 16:55:19 -0500 Subject: [PATCH 115/170] chore(PyObjectProxyHandler): don't use python C API macros that are undefined in python 3.8 & 3.9 --- src/PyObjectProxyHandler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 005090d0..0641d6c1 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -34,7 +34,7 @@ bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy PyObject *nonDunderKeys = PyList_New(0); for (size_t i = 0; i < keysLength; i++) { PyObject *key = PyList_GetItem(keys, i); - if (Py_IsFalse(PyObject_CallMethod(key, "startswith", "(s)", "__"))) { // if key starts with "__", ignore it + if (PyObject_CallMethod(key, "startswith", "(s)", "__") == Py_False) { // if key starts with "__", ignore it PyList_Append(nonDunderKeys, key); } } From 7e7ed3357b66d7c441fc58cfe172899eefa26d49 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 7 Mar 2024 18:32:24 -0500 Subject: [PATCH 116/170] improved reference management --- src/jsTypeFactory.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index 8523b0c8..f9a02f97 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -286,6 +286,9 @@ void setPyException(JSContext *cx) { PyErr_Fetch(&type, &value, &traceback); // also clears the error indicator JSObject *jsException = ExceptionType(value).toJsError(cx); + Py_DECREF(type); + Py_DECREF(value); + Py_DECREF(traceback); JS::RootedValue jsExceptionValue(cx, JS::ObjectValue(*jsException)); JS_SetPendingException(cx, jsExceptionValue); } @@ -331,9 +334,11 @@ bool callPyFunc(JSContext *cx, unsigned int argc, JS::Value *vp) { } callargs.rval().set(jsTypeFactory(cx, pyRval)); if (PyErr_Occurred()) { + Py_DECREF(pyRval); setPyException(cx); return false; } + Py_DECREF(pyRval); return true; } \ No newline at end of file From baab95fa90c52e93413d2ff60ce884f14571acc4 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 7 Mar 2024 18:38:12 -0500 Subject: [PATCH 117/170] dec ref needs to check for null --- src/jsTypeFactory.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index f9a02f97..bca4e38c 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -286,9 +286,9 @@ void setPyException(JSContext *cx) { PyErr_Fetch(&type, &value, &traceback); // also clears the error indicator JSObject *jsException = ExceptionType(value).toJsError(cx); - Py_DECREF(type); - Py_DECREF(value); - Py_DECREF(traceback); + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); JS::RootedValue jsExceptionValue(cx, JS::ObjectValue(*jsException)); JS_SetPendingException(cx, jsExceptionValue); } From f8aafd88832c34042303ac6d77262b01a5fa71ed Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Mon, 11 Mar 2024 12:15:29 -0400 Subject: [PATCH 118/170] feat(FinalizationRegistry): implement job queue for FinalizationRegistry callbacks --- include/JobQueue.hh | 20 ++++++++++++++++ src/JobQueue.cc | 28 +++++++++++++++++++++++ src/modules/pythonmonkey/pythonmonkey.cc | 18 +++++++++------ tests/python/test_finalizationregistry.py | 24 +++++++++++++++++++ tests/python/test_functions_this.py | 9 +++----- 5 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 tests/python/test_finalizationregistry.py diff --git a/include/JobQueue.hh b/include/JobQueue.hh index 5d10e9a7..be5d6617 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -27,6 +27,7 @@ class JobQueue : public JS::JobQueue { // JS::JobQueue methods. // public: +explicit JobQueue(JSContext *cx) : finalizationRegistryCallbacks(cx) {} ~JobQueue() = default; /** @@ -73,7 +74,26 @@ void runJobs(JSContext *cx) override; */ bool empty() const override; +/** + * @brief Appends a callback to the queue of FinalizationRegistry callbacks + * + * @param callback - the callback to be queue'd + */ +void queueFinalizationRegistryCallback(JSFunction *callback); + +/** + * @brief Runs the accumulated queue of FinalizationRegistry callbacks + * + * @param cx - Pointer to the JSContext + * @return true - at least 1 callback was called + * @return false - no callbacks were called + */ +bool runFinalizationRegistryCallbacks(JSContext *cx); + private: +using FunctionVector = JS::GCVector; +JS::PersistentRooted finalizationRegistryCallbacks; + /** * @brief Capture this JobQueue's current job queue as a SavedJobQueue and return it, * leaving the JobQueue's job queue empty. Destroying the returned object diff --git a/src/JobQueue.cc b/src/JobQueue.cc index ee7a013f..f05a2abe 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -5,7 +5,9 @@ #include "include/pyTypeFactory.hh" #include + #include +#include #include @@ -50,6 +52,10 @@ bool JobQueue::empty() const { js::UniquePtr JobQueue::saveJobQueue(JSContext *cx) { auto saved = js::MakeUnique(); + if (!saved) { + JS_ReportOutOfMemory(cx); + return NULL; + } return saved; } @@ -100,3 +106,25 @@ bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable PyGILState_Release(gstate); return true; // dispatchable must eventually run } + +void JobQueue::queueFinalizationRegistryCallback(JSFunction *callback) { + mozilla::Unused << finalizationRegistryCallbacks.append(callback); +} + +bool JobQueue::runFinalizationRegistryCallbacks(JSContext *cx) { + bool ranCallbacks = false; + JS::Rooted callbacks(cx); + std::swap(callbacks.get(), finalizationRegistryCallbacks.get()); + for (JSFunction *f: callbacks) { + JS::ExposeObjectToActiveJS(JS_GetFunctionObject(f)); + + JSAutoRealm ar(cx, JS_GetFunctionObject(f)); + JS::RootedFunction func(cx, f); + JS::RootedValue unused_rval(cx); + // we don't raise an exception here because there is nowhere to catch it + mozilla::Unused << JS_CallFunction(cx, NULL, func, JS::HandleValueArray::empty(), &unused_rval); + ranCallbacks = true; + } + + return ranCallbacks; +} \ No newline at end of file diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 8016f555..29f2f8e7 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -50,6 +50,13 @@ JS::PersistentRootedObject jsFunctionRegistry; +void finalizationRegistryGCCallback(JSContext *cx, JSGCStatus status, JS::GCReason reason, void *data) { + if (status == JSGCStatus::JSGC_END) { + JS::ClearKeptObjects(GLOBAL_CX); + while (JOB_QUEUE->runFinalizationRegistryCallbacks(GLOBAL_CX)); + } +} + bool functionRegistryCallback(JSContext *cx, unsigned int argc, JS::Value *vp) { JS::CallArgs callargs = JS::CallArgsFromVp(argc, vp); Py_DECREF((PyObject *)callargs[0].toPrivate()); @@ -57,12 +64,7 @@ bool functionRegistryCallback(JSContext *cx, unsigned int argc, JS::Value *vp) { } static void cleanupFinalizationRegistry(JSFunction *callback, JSObject *global [[maybe_unused]], void *user_data [[maybe_unused]]) { - JS::ExposeObjectToActiveJS(JS_GetFunctionObject(callback)); - JS::RootedFunction rootedCallback(GLOBAL_CX, callback); - JS::RootedValue unused(GLOBAL_CX); - if (!JS_CallFunction(GLOBAL_CX, NULL, rootedCallback, JS::HandleValueArray::empty(), &unused)) { - setSpiderMonkeyException(GLOBAL_CX); - } + JOB_QUEUE->queueFinalizationRegistryCallback(callback); } typedef struct { @@ -477,7 +479,7 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) .setAsyncStack(true) .setSourcePragmas(true); - JOB_QUEUE = new JobQueue(); + JOB_QUEUE = new JobQueue(GLOBAL_CX); if (!JOB_QUEUE->init(GLOBAL_CX)) { PyErr_SetString(SpiderMonkeyError, "Spidermonkey could not create the event-loop."); return NULL; @@ -488,6 +490,8 @@ PyMODINIT_FUNC PyInit_pythonmonkey(void) return NULL; } + JS_SetGCCallback(GLOBAL_CX, finalizationRegistryGCCallback, NULL); + JS::RealmCreationOptions creationOptions = JS::RealmCreationOptions(); JS::RealmBehaviors behaviours = JS::RealmBehaviors(); creationOptions.setWeakRefsEnabled(JS::WeakRefSpecifier::EnabledWithoutCleanupSome); // enable FinalizationRegistry diff --git a/tests/python/test_finalizationregistry.py b/tests/python/test_finalizationregistry.py new file mode 100644 index 00000000..df0f2fbf --- /dev/null +++ b/tests/python/test_finalizationregistry.py @@ -0,0 +1,24 @@ +import pythonmonkey as pm + +def test_finalizationregistry(): + result = pm.eval(""" + (collect) => { + let arr = [42, 43]; + const registry = new FinalizationRegistry(heldValue => { arr[heldValue] = heldValue; }); + let obj1 = {}; + let obj2 = {}; + registry.register(obj1, 0); + registry.register(obj2, 1); + obj1 = null; + obj2 = null; + + collect(); + + return arr; + } + """)(pm.collect) + + assert result[0] == 0 + assert result[1] == 1 + + diff --git a/tests/python/test_functions_this.py b/tests/python/test_functions_this.py index 4ec6f13c..8f04ba06 100644 --- a/tests/python/test_functions_this.py +++ b/tests/python/test_functions_this.py @@ -189,11 +189,8 @@ def pyFunc(): assert ref[0]() is pyFunc current_ref_count = sys.getrefcount(pyFunc) assert current_ref_count == starting_ref_count[0] + 1 - + outerScope() pm.collect() # this should collect the JS proxy to pyFunc, which should decref pyFunc - current_ref_count = sys.getrefcount(ref[0]()) - #pytest seems to hold an additional reference on inner functions, so we assert here that the refcount - #is what it was when pyFunc was defined. In a non-test environment, pyFunc should be collected and ref[0]() should be None - #at this point - assert current_ref_count == starting_ref_count[0] \ No newline at end of file + #pyFunc should be collected by now + assert ref[0]() is None \ No newline at end of file From 5ed221b0e118cab0aabbf66455124914b9faa0f1 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Mon, 11 Mar 2024 12:55:46 -0400 Subject: [PATCH 119/170] fix(pythonmonkey): delete the JobQueue & JSContext in the correct order --- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 29f2f8e7..d4c85cee 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -258,9 +258,9 @@ PyTypeObject JSObjectItemsProxyType = { static void cleanup() { delete autoRealm; + if (GLOBAL_CX) JS_DestroyContext(GLOBAL_CX); delete global; delete JOB_QUEUE; - if (GLOBAL_CX) JS_DestroyContext(GLOBAL_CX); JS_ShutDown(); } From 9764d1f66f84cd6c5eaed728fcfa9c0109496cfb Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Mon, 11 Mar 2024 14:46:25 -0400 Subject: [PATCH 120/170] post-merge patch-up --- src/JobQueue.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/JobQueue.cc b/src/JobQueue.cc index db9a1e06..80cdded7 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -97,7 +97,6 @@ bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable return true; } -<<<<<<< HEAD bool sendJobToMainLoop(PyObject *pyFunc) { PyGILState_STATE gstate = PyGILState_Ensure(); @@ -111,7 +110,8 @@ bool sendJobToMainLoop(PyObject *pyFunc) { PyGILState_Release(gstate); return true; -======= +} + void JobQueue::queueFinalizationRegistryCallback(JSFunction *callback) { mozilla::Unused << finalizationRegistryCallbacks.append(callback); } @@ -132,5 +132,4 @@ bool JobQueue::runFinalizationRegistryCallbacks(JSContext *cx) { } return ranCallbacks; ->>>>>>> caleb/fix/this } \ No newline at end of file From dec4f724bdc8d74658d5796939f868c31ee1f681 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Mon, 11 Mar 2024 15:11:40 -0400 Subject: [PATCH 121/170] Proxies now use parent type name for enhanced type compatibility --- src/modules/pythonmonkey/pythonmonkey.cc | 14 +++++++------- tests/python/test_event_loop.py | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index a4750f39..cb1765c6 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -80,7 +80,7 @@ static PyTypeObject NullType = { }; static PyTypeObject BigIntType = { - .tp_name = "pythonmonkey.bigint", + .tp_name = PyLong_Type.tp_name, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LONG_SUBCLASS | Py_TPFLAGS_BASETYPE, // can be subclassed @@ -111,7 +111,7 @@ PyTypeObject JSObjectProxyType = { }; PyTypeObject JSStringProxyType = { - .tp_name = "pythonmonkey.JSStringProxy", + .tp_name = PyUnicode_Type.tp_name, .tp_basicsize = sizeof(JSStringProxy), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_UNICODE_SUBCLASS @@ -164,7 +164,7 @@ PyTypeObject JSArrayProxyType = { PyTypeObject JSArrayIterProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSArrayIterProxy", + .tp_name = PyListIter_Type.tp_name, .tp_basicsize = sizeof(JSArrayIterProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_dealloc, @@ -180,7 +180,7 @@ PyTypeObject JSArrayIterProxyType = { PyTypeObject JSObjectIterProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectIterProxy", + .tp_name = PyDictIterKey_Type.tp_name, .tp_basicsize = sizeof(JSObjectIterProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_dealloc, @@ -196,7 +196,7 @@ PyTypeObject JSObjectIterProxyType = { PyTypeObject JSObjectKeysProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectKeysProxy", + .tp_name = PyDictKeys_Type.tp_name, .tp_basicsize = sizeof(JSObjectKeysProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_dealloc, @@ -216,7 +216,7 @@ PyTypeObject JSObjectKeysProxyType = { PyTypeObject JSObjectValuesProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectValuesProxy", + .tp_name = PyDictValues_Type.tp_name, .tp_basicsize = sizeof(JSObjectValuesProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_dealloc, @@ -234,7 +234,7 @@ PyTypeObject JSObjectValuesProxyType = { PyTypeObject JSObjectItemsProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectItemsProxy", + .tp_name = PyDictKeys_Type.tp_name, .tp_basicsize = sizeof(JSObjectItemsProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_dealloc, diff --git a/tests/python/test_event_loop.py b/tests/python/test_event_loop.py index d8bc2e65..26b6e989 100644 --- a/tests/python/test_event_loop.py +++ b/tests/python/test_event_loop.py @@ -175,7 +175,7 @@ async def c(): assert "nested" == await pm.eval("(promise) => promise")(c()) assert "nested" == await pm.eval("(promise) => promise")(await c()) assert "nested" == await pm.eval("(promise) => promise")(await (await c())) - with pytest.raises(TypeError, match="object pythonmonkey.JSStringProxy can't be used in 'await' expression"): + with pytest.raises(TypeError, match="object str can't be used in 'await' expression"): await pm.eval("(promise) => promise")(await (await (await c()))) # Python awaitable throwing exceptions From c2d102d1ddf905fca6106b2f2224e1fd867d173f Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Wed, 13 Mar 2024 11:12:44 -0400 Subject: [PATCH 122/170] cleanup and post-merge adjust --- src/modules/pythonmonkey/pythonmonkey.cc | 52 +++++++++++------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 80ae27d1..3a81b6e5 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -78,21 +78,21 @@ static PyTypeObject NullType = { .tp_name = "pythonmonkey.null", .tp_basicsize = sizeof(NullObject), .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = PyDoc_STR("Javascript null object"), + .tp_doc = PyDoc_STR("Javascript null object") }; static PyTypeObject BigIntType = { - .tp_name = "pythonmonkey.bigint", + .tp_name = PyLong_Type.tp_name, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LONG_SUBCLASS | Py_TPFLAGS_BASETYPE, // can be subclassed .tp_doc = PyDoc_STR("Javascript BigInt object"), - .tp_base = &PyLong_Type, // extending the builtin int type + .tp_base = &PyLong_Type }; PyTypeObject JSObjectProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectProxy", + .tp_name = PyDict_Type.tp_name, .tp_basicsize = sizeof(JSObjectProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc, @@ -100,28 +100,26 @@ PyTypeObject JSObjectProxyType = { .tp_as_number = &JSObjectProxy_number_methods, .tp_as_sequence = &JSObjectProxy_sequence_methods, .tp_as_mapping = &JSObjectProxy_mapping_methods, - .tp_hash = PyObject_HashNotImplemented, .tp_getattro = (getattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get, .tp_setattro = (setattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign, - .tp_flags = Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_DICT_SUBCLASS, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DICT_SUBCLASS | Py_TPFLAGS_HAVE_GC, .tp_doc = PyDoc_STR("Javascript Object proxy dict"), + .tp_traverse = (traverseproc)JSObjectProxyMethodDefinitions::JSObjectProxy_traverse, + .tp_clear = (inquiry)JSObjectProxyMethodDefinitions::JSObjectProxy_clear, .tp_richcompare = (richcmpfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare, .tp_iter = (getiterfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_iter, .tp_methods = JSObjectProxy_methods, - .tp_base = &PyDict_Type, - .tp_init = (initproc)JSObjectProxyMethodDefinitions::JSObjectProxy_init, - .tp_new = JSObjectProxyMethodDefinitions::JSObjectProxy_new, + .tp_base = &PyDict_Type }; PyTypeObject JSStringProxyType = { - .tp_name = "pythonmonkey.JSStringProxy", + .tp_name = PyUnicode_Type.tp_name, .tp_basicsize = sizeof(JSStringProxy), .tp_flags = Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_UNICODE_SUBCLASS // https://docs.python.org/3/c-api/typeobj.html#Py_TPFLAGS_LONG_SUBCLASS + | Py_TPFLAGS_UNICODE_SUBCLASS | Py_TPFLAGS_BASETYPE, // can be subclassed .tp_doc = PyDoc_STR("Javascript String value"), - .tp_base = &PyUnicode_Type, // extending the builtin int type + .tp_base = &PyUnicode_Type }; PyTypeObject JSFunctionProxyType = { @@ -132,7 +130,7 @@ PyTypeObject JSFunctionProxyType = { .tp_call = JSFunctionProxyMethodDefinitions::JSFunctionProxy_call, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Javascript Function proxy object"), - .tp_new = JSFunctionProxyMethodDefinitions::JSFunctionProxy_new, + .tp_new = JSFunctionProxyMethodDefinitions::JSFunctionProxy_new }; PyTypeObject JSMethodProxyType = { @@ -143,12 +141,12 @@ PyTypeObject JSMethodProxyType = { .tp_call = JSMethodProxyMethodDefinitions::JSMethodProxy_call, .tp_flags = Py_TPFLAGS_DEFAULT, .tp_doc = PyDoc_STR("Javascript Method proxy object"), - .tp_new = JSMethodProxyMethodDefinitions::JSMethodProxy_new, + .tp_new = JSMethodProxyMethodDefinitions::JSMethodProxy_new }; PyTypeObject JSArrayProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSArrayProxy", + .tp_name = PyList_Type.tp_name, .tp_basicsize = sizeof(JSArrayProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSArrayProxyMethodDefinitions::JSArrayProxy_dealloc, @@ -156,21 +154,19 @@ PyTypeObject JSArrayProxyType = { .tp_as_sequence = &JSArrayProxy_sequence_methods, .tp_as_mapping = &JSArrayProxy_mapping_methods, .tp_getattro = (getattrofunc)JSArrayProxyMethodDefinitions::JSArrayProxy_get, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS | Py_TPFLAGS_HAVE_GC, .tp_doc = PyDoc_STR("Javascript Array proxy list"), .tp_traverse = (traverseproc)JSArrayProxyMethodDefinitions::JSArrayProxy_traverse, - .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear_slot, + .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear, .tp_richcompare = (richcmpfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare, .tp_iter = (getiterfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_iter, .tp_methods = JSArrayProxy_methods, - .tp_base = &PyList_Type, - .tp_init = (initproc)JSArrayProxyMethodDefinitions::JSArrayProxy_init, - .tp_new = JSArrayProxyMethodDefinitions::JSArrayProxy_new, + .tp_base = &PyList_Type }; PyTypeObject JSArrayIterProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSArrayIterProxy", + .tp_name = PyListIter_Type.tp_name, .tp_basicsize = sizeof(JSArrayIterProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSArrayIterProxyMethodDefinitions::JSArrayIterProxy_dealloc, @@ -186,7 +182,7 @@ PyTypeObject JSArrayIterProxyType = { PyTypeObject JSObjectIterProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectIterProxy", + .tp_name = PyDictIterKey_Type.tp_name, .tp_basicsize = sizeof(JSObjectIterProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectIterProxyMethodDefinitions::JSObjectIterProxy_dealloc, @@ -202,7 +198,7 @@ PyTypeObject JSObjectIterProxyType = { PyTypeObject JSObjectKeysProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectKeysProxy", + .tp_name = PyDictKeys_Type.tp_name, .tp_basicsize = sizeof(JSObjectKeysProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_dealloc, @@ -222,7 +218,7 @@ PyTypeObject JSObjectKeysProxyType = { PyTypeObject JSObjectValuesProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectValuesProxy", + .tp_name = PyDictValues_Type.tp_name, .tp_basicsize = sizeof(JSObjectValuesProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_dealloc, @@ -240,7 +236,7 @@ PyTypeObject JSObjectValuesProxyType = { PyTypeObject JSObjectItemsProxyType = { .ob_base = PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pythonmonkey.JSObjectItemsProxy", + .tp_name = PyDictKeys_Type.tp_name, .tp_basicsize = sizeof(JSObjectItemsProxy), .tp_itemsize = 0, .tp_dealloc = (destructor)JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_dealloc, @@ -424,8 +420,6 @@ static PyObject *eval(PyObject *self, PyObject *args) { script = JS::CompileUtf8File(GLOBAL_CX, options, file); fclose(file); } - file = NULL; - code = NULL; if (!script) { setSpiderMonkeyException(GLOBAL_CX); @@ -439,7 +433,7 @@ static PyObject *eval(PyObject *self, PyObject *args) { } // translate to the proper python type - PyType *returnValue = pyTypeFactory(GLOBAL_CX, global, rval); + PyType *returnValue = pyTypeFactory(GLOBAL_CX, *rval); if (PyErr_Occurred()) { return NULL; } From 5fa62dfe2ce3af63ef90e7a269188c3ef2ca24f1 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Wed, 13 Mar 2024 11:14:08 -0400 Subject: [PATCH 123/170] post-merge fixup --- src/modules/pythonmonkey/pythonmonkey.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 3a81b6e5..930aa9f2 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -52,6 +52,8 @@ #include #include +JS::PersistentRootedObject jsFunctionRegistry; + void finalizationRegistryGCCallback(JSContext *cx, JSGCStatus status, JS::GCReason reason, void *data) { if (status == JSGCStatus::JSGC_END) { JS::ClearKeptObjects(GLOBAL_CX); From c645fca17a7efd24fb1e1cac21a84cbc8c03ec94 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 13 Mar 2024 13:46:39 -0400 Subject: [PATCH 124/170] include cleanup --- src/JSObjectItemsProxy.cc | 1 - src/JSObjectKeysProxy.cc | 1 - src/JSObjectValuesProxy.cc | 1 - 3 files changed, 3 deletions(-) diff --git a/src/JSObjectItemsProxy.cc b/src/JSObjectItemsProxy.cc index 0a500213..ac4488e5 100644 --- a/src/JSObjectItemsProxy.cc +++ b/src/JSObjectItemsProxy.cc @@ -23,7 +23,6 @@ #include -#include void JSObjectItemsProxyMethodDefinitions::JSObjectItemsProxy_dealloc(JSObjectItemsProxy *self) diff --git a/src/JSObjectKeysProxy.cc b/src/JSObjectKeysProxy.cc index 5168d9ad..9174b190 100644 --- a/src/JSObjectKeysProxy.cc +++ b/src/JSObjectKeysProxy.cc @@ -23,7 +23,6 @@ #include -#include void JSObjectKeysProxyMethodDefinitions::JSObjectKeysProxy_dealloc(JSObjectKeysProxy *self) diff --git a/src/JSObjectValuesProxy.cc b/src/JSObjectValuesProxy.cc index 5196f403..23e723ba 100644 --- a/src/JSObjectValuesProxy.cc +++ b/src/JSObjectValuesProxy.cc @@ -23,7 +23,6 @@ #include -#include void JSObjectValuesProxyMethodDefinitions::JSObjectValuesProxy_dealloc(JSObjectValuesProxy *self) From 8668e3d19ffd8ed30161d7b5e738e8bcdbeb1a3e Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 14 Mar 2024 09:44:27 -0400 Subject: [PATCH 125/170] refactor(PyBaseProxyHandler): remvoe PyDictOrObjectProxyHandler and instead have PyDictProxyHandler inherit from PyObjectProxyHandler --- include/PyDictOrObjectProxyHandler.hh | 94 --------------------- include/PyDictProxyHandler.hh | 6 +- include/PyObjectProxyHandler.hh | 77 +++++++++++++++++- src/PyDictOrObjectProxyHandler.cc | 113 -------------------------- src/PyObjectProxyHandler.cc | 96 ++++++++++++++++++++++ 5 files changed, 173 insertions(+), 213 deletions(-) delete mode 100644 include/PyDictOrObjectProxyHandler.hh delete mode 100644 src/PyDictOrObjectProxyHandler.cc diff --git a/include/PyDictOrObjectProxyHandler.hh b/include/PyDictOrObjectProxyHandler.hh deleted file mode 100644 index 0cb340eb..00000000 --- a/include/PyDictOrObjectProxyHandler.hh +++ /dev/null @@ -1,94 +0,0 @@ -/** - * @file PyDictOrObjectProxyHandler.hh - * @author Caleb Aikens (caleb@distributive.network) - * @brief Abstract class inherited by PyDictProxyHandler and PyObjectProxyHandler since they share a lot of logic - * @date 2024-02-13 - * - * Copyright (c) 2024 Distributive Corp. - * - */ - -#ifndef PythonMonkey_PyDictOrObjectProxyHandler_ -#define PythonMonkey_PyDictOrObjectProxyHandler_ - -#include "include/PyBaseProxyHandler.hh" - -#include - -#include - -struct PyDictOrObjectProxyHandler : public PyBaseProxyHandler { - PyDictOrObjectProxyHandler(const void *family) : PyBaseProxyHandler(family) {}; - - /** - * @brief Helper function used by dicts and objects for ownPropertyKeys - * - * @param cx - pointer to the JSContext - * @param keys - PyListObject containing the keys of the proxy'd dict/object - * @param length - the length of keys param - * @param props - out-param, will be a JS vector of the keys converted to JS Ids - * @return true - the function succeeded - * @return false - the function failed (an Exception should be raised) - */ - static bool handleOwnPropertyKeys(JSContext *cx, PyObject *keys, size_t length, JS::MutableHandleIdVector props); - - /** - * @brief Helper function used by dicts and objects for get OwnPropertyDescriptor - * - * @param cx - pointer to the JSContext - * @param id - id of the prop to get - * @param desc - out-param, the property descriptor - * @param item - the python object to be converted to a JS prop - * @return true - the function succeeded - * @return false - the function has failed and an exception has been raised - */ - static bool handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, - JS::MutableHandle> desc, PyObject *item); - - /** - * @brief Handles python object reference count when JS Proxy object is finalized - * - * @param gcx pointer to JS::GCContext - * @param proxy the proxy object being finalized - */ - void finalize(JS::GCContext *gcx, JSObject *proxy) const override; - - /** - * @brief Helper function used by dicts and objects to convert dict/object to String - * - * @param cx - pointer to the JSContext - * @param argc - unused - * @param vp - unused - * @return true - this function always returns true - */ - static bool object_toString(JSContext *cx, unsigned argc, JS::Value *vp); - - /** - * @brief Helper function used by dicts and objects to convert dict/object to LocaleString - * - * @param cx - pointer to the JSContext - * @param argc - unused - * @param vp - unused - * @return true - this function always returns true - */ - static bool object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp); - - /** - * @brief Helper function used by dicts and objects to get valueOf, just returns a new reference to `self` - * - * @param cx - pointer to the JSContext - * @param argc - unused - * @param vp - unused - * @return true - the function succeeded - * @return false - the function failed and an exception has been raised - */ - static bool object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp); - - /** - * @brief An array of method definitions for Object prototype methods - * - */ - static JSMethodDef object_methods[]; -}; - -#endif \ No newline at end of file diff --git a/include/PyDictProxyHandler.hh b/include/PyDictProxyHandler.hh index 0e3128d1..f8579c5d 100644 --- a/include/PyDictProxyHandler.hh +++ b/include/PyDictProxyHandler.hh @@ -11,16 +11,16 @@ #ifndef PythonMonkey_PyDictProxy_ #define PythonMonkey_PyDictProxy_ -#include "include/PyDictOrObjectProxyHandler.hh" +#include "include/PyObjectProxyHandler.hh" /** * @brief This struct is the ProxyHandler for JS Proxy Objects pythonmonkey creates to handle coercion from python dicts to JS Objects * */ -struct PyDictProxyHandler : public PyDictOrObjectProxyHandler { +struct PyDictProxyHandler : public PyObjectProxyHandler { public: - PyDictProxyHandler() : PyDictOrObjectProxyHandler(&family) {}; + PyDictProxyHandler() : PyObjectProxyHandler(&family) {}; static const char family; /** diff --git a/include/PyObjectProxyHandler.hh b/include/PyObjectProxyHandler.hh index acefa531..9104ca98 100644 --- a/include/PyObjectProxyHandler.hh +++ b/include/PyObjectProxyHandler.hh @@ -12,7 +12,7 @@ #ifndef PythonMonkey_PyObjectProxy_ #define PythonMonkey_PyObjectProxy_ -#include "include/PyDictOrObjectProxyHandler.hh" +#include "include/PyBaseProxyHandler.hh" #include #include @@ -22,11 +22,76 @@ * @brief This struct is the ProxyHandler for JS Proxy Objects pythonmonkey creates to handle coercion from python objects to JS Objects * */ -struct PyObjectProxyHandler : public PyDictOrObjectProxyHandler { +struct PyObjectProxyHandler : public PyBaseProxyHandler { public: - PyObjectProxyHandler() : PyDictOrObjectProxyHandler(&family) {}; + PyObjectProxyHandler() : PyBaseProxyHandler(&family) {}; + PyObjectProxyHandler(const void *childFamily) : PyBaseProxyHandler(childFamily) {}; static const char family; + /** + * @brief Helper function used by dicts and objects for ownPropertyKeys + * + * @param cx - pointer to the JSContext + * @param keys - PyListObject containing the keys of the proxy'd dict/object + * @param length - the length of keys param + * @param props - out-param, will be a JS vector of the keys converted to JS Ids + * @return true - the function succeeded + * @return false - the function failed (an Exception should be raised) + */ + static bool handleOwnPropertyKeys(JSContext *cx, PyObject *keys, size_t length, JS::MutableHandleIdVector props); + + /** + * @brief Helper function used by dicts and objects for get OwnPropertyDescriptor + * + * @param cx - pointer to the JSContext + * @param id - id of the prop to get + * @param desc - out-param, the property descriptor + * @param item - the python object to be converted to a JS prop + * @return true - the function succeeded + * @return false - the function has failed and an exception has been raised + */ + static bool handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, + JS::MutableHandle> desc, PyObject *item); + + /** + * @brief Handles python object reference count when JS Proxy object is finalized + * + * @param gcx pointer to JS::GCContext + * @param proxy the proxy object being finalized + */ + void finalize(JS::GCContext *gcx, JSObject *proxy) const override; + + /** + * @brief Helper function used by dicts and objects to convert dict/object to String + * + * @param cx - pointer to the JSContext + * @param argc - unused + * @param vp - unused + * @return true - this function always returns true + */ + static bool object_toString(JSContext *cx, unsigned argc, JS::Value *vp); + + /** + * @brief Helper function used by dicts and objects to convert dict/object to LocaleString + * + * @param cx - pointer to the JSContext + * @param argc - unused + * @param vp - unused + * @return true - this function always returns true + */ + static bool object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp); + + /** + * @brief Helper function used by dicts and objects to get valueOf, just returns a new reference to `self` + * + * @param cx - pointer to the JSContext + * @param argc - unused + * @param vp - unused + * @return true - the function succeeded + * @return false - the function failed and an exception has been raised + */ + static bool object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp); + /** * @brief [[OwnPropertyKeys]] * @@ -126,6 +191,12 @@ public: JS::ObjectOpResult &result) const override; bool getBuiltinClass(JSContext *cx, JS::HandleObject proxy, js::ESClass *cls) const override; + + /** + * @brief An array of method definitions for Object prototype methods + * + */ + static JSMethodDef object_methods[]; }; #endif \ No newline at end of file diff --git a/src/PyDictOrObjectProxyHandler.cc b/src/PyDictOrObjectProxyHandler.cc deleted file mode 100644 index cf7bbcb7..00000000 --- a/src/PyDictOrObjectProxyHandler.cc +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @file PyDictOrObjectProxyHandler.cc - * @author Caleb Aikens (caleb@distributive.network) - * @brief - * @date 2024-02-13 - * - * Copyright (c) 2024 Distributive Corp. - * - */ - -#include "include/PyDictOrObjectProxyHandler.hh" - -#include "include/jsTypeFactory.hh" - -#include -#include - -bool PyDictOrObjectProxyHandler::object_toString(JSContext *cx, unsigned argc, JS::Value *vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - - args.rval().setString(JS_NewStringCopyZ(cx, "[object Object]")); - return true; -} - -bool PyDictOrObjectProxyHandler::object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp) { - return object_toString(cx, argc, vp); -} - -bool PyDictOrObjectProxyHandler::object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp) { - JS::CallArgs args = JS::CallArgsFromVp(argc, vp); - - JS::RootedObject proxy(cx, JS::ToObject(cx, args.thisv())); - if (!proxy) { - return false; - } - PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - - // return ref to self - args.rval().set(jsTypeFactory(cx, self)); - return true; -} - -JSMethodDef PyDictOrObjectProxyHandler::object_methods[] = { - {"toString", PyDictOrObjectProxyHandler::object_toString, 0}, - {"toLocaleString", PyDictOrObjectProxyHandler::object_toLocaleString, 0}, - {"valueOf", PyDictOrObjectProxyHandler::object_valueOf, 0}, - {NULL, NULL, 0} -}; - - -bool PyDictOrObjectProxyHandler::handleOwnPropertyKeys(JSContext *cx, PyObject *keys, size_t length, JS::MutableHandleIdVector props) { - if (!props.reserve(length)) { - return false; // out of memory - } - - for (size_t i = 0; i < length; i++) { - PyObject *key = PyList_GetItem(keys, i); - JS::RootedId jsId(cx); - if (!keyToId(key, &jsId)) { - continue; // skip over keys that are not str or int - } - props.infallibleAppend(jsId); - } - return true; -} - -bool PyDictOrObjectProxyHandler::handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, - JS::MutableHandle> desc, PyObject *item) { - // see if we're calling a function - if (id.isString()) { - for (size_t index = 0;; index++) { - bool isThatFunction; - const char *methodName = object_methods[index].name; - if (methodName == NULL) { - break; - } - else if (JS_StringEqualsAscii(cx, id.toString(), methodName, &isThatFunction) && isThatFunction) { - JSFunction *newFunction = JS_NewFunction(cx, object_methods[index].call, object_methods[index].nargs, 0, NULL); - if (!newFunction) return false; - JS::RootedObject funObj(cx, JS_GetFunctionObject(newFunction)); - desc.set(mozilla::Some( - JS::PropertyDescriptor::Data( - JS::ObjectValue(*funObj), - {JS::PropertyAttribute::Enumerable} - ) - )); - return true; - } - } - } - - if (!item) { // NULL if the key is not present - desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys - } else { - desc.set(mozilla::Some( - JS::PropertyDescriptor::Data( - jsTypeFactory(cx, item), - {JS::PropertyAttribute::Writable, JS::PropertyAttribute::Enumerable} - ) - )); - } - return true; -} - -void PyDictOrObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { - // We cannot call Py_DECREF here when shutting down as the thread state is gone. - // Then, when shutting down, there is only on reference left, and we don't need - // to free the object since the entire process memory is being released. - PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); - if (Py_REFCNT(self) > 1) { - Py_DECREF(self); - } -} \ No newline at end of file diff --git a/src/PyObjectProxyHandler.cc b/src/PyObjectProxyHandler.cc index 0641d6c1..f0a64dd8 100644 --- a/src/PyObjectProxyHandler.cc +++ b/src/PyObjectProxyHandler.cc @@ -26,6 +26,102 @@ const char PyObjectProxyHandler::family = 0; +bool PyObjectProxyHandler::object_toString(JSContext *cx, unsigned argc, JS::Value *vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + args.rval().setString(JS_NewStringCopyZ(cx, "[object Object]")); + return true; +} + +bool PyObjectProxyHandler::object_toLocaleString(JSContext *cx, unsigned argc, JS::Value *vp) { + return object_toString(cx, argc, vp); +} + +bool PyObjectProxyHandler::object_valueOf(JSContext *cx, unsigned argc, JS::Value *vp) { + JS::CallArgs args = JS::CallArgsFromVp(argc, vp); + + JS::RootedObject proxy(cx, JS::ToObject(cx, args.thisv())); + if (!proxy) { + return false; + } + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + + // return ref to self + args.rval().set(jsTypeFactory(cx, self)); + return true; +} + +JSMethodDef PyObjectProxyHandler::object_methods[] = { + {"toString", PyObjectProxyHandler::object_toString, 0}, + {"toLocaleString", PyObjectProxyHandler::object_toLocaleString, 0}, + {"valueOf", PyObjectProxyHandler::object_valueOf, 0}, + {NULL, NULL, 0} +}; + +bool PyObjectProxyHandler::handleOwnPropertyKeys(JSContext *cx, PyObject *keys, size_t length, JS::MutableHandleIdVector props) { + if (!props.reserve(length)) { + return false; // out of memory + } + + for (size_t i = 0; i < length; i++) { + PyObject *key = PyList_GetItem(keys, i); + JS::RootedId jsId(cx); + if (!keyToId(key, &jsId)) { + continue; // skip over keys that are not str or int + } + props.infallibleAppend(jsId); + } + return true; +} + +bool PyObjectProxyHandler::handleGetOwnPropertyDescriptor(JSContext *cx, JS::HandleId id, + JS::MutableHandle> desc, PyObject *item) { + // see if we're calling a function + if (id.isString()) { + for (size_t index = 0;; index++) { + bool isThatFunction; + const char *methodName = object_methods[index].name; + if (methodName == NULL) { + break; + } + else if (JS_StringEqualsAscii(cx, id.toString(), methodName, &isThatFunction) && isThatFunction) { + JSFunction *newFunction = JS_NewFunction(cx, object_methods[index].call, object_methods[index].nargs, 0, NULL); + if (!newFunction) return false; + JS::RootedObject funObj(cx, JS_GetFunctionObject(newFunction)); + desc.set(mozilla::Some( + JS::PropertyDescriptor::Data( + JS::ObjectValue(*funObj), + {JS::PropertyAttribute::Enumerable} + ) + )); + return true; + } + } + } + + if (!item) { // NULL if the key is not present + desc.set(mozilla::Nothing()); // JS objects return undefined for nonpresent keys + } else { + desc.set(mozilla::Some( + JS::PropertyDescriptor::Data( + jsTypeFactory(cx, item), + {JS::PropertyAttribute::Writable, JS::PropertyAttribute::Enumerable} + ) + )); + } + return true; +} + +void PyObjectProxyHandler::finalize(JS::GCContext *gcx, JSObject *proxy) const { + // We cannot call Py_DECREF here when shutting down as the thread state is gone. + // Then, when shutting down, there is only on reference left, and we don't need + // to free the object since the entire process memory is being released. + PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); + if (Py_REFCNT(self) > 1) { + Py_DECREF(self); + } +} + bool PyObjectProxyHandler::ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) const { PyObject *self = JS::GetMaybePtrFromReservedSlot(proxy, PyObjectSlot); PyObject *keys = PyObject_Dir(self); From bf16c39a493a9d193048d335363d43144180fdcb Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 14 Mar 2024 12:33:12 -0400 Subject: [PATCH 126/170] enhanced python stack trace tests --- tests/python/test_arrays.py | 7 ++++++- tests/python/test_pythonmonkey_eval.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 647e6773..78c8f0ad 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -755,6 +755,9 @@ def myFunc(e,f): except Exception as e: assert str(type(e)) == "" assert "object of type 'float' has no len()" in str(e) + assert "test_arrays.py" in str(e) + assert "line 751" in str(e) + assert "in myFunc" in str(e) def test_sort_with_one_arg_keyfunc(): items = ['Four', 'Three', 'One'] @@ -766,6 +769,7 @@ def myFunc(e): except Exception as e: assert str(type(e)) == "" assert "takes 1 positional argument but 2 were given" in str(e) + assert "[python code]" in str(e) def test_sort_with_builtin_keyfunc(): items = ['Four', 'Three', 'One'] @@ -774,7 +778,8 @@ def test_sort_with_builtin_keyfunc(): assert (False) except Exception as e: assert str(type(e)) == "" - assert "len() takes exactly one argument (2 given)" in str(e) + assert "len() takes exactly one argument (2 given)" in str(e) + assert "[python code]" in str(e) def test_sort_with_js_func(): items = ['Four', 'Three', 'One'] diff --git a/tests/python/test_pythonmonkey_eval.py b/tests/python/test_pythonmonkey_eval.py index 82bd8401..d1be04db 100644 --- a/tests/python/test_pythonmonkey_eval.py +++ b/tests/python/test_pythonmonkey_eval.py @@ -133,7 +133,7 @@ def c(): }''') assert str(b(c)).__contains__("Caught in JS Error: Python Exception: this is an exception") assert str(b(c)).__contains__("test_pythonmonkey_eval.py") - assert str(b(c)).__contains__("line 124") + assert str(b(c)).__contains__("line 126") assert str(b(c)).__contains__("in c") def test_eval_exceptions_nested_js_py_js(): From 7abbd1bc827620280e42c91901c0d3ece108f29d Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Thu, 14 Mar 2024 15:59:19 -0400 Subject: [PATCH 127/170] Revert "enhanced python stack trace tests" This reverts commit bf16c39a493a9d193048d335363d43144180fdcb. --- tests/python/test_arrays.py | 7 +------ tests/python/test_pythonmonkey_eval.py | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 78c8f0ad..647e6773 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -755,9 +755,6 @@ def myFunc(e,f): except Exception as e: assert str(type(e)) == "" assert "object of type 'float' has no len()" in str(e) - assert "test_arrays.py" in str(e) - assert "line 751" in str(e) - assert "in myFunc" in str(e) def test_sort_with_one_arg_keyfunc(): items = ['Four', 'Three', 'One'] @@ -769,7 +766,6 @@ def myFunc(e): except Exception as e: assert str(type(e)) == "" assert "takes 1 positional argument but 2 were given" in str(e) - assert "[python code]" in str(e) def test_sort_with_builtin_keyfunc(): items = ['Four', 'Three', 'One'] @@ -778,8 +774,7 @@ def test_sort_with_builtin_keyfunc(): assert (False) except Exception as e: assert str(type(e)) == "" - assert "len() takes exactly one argument (2 given)" in str(e) - assert "[python code]" in str(e) + assert "len() takes exactly one argument (2 given)" in str(e) def test_sort_with_js_func(): items = ['Four', 'Three', 'One'] diff --git a/tests/python/test_pythonmonkey_eval.py b/tests/python/test_pythonmonkey_eval.py index d1be04db..82bd8401 100644 --- a/tests/python/test_pythonmonkey_eval.py +++ b/tests/python/test_pythonmonkey_eval.py @@ -133,7 +133,7 @@ def c(): }''') assert str(b(c)).__contains__("Caught in JS Error: Python Exception: this is an exception") assert str(b(c)).__contains__("test_pythonmonkey_eval.py") - assert str(b(c)).__contains__("line 126") + assert str(b(c)).__contains__("line 124") assert str(b(c)).__contains__("in c") def test_eval_exceptions_nested_js_py_js(): From f88cfd2539e3783c32b8569a1f861fc393d61510 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 14 Mar 2024 16:40:59 -0400 Subject: [PATCH 128/170] test fix --- tests/python/test_pythonmonkey_eval.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/python/test_pythonmonkey_eval.py b/tests/python/test_pythonmonkey_eval.py index 82bd8401..d1be04db 100644 --- a/tests/python/test_pythonmonkey_eval.py +++ b/tests/python/test_pythonmonkey_eval.py @@ -133,7 +133,7 @@ def c(): }''') assert str(b(c)).__contains__("Caught in JS Error: Python Exception: this is an exception") assert str(b(c)).__contains__("test_pythonmonkey_eval.py") - assert str(b(c)).__contains__("line 124") + assert str(b(c)).__contains__("line 126") assert str(b(c)).__contains__("in c") def test_eval_exceptions_nested_js_py_js(): From 25f6134204441d8b91c6cf9bb2601675db093de5 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 14 Mar 2024 16:45:06 -0400 Subject: [PATCH 129/170] Full JS stacks provided in the context of a Python Exception while calling from JS code --- src/ExceptionType.cc | 42 ++++++++++++++++++++++++++++++------- tests/python/test_arrays.py | 13 ++++++++++-- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index 62a1fad2..8f887308 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -12,6 +12,7 @@ #include "include/setSpiderMonkeyException.hh" #include "include/ExceptionType.hh" +#include "include/StrType.hh" #include #include @@ -78,6 +79,25 @@ tb_print_line_repeated(_PyUnicodeWriter *writer, long cnt) JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyObject *traceBack) { assert(exceptionValue != NULL); + // Gather JS context + JS_ReportErrorASCII(cx, ""); // throw JS error and gather all details + + JS::ExceptionStack exceptionStack(cx); + if (!JS::GetPendingExceptionStack(cx, &exceptionStack)) { + return NULL; + } + JS_ClearPendingException(cx); + + std::stringstream stackStream; + JS::HandleObject stackObj = exceptionStack.stack(); + if (stackObj) { + JS::RootedString stackStr(cx); + JS::BuildStackString(cx, nullptr, stackObj, &stackStr, 2, js::StackFormat::SpiderMonkey); + stackStream << "\nJS Stack Trace:\n" << StrType(cx, stackStr).getValue(); + } + + + // Gather Python context PyObject *pyErrType = PyObject_Type(exceptionValue); const char *pyErrTypeName = _PyType_Name((PyTypeObject *)pyErrType); @@ -235,12 +255,12 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb { std::stringstream msgStream; msgStream << "Python " << pyErrTypeName << ": " << PyUnicode_AsUTF8(pyErrMsg) << "\n" << PyUnicode_AsUTF8(_PyUnicodeWriter_Finish(&writer)); - std::string msg = msgStream.str(); + msgStream << stackStream.str(); JS::RootedValue rval(cx); JS::RootedString filename(cx, JS_NewStringCopyZ(cx, PyUnicode_AsUTF8(fileName))); - JS::RootedString message(cx, JS_NewStringCopyZ(cx, msg.c_str())); - // TODO stack argument cannot be passed in as a string anymore (deprecated), and could not find a proper example using the new argument type + JS::RootedString message(cx, JS_NewStringCopyZ(cx, msgStream.str().c_str())); + // stack argument cannot be passed in as a string anymore (deprecated), and could not find a proper example using the new argument type if (!JS::CreateError(cx, JSExnType::JSEXN_ERR, nullptr, filename, lineno, 0, nullptr, message, JS::NothingHandleValue, &rval)) { return NULL; } @@ -256,15 +276,21 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb Py_XDECREF(code); } + // gather additional JS context + JS::ErrorReportBuilder reportBuilder(cx); + if (!reportBuilder.init(cx, exceptionStack, JS::ErrorReportBuilder::WithSideEffects)) { + return NULL; + } + JSErrorReport *errorReport = reportBuilder.report(); + std::stringstream msgStream; msgStream << "Python " << pyErrTypeName << ": " << PyUnicode_AsUTF8(pyErrMsg); - std::string msg = msgStream.str(); + msgStream << stackStream.str(); JS::RootedValue rval(cx); - JS::RootedObject stack(cx); - JS::RootedString filename(cx, JS_NewStringCopyZ(cx, "[python code]")); - JS::RootedString message(cx, JS_NewStringCopyZ(cx, msg.c_str())); - if (!JS::CreateError(cx, JSExnType::JSEXN_ERR, nullptr, filename, 0, 0, nullptr, message, JS::NothingHandleValue, &rval)) { + JS::RootedString filename(cx, JS_NewStringCopyZ(cx, "")); // cannot be null or omitted, but is overriden by the errorReport + JS::RootedString message(cx, JS_NewStringCopyZ(cx, msgStream.str().c_str())); + if (!JS::CreateError(cx, JSExnType::JSEXN_ERR, nullptr, filename, 0, 0, errorReport, message, JS::NothingHandleValue, &rval)) { return NULL; } diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 647e6773..e7d8f45a 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -754,7 +754,12 @@ def myFunc(e,f): assert (False) except Exception as e: assert str(type(e)) == "" - assert "object of type 'float' has no len()" in str(e) + assert "object of type 'float' has no len()" in str(e) + assert "test_arrays.py" in str(e) + assert "line 751" in str(e) + assert "in myFunc" in str(e) + assert "JS Stack Trace" in str(e) + assert "@evaluate:1:27" in str(e) def test_sort_with_one_arg_keyfunc(): items = ['Four', 'Three', 'One'] @@ -766,6 +771,8 @@ def myFunc(e): except Exception as e: assert str(type(e)) == "" assert "takes 1 positional argument but 2 were given" in str(e) + assert "JS Stack Trace" in str(e) + assert "@evaluate:1:27" in str(e) def test_sort_with_builtin_keyfunc(): items = ['Four', 'Three', 'One'] @@ -774,7 +781,9 @@ def test_sort_with_builtin_keyfunc(): assert (False) except Exception as e: assert str(type(e)) == "" - assert "len() takes exactly one argument (2 given)" in str(e) + assert "len() takes exactly one argument (2 given)" in str(e) + assert "JS Stack Trace" in str(e) + assert "@evaluate:1:27" in str(e) def test_sort_with_js_func(): items = ['Four', 'Three', 'One'] From 7eaab5e8198de935b560f32ee5d960e07567e2ed Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 14 Mar 2024 16:47:56 -0400 Subject: [PATCH 130/170] improved comment --- src/ExceptionType.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index 8f887308..9faf225a 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -276,7 +276,7 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb Py_XDECREF(code); } - // gather additional JS context + // gather additional JS context details JS::ErrorReportBuilder reportBuilder(cx); if (!reportBuilder.init(cx, exceptionStack, JS::ErrorReportBuilder::WithSideEffects)) { return NULL; From 93fb62120e17052ca332a0f8c645338557edcd14 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 14 Mar 2024 16:48:45 -0400 Subject: [PATCH 131/170] one more comment --- src/ExceptionType.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index 9faf225a..d1bbfea6 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -290,6 +290,7 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb JS::RootedValue rval(cx); JS::RootedString filename(cx, JS_NewStringCopyZ(cx, "")); // cannot be null or omitted, but is overriden by the errorReport JS::RootedString message(cx, JS_NewStringCopyZ(cx, msgStream.str().c_str())); + // filename cannot be null if (!JS::CreateError(cx, JSExnType::JSEXN_ERR, nullptr, filename, 0, 0, errorReport, message, JS::NothingHandleValue, &rval)) { return NULL; } From 015eb14f5c1af96ad0871832c09b07904425a530 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 14 Mar 2024 17:31:13 -0400 Subject: [PATCH 132/170] added test for array subclass --- tests/python/test_arrays.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/python/test_arrays.py b/tests/python/test_arrays.py index 647e6773..42cec7d2 100644 --- a/tests/python/test_arrays.py +++ b/tests/python/test_arrays.py @@ -2139,4 +2139,26 @@ def test_assign_bad_index_with_gap(): items = [1,2,3] result = [] pm.eval("(result, arr) => {result[0] = 4; result[5] = 6}")(result, items) - assert result == [4, None, None, None, None, 6] \ No newline at end of file + assert result == [4, None, None, None, None, 6] + +def test_array_subclass_behaves_as_array(): + my_JS_function = pm.eval(""" + () => { + class MyClass extends Array { + constructor(...args) + { + super(...args); + return this; + } + } + return new MyClass(1,2); + } + """) + + a = my_JS_function() + assert a == [1,2] + result = [] + for i in a: + result.append(i) + assert result == [1,2] + assert a is not result \ No newline at end of file From b58b0ac3ec5ef6ff489d0ee7df14feddf86cd475 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 15 Mar 2024 11:31:51 -0400 Subject: [PATCH 133/170] adjustment to __class__ implementation and added test --- src/JSObjectProxy.cc | 3 ++- tests/python/test_dicts.py | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 75b9d817..f368ea93 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -100,7 +100,8 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId } value.set(boundFunction); } - } else if (value.isUndefined()) { + } + else if (value.isUndefined() && PyUnicode_Check(key)) { if (strcmp("__class__", PyUnicode_AsUTF8(key)) == 0) { return PyObject_GenericGetAttr((PyObject *)self, key); } diff --git a/tests/python/test_dicts.py b/tests/python/test_dicts.py index 65112e4b..9e147726 100644 --- a/tests/python/test_dicts.py +++ b/tests/python/test_dicts.py @@ -325,4 +325,9 @@ def test_repr_max_recursion_depth(): #__class__ def test___class__attribute(): items = pm.eval("({'a': 10})") - assert repr(items.__class__) == "" \ No newline at end of file + assert repr(items.__class__) == "" + +#none value attribute +def test___none__attribute(): + a = pm.eval("({'0': 1, '1': 2})") + assert a[2] is None \ No newline at end of file From 453ded4b436d53b2859ffc255c85e9d33682b8cc Mon Sep 17 00:00:00 2001 From: philippedistributive <151072087+philippedistributive@users.noreply.github.com> Date: Fri, 15 Mar 2024 11:33:03 -0400 Subject: [PATCH 134/170] Update src/ExceptionType.cc Co-authored-by: Caleb Aikens --- src/ExceptionType.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index d1bbfea6..b82c45c8 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -89,7 +89,7 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb JS_ClearPendingException(cx); std::stringstream stackStream; - JS::HandleObject stackObj = exceptionStack.stack(); + JS::RootedObject stackObj(cx, exceptionStack.stack(); if (stackObj) { JS::RootedString stackStr(cx); JS::BuildStackString(cx, nullptr, stackObj, &stackStr, 2, js::StackFormat::SpiderMonkey); From 327ab26bc891debd047045a86a472915d4a7c726 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 15 Mar 2024 11:40:51 -0400 Subject: [PATCH 135/170] proper exception stack type --- src/ExceptionType.cc | 4 ++-- src/setSpiderMonkeyException.cc | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index b82c45c8..56f469a1 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -89,8 +89,8 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb JS_ClearPendingException(cx); std::stringstream stackStream; - JS::RootedObject stackObj(cx, exceptionStack.stack(); - if (stackObj) { + JS::RootedObject stackObj(cx, exceptionStack.stack()); + if (stackObj.get()) { JS::RootedString stackStr(cx); JS::BuildStackString(cx, nullptr, stackObj, &stackStr, 2, js::StackFormat::SpiderMonkey); stackStream << "\nJS Stack Trace:\n" << StrType(cx, stackStr).getValue(); diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index a4f8c72b..08ecca7f 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -56,8 +56,9 @@ PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionS // print out the SpiderMonkey error message outStrStream << reportBuilder.toStringResult().c_str() << "\n"; - JS::HandleObject stackObj = exceptionStack.stack(); - if (stackObj) { // stack can be null + + JS::RootedObject stackObj(cx, exceptionStack.stack()); + if (stackObj.get()) { JS::RootedString stackStr(cx); BuildStackString(cx, nullptr, stackObj, &stackStr, /* indent */ 2, js::StackFormat::SpiderMonkey); outStrStream << "Stack Trace: \n" << StrType(cx, stackStr).getValue(); From 66dbb978f6ddba18bbefdfc99b181389e2758769 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 15 Mar 2024 12:16:24 -0400 Subject: [PATCH 136/170] adjustment to __class__ for arrayproxy --- src/JSArrayProxy.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index a3caa54a..53f2f21c 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -54,7 +54,7 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_get(JSArrayProxy *self, Py if (methodName == NULL || !PyUnicode_Check(key)) { // reached end of list JS::RootedValue value(GLOBAL_CX); JS_GetPropertyById(GLOBAL_CX, *(self->jsArray), id, &value); - if (value.isUndefined()) { + if (value.isUndefined() && PyUnicode_Check(key)) { if (strcmp("__class__", PyUnicode_AsUTF8(key)) == 0) { return PyObject_GenericGetAttr((PyObject *)self, key); } From e08f74be4657ece3d95da7ae47d46d10df0bf58f Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 15 Mar 2024 12:20:30 -0400 Subject: [PATCH 137/170] cleanup --- python/pythonmonkey/newfile.py | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 python/pythonmonkey/newfile.py diff --git a/python/pythonmonkey/newfile.py b/python/pythonmonkey/newfile.py deleted file mode 100644 index 484eb717..00000000 --- a/python/pythonmonkey/newfile.py +++ /dev/null @@ -1,6 +0,0 @@ -import pythonmonkey as pm - -#likes = pm.eval('({"color": "blue", "fruit": "apple", "pet": "dog"})') - -CryptoJS = pm.require('crypto-js') -CryptoJS From 77f460b8fd888a3b7846a9a77a65a24e930578b1 Mon Sep 17 00:00:00 2001 From: philippedistributive <151072087+philippedistributive@users.noreply.github.com> Date: Fri, 15 Mar 2024 15:24:28 -0400 Subject: [PATCH 138/170] Update src/jsTypeFactory.cc Co-authored-by: Caleb Aikens --- src/jsTypeFactory.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index ce8c6490..d627ee06 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -250,7 +250,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { returnType.setNull(); } else if (PythonAwaitable_Check(object)) { - returnType.setObjectOrNull((new PromiseType(object))->toJsPromise(cx)); + returnType.setObjectOrNull((PromiseType(object)).toJsPromise(cx)); } else { JS::RootedValue v(cx); From c627e30928d9c2243f4bdb08c00163defbedd803 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 15 Mar 2024 15:28:27 -0400 Subject: [PATCH 139/170] Revert "Update src/jsTypeFactory.cc" This reverts commit 77f460b8fd888a3b7846a9a77a65a24e930578b1. --- src/jsTypeFactory.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index d627ee06..ce8c6490 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -250,7 +250,7 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) { returnType.setNull(); } else if (PythonAwaitable_Check(object)) { - returnType.setObjectOrNull((PromiseType(object)).toJsPromise(cx)); + returnType.setObjectOrNull((new PromiseType(object))->toJsPromise(cx)); } else { JS::RootedValue v(cx); From f9c29ada61a11acf5c179ad11c68d3cafe9a3f69 Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Fri, 15 Mar 2024 15:58:37 -0400 Subject: [PATCH 140/170] remove commented-out code --- src/PyEventLoop.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/PyEventLoop.cc b/src/PyEventLoop.cc index e24ee3cd..14403bd7 100644 --- a/src/PyEventLoop.cc +++ b/src/PyEventLoop.cc @@ -22,7 +22,7 @@ static PyObject *eventLoopJobWrapper(PyObject *jobFn, PyObject *Py_UNUSED(_)) { if (!ret) { return NULL; } - Py_DECREF(ret); + Py_DECREF(ret); if (PyErr_Occurred()) { return NULL; } else { @@ -78,7 +78,6 @@ PyEventLoop::Future PyEventLoop::ensureFuture(PyObject *awaitable) { Py_DECREF(args); Py_DECREF(kwargs); - // Py_INCREF(futureObj); return PyEventLoop::Future(futureObj); } From e5a82e6d6eb91ec29c3137b22eb3421f9440c1b7 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 19 Mar 2024 11:40:06 -0400 Subject: [PATCH 141/170] remove duplicate JS stack trace --- include/setSpiderMonkeyException.hh | 3 ++- src/ExceptionType.cc | 2 +- src/jsTypeFactory.cc | 2 +- src/setSpiderMonkeyException.cc | 41 +++++++++++++++++++++++------ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/include/setSpiderMonkeyException.hh b/include/setSpiderMonkeyException.hh index aa388ca9..dc7a8794 100644 --- a/include/setSpiderMonkeyException.hh +++ b/include/setSpiderMonkeyException.hh @@ -18,8 +18,9 @@ * * @param cx - pointer to the JS context * @param exceptionStack - reference to the SpiderMonkey exception stack + * @param printStack - whether or not to print the JS stack */ -PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionStack); +PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionStack, bool printStack); /** * @brief This function sets a python error under the assumption that a JS_* function call has failed. Do not call this function if that is not the case. diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index 56f469a1..a9b1a891 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -27,7 +27,7 @@ ExceptionType::ExceptionType(JSContext *cx, JS::HandleObject error) { // Convert the JS Error object to a Python string JS::RootedValue errValue(cx, JS::ObjectValue(*error)); // err JS::RootedObject errStack(cx, JS::ExceptionStackOrNull(error)); // err.stack - PyObject *errStr = getExceptionString(cx, JS::ExceptionStack(cx, errValue, errStack)); + PyObject *errStr = getExceptionString(cx, JS::ExceptionStack(cx, errValue, errStack), true); // Construct a new SpiderMonkeyError python object // pyObject = SpiderMonkeyError(errStr) diff --git a/src/jsTypeFactory.cc b/src/jsTypeFactory.cc index ce8c6490..0ce2492a 100644 --- a/src/jsTypeFactory.cc +++ b/src/jsTypeFactory.cc @@ -288,7 +288,7 @@ void setPyException(JSContext *cx) { } PyObject *type, *value, *traceback; - PyErr_Fetch(&type, &value, &traceback); // also clears the error indicator + PyErr_Fetch(&type, &value, &traceback); JSObject *jsException = ExceptionType::toJsError(cx, value, traceback); diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index 08ecca7f..cdcef274 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -18,7 +18,7 @@ #include #include -PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionStack) { +PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionStack, bool printStack) { JS::ErrorReportBuilder reportBuilder(cx); if (!reportBuilder.init(cx, exceptionStack, JS::ErrorReportBuilder::WithSideEffects /* may call the `toString` method if an object is thrown */)) { return PyUnicode_FromString("Spidermonkey set an exception, but could not initialize the error report."); @@ -56,12 +56,13 @@ PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionS // print out the SpiderMonkey error message outStrStream << reportBuilder.toStringResult().c_str() << "\n"; - - JS::RootedObject stackObj(cx, exceptionStack.stack()); - if (stackObj.get()) { - JS::RootedString stackStr(cx); - BuildStackString(cx, nullptr, stackObj, &stackStr, /* indent */ 2, js::StackFormat::SpiderMonkey); - outStrStream << "Stack Trace: \n" << StrType(cx, stackStr).getValue(); + if (printStack) { + JS::RootedObject stackObj(cx, exceptionStack.stack()); + if (stackObj.get()) { + JS::RootedString stackStr(cx); + BuildStackString(cx, nullptr, stackObj, &stackStr, /* indent */ 2, js::StackFormat::SpiderMonkey); + outStrStream << "Stack Trace: \n" << StrType(cx, stackStr).getValue(); + } } return PyUnicode_FromString(outStrStream.str().c_str()); @@ -80,11 +81,35 @@ void setSpiderMonkeyException(JSContext *cx) { PyErr_SetString(SpiderMonkeyError, "Spidermonkey set an exception, but was unable to retrieve it."); return; } + + // check if it is a Python Exception and already has a stack trace + bool printStack; + + JS::Rooted exn(cx); + JS_GetPendingException(cx, &exn); + if (exn.isObject()) { + JS::RootedObject exnObj(cx, &exn.toObject()); + JS::RootedValue tmp(cx); + if (!JS_GetProperty(cx, exnObj, "message", &tmp)) { + printStack = true; + } + else if (tmp.isString()) { + JS::RootedString rootedStr(cx, tmp.toString()); + printStack = strstr(JS_EncodeStringToUTF8(cx, rootedStr).get(), "JS Stack Trace") == NULL; + } + else { + printStack = true; + } + } + else { + printStack = true; + } + JS_ClearPendingException(cx); // `PyErr_SetString` uses `PyErr_SetObject` with `PyUnicode_FromString` under the hood // see https://github.com/python/cpython/blob/3.9/Python/errors.c#L234-L236 - PyObject *errStr = getExceptionString(cx, exceptionStack); + PyObject *errStr = getExceptionString(cx, exceptionStack, printStack); PyErr_SetObject(SpiderMonkeyError, errStr); Py_XDECREF(errStr); } From da5f621208978c25b06da7cda7f82c3d63b4a4a1 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 19 Mar 2024 11:48:33 -0400 Subject: [PATCH 142/170] logic improvement --- src/setSpiderMonkeyException.cc | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index cdcef274..7cf42439 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -83,28 +83,20 @@ void setSpiderMonkeyException(JSContext *cx) { } // check if it is a Python Exception and already has a stack trace - bool printStack; + bool printStack = true; - JS::Rooted exn(cx); + JS::RootedValue exn(cx); JS_GetPendingException(cx, &exn); if (exn.isObject()) { JS::RootedObject exnObj(cx, &exn.toObject()); JS::RootedValue tmp(cx); - if (!JS_GetProperty(cx, exnObj, "message", &tmp)) { - printStack = true; - } - else if (tmp.isString()) { + if (JS_GetProperty(cx, exnObj, "message", &tmp) && tmp.isString()) { JS::RootedString rootedStr(cx, tmp.toString()); printStack = strstr(JS_EncodeStringToUTF8(cx, rootedStr).get(), "JS Stack Trace") == NULL; } - else { - printStack = true; - } - } - else { - printStack = true; } + JS_ClearPendingException(cx); // `PyErr_SetString` uses `PyErr_SetObject` with `PyUnicode_FromString` under the hood From e8c439a5d636769ebc459af687b88dd4001f77cf Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 19 Mar 2024 11:51:26 -0400 Subject: [PATCH 143/170] cleanup --- src/setSpiderMonkeyException.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index 7cf42439..4239d9f1 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -60,8 +60,8 @@ PyObject *getExceptionString(JSContext *cx, const JS::ExceptionStack &exceptionS JS::RootedObject stackObj(cx, exceptionStack.stack()); if (stackObj.get()) { JS::RootedString stackStr(cx); - BuildStackString(cx, nullptr, stackObj, &stackStr, /* indent */ 2, js::StackFormat::SpiderMonkey); - outStrStream << "Stack Trace: \n" << StrType(cx, stackStr).getValue(); + BuildStackString(cx, nullptr, stackObj, &stackStr, 2, js::StackFormat::SpiderMonkey); + outStrStream << "Stack Trace:\n" << StrType(cx, stackStr).getValue(); } } @@ -96,7 +96,6 @@ void setSpiderMonkeyException(JSContext *cx) { } } - JS_ClearPendingException(cx); // `PyErr_SetString` uses `PyErr_SetObject` with `PyUnicode_FromString` under the hood From 0dc70afc635bcf1a242b0250a065c57efed337c6 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 19 Mar 2024 11:55:20 -0400 Subject: [PATCH 144/170] logic improvement --- src/setSpiderMonkeyException.cc | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/setSpiderMonkeyException.cc b/src/setSpiderMonkeyException.cc index 4239d9f1..3fe9b494 100644 --- a/src/setSpiderMonkeyException.cc +++ b/src/setSpiderMonkeyException.cc @@ -86,13 +86,14 @@ void setSpiderMonkeyException(JSContext *cx) { bool printStack = true; JS::RootedValue exn(cx); - JS_GetPendingException(cx, &exn); - if (exn.isObject()) { - JS::RootedObject exnObj(cx, &exn.toObject()); - JS::RootedValue tmp(cx); - if (JS_GetProperty(cx, exnObj, "message", &tmp) && tmp.isString()) { - JS::RootedString rootedStr(cx, tmp.toString()); - printStack = strstr(JS_EncodeStringToUTF8(cx, rootedStr).get(), "JS Stack Trace") == NULL; + if (JS_GetPendingException(cx, &exn)) { + if (exn.isObject()) { + JS::RootedObject exnObj(cx, &exn.toObject()); + JS::RootedValue tmp(cx); + if (JS_GetProperty(cx, exnObj, "message", &tmp) && tmp.isString()) { + JS::RootedString rootedStr(cx, tmp.toString()); + printStack = strstr(JS_EncodeStringToUTF8(cx, rootedStr).get(), "JS Stack Trace") == NULL; + } } } From 9d737fdcd1784d69edf70f82a20b42ae2a90850c Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Tue, 19 Mar 2024 15:03:49 -0400 Subject: [PATCH 145/170] newline not needed --- src/ExceptionType.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index a9b1a891..a8daca0d 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -93,7 +93,7 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb if (stackObj.get()) { JS::RootedString stackStr(cx); JS::BuildStackString(cx, nullptr, stackObj, &stackStr, 2, js::StackFormat::SpiderMonkey); - stackStream << "\nJS Stack Trace:\n" << StrType(cx, stackStr).getValue(); + stackStream << "JS Stack Trace:\n" << StrType(cx, stackStr).getValue(); } From dd5e156c229db9aaa808c9dbcd7f565b601c791c Mon Sep 17 00:00:00 2001 From: philippedistributive Date: Wed, 20 Mar 2024 14:23:21 -0400 Subject: [PATCH 146/170] entry newline needed --- src/ExceptionType.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ExceptionType.cc b/src/ExceptionType.cc index a8daca0d..a9b1a891 100644 --- a/src/ExceptionType.cc +++ b/src/ExceptionType.cc @@ -93,7 +93,7 @@ JSObject *ExceptionType::toJsError(JSContext *cx, PyObject *exceptionValue, PyOb if (stackObj.get()) { JS::RootedString stackStr(cx); JS::BuildStackString(cx, nullptr, stackObj, &stackStr, 2, js::StackFormat::SpiderMonkey); - stackStream << "JS Stack Trace:\n" << StrType(cx, stackStr).getValue(); + stackStream << "\nJS Stack Trace:\n" << StrType(cx, stackStr).getValue(); } From c5445298e3354f95c8cf0cc86a49fec70b621e4f Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 20 Mar 2024 16:04:07 -0400 Subject: [PATCH 147/170] chore(CI): add core dumps to CI --- .github/workflows/test-and-publish.yaml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index bfffbbdd..e25c9f3e 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -167,13 +167,36 @@ jobs: with: name: docs-${{ github.run_id }}-${{ github.sha }} path: ./build/docs/html/ + - name: Set cores to get stored in /cores + run: | + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + # TODO (Caleb Aikens) figure out how to get Windows core dumps + sudo mkdir /cores + sudo chmod 777 /cores + # Core filenames will be of the form executable.pid.timestamp: + sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern' + fi - name: Run Python tests (pytest) run: | + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + # TODO (Caleb Aikens) figure out how to get Windows core dumps + ulimit -c unlimited + fi poetry run python -m pip install --force-reinstall --verbose ./dist/* poetry run python -m pytest tests/python - name: Run JS tests (peter-jr) run: | + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + # TODO (Caleb Aikens) figure out how to get Windows core dumps + ulimit -c unlimited + fi poetry run bash ./peter-jr ./tests/js/ + - uses: actions/upload-artifact@v3 + if: ${{ failure() && matrix.os != 'windows-2019' }} # Run only if something went wrong + # TODO (Caleb Aikens) figure out how to get Windows core dumps + with: + name: cores + path: /cores sdist: runs-on: ubuntu-20.04 steps: From a2884c134ea9102f796f105d13be8e1de3ffcd2f Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 20 Mar 2024 16:12:35 -0400 Subject: [PATCH 148/170] chore(CI): fix test-and-publish.yaml syntax error --- .github/workflows/test-and-publish.yaml | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index e25c9f3e..a3e3bf11 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -168,14 +168,14 @@ jobs: name: docs-${{ github.run_id }}-${{ github.sha }} path: ./build/docs/html/ - name: Set cores to get stored in /cores - run: | - if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; - # TODO (Caleb Aikens) figure out how to get Windows core dumps - sudo mkdir /cores - sudo chmod 777 /cores - # Core filenames will be of the form executable.pid.timestamp: - sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern' - fi + run: | + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + # TODO (Caleb Aikens) figure out how to get Windows core dumps + sudo mkdir /cores + sudo chmod 777 /cores + # Core filenames will be of the form executable.pid.timestamp: + sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern' + fi - name: Run Python tests (pytest) run: | if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; @@ -192,11 +192,11 @@ jobs: fi poetry run bash ./peter-jr ./tests/js/ - uses: actions/upload-artifact@v3 - if: ${{ failure() && matrix.os != 'windows-2019' }} # Run only if something went wrong - # TODO (Caleb Aikens) figure out how to get Windows core dumps - with: - name: cores - path: /cores + if: ${{ failure() && matrix.os != 'windows-2019' }} # Run only if something went wrong + # TODO (Caleb Aikens) figure out how to get Windows core dumps + with: + name: cores + path: /cores sdist: runs-on: ubuntu-20.04 steps: From 27019d2e48f73acafc835cdf5642fed19a337aaa Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Wed, 20 Mar 2024 16:29:47 -0400 Subject: [PATCH 149/170] added get method test --- tests/python/test_dict_methods.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/python/test_dict_methods.py b/tests/python/test_dict_methods.py index f70c3854..6bb6a85f 100644 --- a/tests/python/test_dict_methods.py +++ b/tests/python/test_dict_methods.py @@ -443,4 +443,9 @@ def test_items_mapping(): dishes = pm.eval("({'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500})") items = dishes.items() assert str(items.mapping) == "{'eggs': 2.0, 'sausage': 1.0, 'bacon': 1.0, 'spam': 500.0}" - assert items.mapping['spam'] == 500 \ No newline at end of file + assert items.mapping['spam'] == 500 + +#get method +def test_get_method(): + dishes = pm.eval("({'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500})") + assert dishes.get('eggs') == 2 \ No newline at end of file From 86805ba5430182589ca0d483f829a0cfc8a30941 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 20 Mar 2024 16:34:28 -0400 Subject: [PATCH 150/170] chore(CI): fix missing then in test-and-publish.yaml --- .github/workflows/test-and-publish.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index a3e3bf11..8e3ea41d 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -169,7 +169,7 @@ jobs: path: ./build/docs/html/ - name: Set cores to get stored in /cores run: | - if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then # TODO (Caleb Aikens) figure out how to get Windows core dumps sudo mkdir /cores sudo chmod 777 /cores @@ -178,7 +178,7 @@ jobs: fi - name: Run Python tests (pytest) run: | - if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then # TODO (Caleb Aikens) figure out how to get Windows core dumps ulimit -c unlimited fi @@ -186,7 +186,7 @@ jobs: poetry run python -m pytest tests/python - name: Run JS tests (peter-jr) run: | - if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; + if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then # TODO (Caleb Aikens) figure out how to get Windows core dumps ulimit -c unlimited fi From df32a3c5839423a7126c8c3dfae916f1d5631015 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Wed, 20 Mar 2024 17:07:32 -0400 Subject: [PATCH 151/170] allow object key to shadow method --- src/JSObjectProxy.cc | 9 ++++++++- tests/python/test_dict_methods.py | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index f368ea93..f044083b 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -111,7 +111,14 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { - return PyObject_GenericGetAttr((PyObject *)self, key); + // just make sure no property is shadowing a method by name + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); + if (!value.isUndefined()) { + return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); + } else { + return PyObject_GenericGetAttr((PyObject *)self, key); + } } } } diff --git a/tests/python/test_dict_methods.py b/tests/python/test_dict_methods.py index 6bb6a85f..69cf041b 100644 --- a/tests/python/test_dict_methods.py +++ b/tests/python/test_dict_methods.py @@ -448,4 +448,10 @@ def test_items_mapping(): #get method def test_get_method(): dishes = pm.eval("({'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500})") - assert dishes.get('eggs') == 2 \ No newline at end of file + assert dishes.get('eggs') == 2 + +#get method shadowing +def test_method_shadowing(): + jsObj = pm.eval("({get: 'value'})") + assert jsObj.get == 'value' + assert jsObj['get'] == 'value' \ No newline at end of file From 78f993894e1f51a5aa88db2fc6cbb0f688035641 Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Wed, 20 Mar 2024 17:48:16 -0400 Subject: [PATCH 152/170] do distinguish between dict . and [] operators --- include/JSObjectProxy.hh | 13 ++++++++++-- src/JSObjectProxy.cc | 33 +++++++++++++++++++++---------- tests/python/test_dict_methods.py | 9 +++++++-- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index d54c8867..eb511883 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -48,7 +48,7 @@ public: static Py_ssize_t JSObjectProxy_length(JSObjectProxy *self); /** - * @brief Getter method (.mp_subscript), returns a value from the JSObjectProxy given a key, used by several built-in python methods as well as the [] operator + * @brief Getter method, returns a value from the JSObjectProxy given a key, used by several built-in python methods as well as the . operator * * @param self - The JSObjectProxy * @param key - The key for the value in the JSObjectProxy @@ -56,6 +56,15 @@ public: */ static PyObject *JSObjectProxy_get(JSObjectProxy *self, PyObject *key); + /** + * @brief Getter method (.mp_subscript), returns a value from the JSObjectProxy given a key, used by the [] operator + * + * @param self - The JSObjectProxy + * @param key - The key for the value in the JSObjectProxy + * @return PyObject* NULL on exception, the corresponding value otherwise + */ + static PyObject *JSObjectProxy_get_subscript(JSObjectProxy *self, PyObject *key); + /** * @brief Test method (.sq_contains), returns whether a key exists, used by the in operator * @@ -289,7 +298,7 @@ PyDoc_STRVAR(dict_values__doc__, */ static PyMappingMethods JSObjectProxy_mapping_methods = { .mp_length = (lenfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_length, - .mp_subscript = (binaryfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get, + .mp_subscript = (binaryfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get_subscript, .mp_ass_subscript = (objobjargproc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign }; diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index f044083b..853d342f 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -64,7 +64,7 @@ Py_ssize_t JSObjectProxyMethodDefinitions::JSObjectProxy_length(JSObjectProxy *s return props.length(); } -static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId id) { +static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId id, bool checkPropertyShadowsMethod) { // look through the methods for dispatch for (size_t index = 0;; index++) { const char *methodName = JSObjectProxyType.tp_methods[index].ml_name; @@ -111,14 +111,16 @@ static inline PyObject *getKey(JSObjectProxy *self, PyObject *key, JS::HandleId } else { if (strcmp(methodName, PyUnicode_AsUTF8(key)) == 0) { - // just make sure no property is shadowing a method by name - JS::RootedValue value(GLOBAL_CX); - JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); - if (!value.isUndefined()) { - return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); - } else { - return PyObject_GenericGetAttr((PyObject *)self, key); + if (checkPropertyShadowsMethod) { + // just make sure no property is shadowing a method by name + JS::RootedValue value(GLOBAL_CX); + JS_GetPropertyById(GLOBAL_CX, *(self->jsObject), id, &value); + if (!value.isUndefined()) { + return pyTypeFactory(GLOBAL_CX, value)->getPyObject(); + } } + + return PyObject_GenericGetAttr((PyObject *)self, key); } } } @@ -132,7 +134,18 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self, return NULL; } - return getKey(self, key, id); + return getKey(self, key, id, false); +} + +PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get_subscript(JSObjectProxy *self, PyObject *key) +{ + JS::RootedId id(GLOBAL_CX); + if (!keyToId(key, &id)) { + PyErr_SetString(PyExc_AttributeError, "JSObjectProxy property name must be of type str or int"); + return NULL; + } + + return getKey(self, key, id, true); } int JSObjectProxyMethodDefinitions::JSObjectProxy_contains(JSObjectProxy *self, PyObject *key) @@ -589,7 +602,7 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method(JSObje return NULL; } - PyObject *value = getKey(self, key, id); + PyObject *value = getKey(self, key, id, true); if (value == Py_None) { assignKeyValue(self, key, id, default_value); Py_XINCREF(default_value); diff --git a/tests/python/test_dict_methods.py b/tests/python/test_dict_methods.py index 69cf041b..36694856 100644 --- a/tests/python/test_dict_methods.py +++ b/tests/python/test_dict_methods.py @@ -55,7 +55,12 @@ def test_setdefault_no_params(): assert (False) except Exception as e: assert str(type(e)) == "" - assert str(e) == "setdefault expected at least 1 argument, got 0" + assert str(e) == "setdefault expected at least 1 argument, got 0" + +def test_setdefault_with_shadowing(): + jsObj = pm.eval("({get: 'value'})") + a = jsObj.setdefault("get", "val") + assert a == 'value' #pop def test_pop_found(): @@ -453,5 +458,5 @@ def test_get_method(): #get method shadowing def test_method_shadowing(): jsObj = pm.eval("({get: 'value'})") - assert jsObj.get == 'value' + assert repr(jsObj.get).__contains__(" Date: Thu, 21 Mar 2024 09:30:32 -0400 Subject: [PATCH 153/170] chore(CI): add tmate debugging to Actions runner --- .github/workflows/test-and-publish.yaml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 8e3ea41d..60417ab2 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -8,6 +8,12 @@ on: - '*' workflow_call: workflow_dispatch: + inputs: + debug_enabled: + type: boolean + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: false pull_request: env: @@ -192,11 +198,18 @@ jobs: fi poetry run bash ./peter-jr ./tests/js/ - uses: actions/upload-artifact@v3 - if: ${{ failure() && matrix.os != 'windows-2019' }} # Run only if something went wrong + if: ${{ matrix.os != 'windows-2019' }} # TODO (Caleb Aikens) figure out how to get Windows core dumps with: name: cores path: /cores + # Enable tmate debugging of manually-triggered workflows if the input option was provided + - name: SSH debug session + if: ${{ (success() || failure()) && github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} + uses: mxschmitt/action-tmate@v3 + with: + detached: true + limit-access-to-actor: true sdist: runs-on: ubuntu-20.04 steps: From 73e57c8b43dab93dbe20507c439e9805aee245cf Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 09:34:12 -0400 Subject: [PATCH 154/170] chore(CI): add -p flag to mkdir /cores --- .github/workflows/test-and-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 60417ab2..55673c29 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -177,7 +177,7 @@ jobs: run: | if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then # TODO (Caleb Aikens) figure out how to get Windows core dumps - sudo mkdir /cores + sudo mkdir -p /cores sudo chmod 777 /cores # Core filenames will be of the form executable.pid.timestamp: sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern' From fd78561e7d4d0b05201f95a52f4f7e05a21b683d Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 09:45:47 -0400 Subject: [PATCH 155/170] chore(CI): upgrade deprecated actions --- .github/workflows/test-and-publish.yaml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 55673c29..fa2f6614 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -37,13 +37,13 @@ jobs: python_version: [ '3.10' ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} - name: Cache spidermonkey build id: cache-spidermonkey - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./_spidermonkey_install/* @@ -60,10 +60,10 @@ jobs: build-spidermonkey-win: runs-on: windows-2019 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Cache spidermonkey build id: cache-spidermonkey - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./_spidermonkey_install/* @@ -110,12 +110,12 @@ jobs: python_version: '3.9' runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 # fetch all history for all branches and tags # poetry-dynamic-versioning needs git tags to produce the correct version number - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python_version }} - name: Setup Poetry @@ -144,7 +144,7 @@ jobs: poetry install --no-root --only=dev echo "Installed Dependencies" - name: Use cached spidermonkey build - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ./_spidermonkey_install/* @@ -213,10 +213,10 @@ jobs: sdist: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.9' - name: Setup Poetry @@ -239,7 +239,7 @@ jobs: if: ${{ success() && github.event_name == 'push' && contains(github.ref, 'refs/tags/') }} steps: # no need to checkout - - uses: actions/setup-python@v4 + - uses: actions/setup-python@v5 with: python-version: '3.9' - run: pip install twine From 6c477489b6e252fd0127f5cb230b6648daa8093c Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 10:03:14 -0400 Subject: [PATCH 156/170] chore(CI): add osname and python version to core dump --- .github/workflows/test-and-publish.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index fa2f6614..640ade1c 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -179,8 +179,12 @@ jobs: # TODO (Caleb Aikens) figure out how to get Windows core dumps sudo mkdir -p /cores sudo chmod 777 /cores - # Core filenames will be of the form executable.pid.timestamp: - sudo bash -c 'echo "/cores/%e.%p.%t" > /proc/sys/kernel/core_pattern' + # Core filenames will be of the form osname.pythonversion.executable.pid.timestamp: + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + sudo bash -c 'echo "/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%t" > /proc/sys/kernel/core_pattern' + else + sudo sysctl kernel.core_pattern="/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%y" + fi fi - name: Run Python tests (pytest) run: | From c69ea53f5beb13bb7b296bc0156b804091899162 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 10:06:15 -0400 Subject: [PATCH 157/170] chore(CI): upgrade deprecated upload-artifact action --- .github/workflows/test-and-publish.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 640ade1c..b7aae93e 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -163,12 +163,12 @@ jobs: poetry build --format=wheel ls -lah ./dist/ - name: Upload wheel as CI artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: wheel-${{ github.run_id }}-${{ github.sha }} path: ./dist/ - name: Upload Doxygen-generated docs as CI artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: ${{ matrix.os == 'ubuntu-20.04' && matrix.python_version == '3.11' }} # making sure we only upload once with: name: docs-${{ github.run_id }}-${{ github.sha }} @@ -201,7 +201,7 @@ jobs: ulimit -c unlimited fi poetry run bash ./peter-jr ./tests/js/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: ${{ matrix.os != 'windows-2019' }} # TODO (Caleb Aikens) figure out how to get Windows core dumps with: @@ -233,7 +233,7 @@ jobs: poetry build --format=sdist ls -lah ./dist/ - name: Upload sdist as CI artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: wheel-${{ github.run_id }}-${{ github.sha }} path: ./dist/ From c2fdfa8883615e1c918bd3432f4b6801d28ed4ba Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 10:08:13 -0400 Subject: [PATCH 158/170] chore(CI): fix core dump names for Mac --- .github/workflows/test-and-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index b7aae93e..7f691fdf 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -183,7 +183,7 @@ jobs: if [[ "$OSTYPE" == "linux-gnu"* ]]; then sudo bash -c 'echo "/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%t" > /proc/sys/kernel/core_pattern' else - sudo sysctl kernel.core_pattern="/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%y" + sudo sysctl kern.corefile="/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%y" fi fi - name: Run Python tests (pytest) From 1b444e0e5ea5c0f75bade96c97e3c8247cecaad1 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 10:15:46 -0400 Subject: [PATCH 159/170] chore(CI): name core dump artifact in Github Actions --- .github/workflows/test-and-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 7f691fdf..4affaaaf 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -205,7 +205,7 @@ jobs: if: ${{ matrix.os != 'windows-2019' }} # TODO (Caleb Aikens) figure out how to get Windows core dumps with: - name: cores + name: cores-${{ matrix.os }}-${ matrix.python_version } path: /cores # Enable tmate debugging of manually-triggered workflows if the input option was provided - name: SSH debug session From b944db4257171fabb83b102c8dbfd32ce93ec3ec Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 10:23:43 -0400 Subject: [PATCH 160/170] Revert "chore(CI): upgrade deprecated upload-artifact action" This reverts commit c69ea53f5beb13bb7b296bc0156b804091899162. --- .github/workflows/test-and-publish.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 4affaaaf..d8f31992 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -163,12 +163,12 @@ jobs: poetry build --format=wheel ls -lah ./dist/ - name: Upload wheel as CI artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: wheel-${{ github.run_id }}-${{ github.sha }} path: ./dist/ - name: Upload Doxygen-generated docs as CI artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 if: ${{ matrix.os == 'ubuntu-20.04' && matrix.python_version == '3.11' }} # making sure we only upload once with: name: docs-${{ github.run_id }}-${{ github.sha }} @@ -201,7 +201,7 @@ jobs: ulimit -c unlimited fi poetry run bash ./peter-jr ./tests/js/ - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v3 if: ${{ matrix.os != 'windows-2019' }} # TODO (Caleb Aikens) figure out how to get Windows core dumps with: @@ -233,7 +233,7 @@ jobs: poetry build --format=sdist ls -lah ./dist/ - name: Upload sdist as CI artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: wheel-${{ github.run_id }}-${{ github.sha }} path: ./dist/ From 96cb24d29f571aef0344d96fe82b1d055c3e4343 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 10:56:36 -0400 Subject: [PATCH 161/170] chore(CI): fix core dump artifact name, only upload cores on failure --- .github/workflows/test-and-publish.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index d8f31992..73234b3a 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -201,11 +201,12 @@ jobs: ulimit -c unlimited fi poetry run bash ./peter-jr ./tests/js/ - - uses: actions/upload-artifact@v3 - if: ${{ matrix.os != 'windows-2019' }} + - name: Upload core dumps as CI artifacts + uses: actions/upload-artifact@v3 + if: ${{ matrix.os != 'windows-2019' && failure() }} # TODO (Caleb Aikens) figure out how to get Windows core dumps with: - name: cores-${{ matrix.os }}-${ matrix.python_version } + name: cores-${{ matrix.os }}-${{ matrix.python_version }} path: /cores # Enable tmate debugging of manually-triggered workflows if the input option was provided - name: SSH debug session From 1e9ec276c3e276606f7726e46d075ede47325239 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 11:09:55 -0400 Subject: [PATCH 162/170] chore(CI): run JS tests even if python tests fail --- .github/workflows/test-and-publish.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 73234b3a..3200e0e8 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -195,6 +195,7 @@ jobs: poetry run python -m pip install --force-reinstall --verbose ./dist/* poetry run python -m pytest tests/python - name: Run JS tests (peter-jr) + if: ${{ (success() || failure()) run: | if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then # TODO (Caleb Aikens) figure out how to get Windows core dumps From 216545b71679334a52be6a766758b5b57b696280 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 11:10:44 -0400 Subject: [PATCH 163/170] chore(CI): fix yaml syntax error --- .github/workflows/test-and-publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 3200e0e8..6b4edece 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -195,7 +195,7 @@ jobs: poetry run python -m pip install --force-reinstall --verbose ./dist/* poetry run python -m pytest tests/python - name: Run JS tests (peter-jr) - if: ${{ (success() || failure()) + if: ${{ (success() || failure()) }} run: | if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then # TODO (Caleb Aikens) figure out how to get Windows core dumps From 398baa874fe1b8a0f753134a8230aeb28c604cda Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 11:14:19 -0400 Subject: [PATCH 164/170] chore(CI): add dump_cores input to CI workflow dispatch --- .github/workflows/test-and-publish.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index 6b4edece..c1a73444 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -14,6 +14,11 @@ on: description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' required: false default: false + dump_cores: + type: boolean + description: 'Include core dumps in CI artifacts' + required: false + default: false pull_request: env: @@ -204,7 +209,7 @@ jobs: poetry run bash ./peter-jr ./tests/js/ - name: Upload core dumps as CI artifacts uses: actions/upload-artifact@v3 - if: ${{ matrix.os != 'windows-2019' && failure() }} + if: ${{ matrix.os != 'windows-2019' && github.event_name == 'workflow_dispatch' && inputs.dump_cores }} # TODO (Caleb Aikens) figure out how to get Windows core dumps with: name: cores-${{ matrix.os }}-${{ matrix.python_version }} From ee8c33a05228edcd546d557c35ca74e97f51d99b Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 12:28:35 -0400 Subject: [PATCH 165/170] chore(CI): only set core names if cores will be dumped --- .github/workflows/test-and-publish.yaml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-and-publish.yaml b/.github/workflows/test-and-publish.yaml index c1a73444..ba8356ed 100644 --- a/.github/workflows/test-and-publish.yaml +++ b/.github/workflows/test-and-publish.yaml @@ -179,17 +179,16 @@ jobs: name: docs-${{ github.run_id }}-${{ github.sha }} path: ./build/docs/html/ - name: Set cores to get stored in /cores + if: ${{ matrix.os != 'windows-2019' && github.event_name == 'workflow_dispatch' && inputs.dump_cores }} + # TODO (Caleb Aikens) figure out how to get Windows core dumps run: | - if [[ "$OSTYPE" == "linux-gnu"* || "$OSTYPE" == "darwin"* ]]; then - # TODO (Caleb Aikens) figure out how to get Windows core dumps - sudo mkdir -p /cores - sudo chmod 777 /cores - # Core filenames will be of the form osname.pythonversion.executable.pid.timestamp: - if [[ "$OSTYPE" == "linux-gnu"* ]]; then - sudo bash -c 'echo "/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%t" > /proc/sys/kernel/core_pattern' - else - sudo sysctl kern.corefile="/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%y" - fi + sudo mkdir -p /cores + sudo chmod 777 /cores + # Core filenames will be of the form osname.pythonversion.executable.pid.timestamp: + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + sudo bash -c 'echo "/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%t" > /proc/sys/kernel/core_pattern' + else + sudo sysctl kern.corefile="/cores/${OSTYPE}.$(poetry run python --version).%e.%p.%y" fi - name: Run Python tests (pytest) run: | From 3cb7ebcf125426840ac7e47d2a28f28a00ad3bea Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Thu, 21 Mar 2024 14:20:24 -0400 Subject: [PATCH 166/170] no need for cpython GC compatibility for these --- include/JSArrayProxy.hh | 18 ------------------ include/JSObjectProxy.hh | 18 ------------------ src/JSArrayProxy.cc | 12 ------------ src/JSObjectProxy.cc | 12 ------------ src/modules/pythonmonkey/pythonmonkey.cc | 8 ++------ 5 files changed, 2 insertions(+), 66 deletions(-) diff --git a/include/JSArrayProxy.hh b/include/JSArrayProxy.hh index 592778d7..5f66836f 100644 --- a/include/JSArrayProxy.hh +++ b/include/JSArrayProxy.hh @@ -164,24 +164,6 @@ public: */ static PyObject *JSArrayProxy_clear_method(JSArrayProxy *self); - /** - * @brief .tp_clear method - * - * @param self - The JSArrayProxy - * @return 0 on success - */ - static int JSArrayProxy_clear(JSArrayProxy *self); - - /** - * @brief .tp_traverse method - * - * @param self - The JSArrayProxy - * @param visitproc - The function to be applied on each element of the list - * @param arg - The argument to the visit function - * @return 0 on success - */ - static int JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg); - /** * @brief copy method * diff --git a/include/JSObjectProxy.hh b/include/JSObjectProxy.hh index eb511883..ba7c97eb 100644 --- a/include/JSObjectProxy.hh +++ b/include/JSObjectProxy.hh @@ -84,24 +84,6 @@ public: */ static int JSObjectProxy_assign(JSObjectProxy *self, PyObject *key, PyObject *value); - /** - * @brief .tp_traverse method - * - * @param self - The JSObjectProxy - * @param visitproc - The function to be applied on each element of the dict - * @param arg - The argument to the visit function - * @return 0 on success - */ - static int JSObjectProxy_traverse(JSObjectProxy *self, visitproc visit, void *arg); - - /** - * @brief clear method - * - * @param self - The JSObjectProxy - * @return 0 on success - */ - static int JSObjectProxy_clear(JSObjectProxy *self); - /** * @brief Comparison method (.tp_richcompare), returns appropriate boolean given a comparison operator and other pyobject * diff --git a/src/JSArrayProxy.cc b/src/JSArrayProxy.cc index 53f2f21c..d57e0107 100644 --- a/src/JSArrayProxy.cc +++ b/src/JSArrayProxy.cc @@ -29,7 +29,6 @@ void JSArrayProxyMethodDefinitions::JSArrayProxy_dealloc(JSArrayProxy *self) { self->jsArray->set(nullptr); delete self->jsArray; - PyObject_GC_UnTrack(self); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -736,17 +735,6 @@ PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_clear_method(JSArrayProxy Py_RETURN_NONE; } -int JSArrayProxyMethodDefinitions::JSArrayProxy_clear(JSArrayProxy *self) { - JS::SetArrayLength(GLOBAL_CX, *(self->jsArray), 0); - return 0; -} - -int JSArrayProxyMethodDefinitions::JSArrayProxy_traverse(JSArrayProxy *self, visitproc visit, void *arg) { - // Nothing to be done - // TODO do we need to iterate through the list and call traverse on proxied PyObjects? - return 0; -} - PyObject *JSArrayProxyMethodDefinitions::JSArrayProxy_copy(JSArrayProxy *self) { JS::Rooted> jArgs(GLOBAL_CX); jArgs[0].setInt32(0); diff --git a/src/JSObjectProxy.cc b/src/JSObjectProxy.cc index 853d342f..a5424cc9 100644 --- a/src/JSObjectProxy.cc +++ b/src/JSObjectProxy.cc @@ -49,7 +49,6 @@ void JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc(JSObjectProxy *self) { self->jsObject->set(nullptr); delete self->jsObject; - PyObject_GC_UnTrack(self); Py_TYPE(self)->tp_free((PyObject *)self); } @@ -183,17 +182,6 @@ int JSObjectProxyMethodDefinitions::JSObjectProxy_assign(JSObjectProxy *self, Py return 0; } -int JSObjectProxyMethodDefinitions::JSObjectProxy_traverse(JSObjectProxy *self, visitproc visit, void *arg) { - // Nothing to be done - // TODO do we need to iterate through the dict and call traverse on proxied PyObjects? - return 0; -} - -int JSObjectProxyMethodDefinitions::JSObjectProxy_clear(JSObjectProxy *self) { - JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method(self); - return 0; -} - PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare(JSObjectProxy *self, PyObject *other, int op) { if (op != Py_EQ && op != Py_NE) { diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 930aa9f2..4e89f90e 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -104,10 +104,8 @@ PyTypeObject JSObjectProxyType = { .tp_as_mapping = &JSObjectProxy_mapping_methods, .tp_getattro = (getattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_get, .tp_setattro = (setattrofunc)JSObjectProxyMethodDefinitions::JSObjectProxy_assign, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DICT_SUBCLASS | Py_TPFLAGS_HAVE_GC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DICT_SUBCLASS, .tp_doc = PyDoc_STR("Javascript Object proxy dict"), - .tp_traverse = (traverseproc)JSObjectProxyMethodDefinitions::JSObjectProxy_traverse, - .tp_clear = (inquiry)JSObjectProxyMethodDefinitions::JSObjectProxy_clear, .tp_richcompare = (richcmpfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare, .tp_iter = (getiterfunc)JSObjectProxyMethodDefinitions::JSObjectProxy_iter, .tp_methods = JSObjectProxy_methods, @@ -156,10 +154,8 @@ PyTypeObject JSArrayProxyType = { .tp_as_sequence = &JSArrayProxy_sequence_methods, .tp_as_mapping = &JSArrayProxy_mapping_methods, .tp_getattro = (getattrofunc)JSArrayProxyMethodDefinitions::JSArrayProxy_get, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS | Py_TPFLAGS_HAVE_GC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_LIST_SUBCLASS, .tp_doc = PyDoc_STR("Javascript Array proxy list"), - .tp_traverse = (traverseproc)JSArrayProxyMethodDefinitions::JSArrayProxy_traverse, - .tp_clear = (inquiry)JSArrayProxyMethodDefinitions::JSArrayProxy_clear, .tp_richcompare = (richcmpfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_richcompare, .tp_iter = (getiterfunc)JSArrayProxyMethodDefinitions::JSArrayProxy_iter, .tp_methods = JSArrayProxy_methods, From 6452123bd023e98fce9ef462e5d265097288c3a0 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Thu, 21 Mar 2024 14:35:10 -0400 Subject: [PATCH 167/170] chore(meta): add logging of python libs during compilation --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cbe02d0..50f9e6de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) set(Python_FIND_VIRTUALENV FIRST) # (require cmake >= v3.15 and this is the default) use the Python version configured by pyenv if available set(PYTHON_LIBRARIES ${Python_LIBRARIES}) set(PYTHON_INCLUDE_DIR ${Python_INCLUDE_DIRS}) + message("Linux - Using Python:${Python_VERSION_MAJOR}.${Python_VERSION_MINOR} - Libraries:${PYTHON_LIBRARIES} - IncludeDirs: ${PYTHON_INCLUDE_DIR}") find_package(SpiderMonkey REQUIRED) elseif(WIN32) find_package(PythonInterp 3.8 REQUIRED) From a2d818a7151743cd7a430a3b130dd79e313fb50e Mon Sep 17 00:00:00 2001 From: Philippe Laporte Date: Wed, 27 Mar 2024 13:15:37 -0400 Subject: [PATCH 168/170] post-merge fixup --- src/PyEventLoop.cc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/PyEventLoop.cc b/src/PyEventLoop.cc index ca51c9fe..f68196a0 100644 --- a/src/PyEventLoop.cc +++ b/src/PyEventLoop.cc @@ -18,11 +18,8 @@ */ static PyObject *eventLoopJobWrapper(PyObject *jobFn, PyObject *Py_UNUSED(_)) { PyObject *ret = PyObject_CallObject(jobFn, NULL); + Py_XDECREF(ret); // don't care about its return value PyEventLoop::_locker->decCounter(); - if (!ret) { - return NULL; - } - Py_DECREF(ret); if (PyErr_Occurred()) { return NULL; } else { From fba9c25e18d4cccc4b8ded026364b3c4ee3aa102 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 3 Apr 2024 09:48:32 -0400 Subject: [PATCH 169/170] fix(JobQueue): allocate callbacks on the heap and delete global before destroying the context --- include/JobQueue.hh | 6 +++--- src/JobQueue.cc | 12 ++++++++++-- src/modules/pythonmonkey/pythonmonkey.cc | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/JobQueue.hh b/include/JobQueue.hh index be5d6617..ea8aaac5 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -27,8 +27,8 @@ class JobQueue : public JS::JobQueue { // JS::JobQueue methods. // public: -explicit JobQueue(JSContext *cx) : finalizationRegistryCallbacks(cx) {} -~JobQueue() = default; +explicit JobQueue(JSContext *cx); +~JobQueue(); /** * @brief Ask the embedding for the incumbent global. @@ -92,7 +92,7 @@ bool runFinalizationRegistryCallbacks(JSContext *cx); private: using FunctionVector = JS::GCVector; -JS::PersistentRooted finalizationRegistryCallbacks; +JS::PersistentRooted *finalizationRegistryCallbacks; /** * @brief Capture this JobQueue's current job queue as a SavedJobQueue and return it, diff --git a/src/JobQueue.cc b/src/JobQueue.cc index f05a2abe..ce6ce38d 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -11,6 +11,14 @@ #include +JobQueue::JobQueue(JSContext *cx) { + finalizationRegistryCallbacks = new JS::PersistentRooted(cx); +} + +JobQueue::~JobQueue() { + delete finalizationRegistryCallbacks; +} + JSObject *JobQueue::getIncumbentGlobal(JSContext *cx) { return JS::CurrentGlobalOrNull(cx); } @@ -108,13 +116,13 @@ bool JobQueue::dispatchToEventLoop(void *closure, JS::Dispatchable *dispatchable } void JobQueue::queueFinalizationRegistryCallback(JSFunction *callback) { - mozilla::Unused << finalizationRegistryCallbacks.append(callback); + mozilla::Unused << finalizationRegistryCallbacks->append(callback); } bool JobQueue::runFinalizationRegistryCallbacks(JSContext *cx) { bool ranCallbacks = false; JS::Rooted callbacks(cx); - std::swap(callbacks.get(), finalizationRegistryCallbacks.get()); + std::swap(callbacks.get(), finalizationRegistryCallbacks->get()); for (JSFunction *f: callbacks) { JS::ExposeObjectToActiveJS(JS_GetFunctionObject(f)); diff --git a/src/modules/pythonmonkey/pythonmonkey.cc b/src/modules/pythonmonkey/pythonmonkey.cc index 81d35a03..5e9961db 100644 --- a/src/modules/pythonmonkey/pythonmonkey.cc +++ b/src/modules/pythonmonkey/pythonmonkey.cc @@ -263,8 +263,8 @@ PyTypeObject JSObjectItemsProxyType = { static void cleanup() { delete autoRealm; - if (GLOBAL_CX) JS_DestroyContext(GLOBAL_CX); delete global; + if (GLOBAL_CX) JS_DestroyContext(GLOBAL_CX); delete JOB_QUEUE; JS_ShutDown(); } From 74ceb71eabd2bf2e9736a064beaf33fd0cb3ca02 Mon Sep 17 00:00:00 2001 From: Caleb Aikens Date: Wed, 3 Apr 2024 10:13:11 -0400 Subject: [PATCH 170/170] fix(JobQueue): don't double-free finalizationRegistryCallbacks vector --- include/JobQueue.hh | 2 +- src/JobQueue.cc | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/include/JobQueue.hh b/include/JobQueue.hh index ea8aaac5..bb5561ce 100644 --- a/include/JobQueue.hh +++ b/include/JobQueue.hh @@ -28,7 +28,7 @@ class JobQueue : public JS::JobQueue { // public: explicit JobQueue(JSContext *cx); -~JobQueue(); +~JobQueue() = default; /** * @brief Ask the embedding for the incumbent global. diff --git a/src/JobQueue.cc b/src/JobQueue.cc index ce6ce38d..de63538c 100644 --- a/src/JobQueue.cc +++ b/src/JobQueue.cc @@ -15,10 +15,6 @@ JobQueue::JobQueue(JSContext *cx) { finalizationRegistryCallbacks = new JS::PersistentRooted(cx); } -JobQueue::~JobQueue() { - delete finalizationRegistryCallbacks; -} - JSObject *JobQueue::getIncumbentGlobal(JSContext *cx) { return JS::CurrentGlobalOrNull(cx); }