forked from Distributive-Network/PythonMonkey
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathJSFunctionProxy.cc
More file actions
175 lines (150 loc) · 5.51 KB
/
JSFunctionProxy.cc
File metadata and controls
175 lines (150 loc) · 5.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* @file JSFunctionProxy.cc
* @author Caleb Aikens (caleb@distributive.network)
* @brief JSFunctionProxy is a custom C-implemented python type that derives from PyCFunctionObject. It acts as a proxy for JSFunctions from Spidermonkey, and behaves like a function would.
* @version 0.1
* @date 2023-07-05
*
* Copyright (c) 2023 Distributive Corp.
*
*/
#include "include/JSFunctionProxy.hh"
#include "include/JSObjectProxy.hh"
#include "include/StrType.hh"
#include "include/jsTypeFactory.hh"
#include "include/pyTypeFactory.hh"
#include "include/setSpiderMonkeyException.hh"
#include <jsapi.h>
#include <js/Equality.h>
#include <js/Exception.h>
#include <js/Value.h>
#include <Python.h>
static PyMethodDef callJSFuncDef = {"JSFunctionProxy", NULL, METH_VARARGS | METH_KEYWORDS, NULL};
void JSFunctionProxyMethodDefinitions::JSFunctionProxy_dealloc(JSFunctionProxy *self)
{
delete self->jsFunction;
(Py_TYPE(self))->tp_base->tp_dealloc((PyObject *)self);
return;
}
PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
{
JSFunctionProxy *self = PyObject_GC_New(JSFunctionProxy, &JSFunctionProxyType);
if (!self) {
return NULL;
}
self->func.m_ml = &callJSFuncDef;
self->func.m_module = NULL;
self->func.m_self = NULL;
self->func.m_weakreflist = NULL;
self->func.vectorcall = NULL;
self->jsFunction = new JS::PersistentRootedObject(GLOBAL_CX);
return (PyObject *)self;
}
int JSFunctionProxyMethodDefinitions::JSFunctionProxy_init(JSFunctionProxy *self, PyObject *args, PyObject *kwds)
{
// make fresh JSFunction for proxy
self->jsFunction->set(nullptr);
return 0;
}
PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_get(JSFunctionProxy *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
}
JS::RootedValue *value = new JS::RootedValue(GLOBAL_CX);
JS_GetPropertyById(GLOBAL_CX, *self->jsFunction, id, value);
JS::RootedObject *global = new JS::RootedObject(GLOBAL_CX, JS::GetNonCCWObjectGlobal(*self->jsFunction));
return pyTypeFactory(GLOBAL_CX, global, value)->getPyObject();
}
int JSFunctionProxyMethodDefinitions::JSFunctionProxy_assign(JSFunctionProxy *self, PyObject *key, PyObject *value)
{
JS::RootedId id(GLOBAL_CX);
if (!keyToId(key, &id)) { // invalid key
// TODO (Caleb Aikens): raise exception here
return -1;
}
if (value) { // we are setting a value
JS::RootedValue jValue(GLOBAL_CX, jsTypeFactory(GLOBAL_CX, value));
JS_SetPropertyById(GLOBAL_CX, *self->jsFunction, id, jValue);
} else { // we are deleting a value
JS::ObjectOpResult ignoredResult;
JS_DeletePropertyById(GLOBAL_CX, *self->jsFunction, id, ignoredResult);
}
return 0;
}
PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_richcompare(JSFunctionProxy *self, PyObject *other, int op)
{
if (op != Py_EQ && op != Py_NE) {
Py_RETURN_NOTIMPLEMENTED;
}
bool isEqual;
if (Py_TYPE((PyObject *)self) == Py_TYPE(other)) {
JS::RootedValue funcValSelf(GLOBAL_CX, JS::ObjectValue(**self->jsFunction));
JS::RootedValue funcValOther(GLOBAL_CX, JS::ObjectValue(**((JSFunctionProxy *)other)->jsFunction));
if (!JS::StrictlyEqual(GLOBAL_CX, funcValSelf, funcValOther, &isEqual)) {
// TODO (Caleb Aikens): raise exception here
return NULL;
}
}
else {
isEqual = false;
}
switch (op)
{
case Py_EQ:
return PyBool_FromLong(isEqual);
case Py_NE:
return PyBool_FromLong(!isEqual);
default:
return NULL;
}
}
PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_repr(JSFunctionProxy *self) {
JS::RootedValue funcVal(GLOBAL_CX, JS::ObjectValue(**self->jsFunction));
return StrType(GLOBAL_CX, JS_ValueToSource(GLOBAL_CX, funcVal)).getPyObject();
}
PyObject *JSFunctionProxyMethodDefinitions::JSFunctionProxy_call(JSFunctionProxy *self, PyObject *args, PyObject *kwargs) {
// @TODO (Caleb Aikens) handle kwargs
// @TODO (Caleb Aikens) handle bidirectional exceptions
JSContext *cx = GLOBAL_CX;
if (PyErr_Occurred()) {
return NULL;
}
if (JS_IsExceptionPending(cx)) {
setSpiderMonkeyException(cx);
return NULL;
}
JS::RootedObject *thisObj = new JS::RootedObject(cx);
if (self->func.m_self) { // set `this` to the self of the pyfunction if it has one
thisObj->set(&(jsTypeFactory(cx, self->func.m_self).toObject()));
}
else { // otherwise, default to the JS global object
thisObj->set(JS::GetNonCCWObjectGlobal(*self->jsFunction));
}
JS::RootedValue *jsFunc = new JS::RootedValue(GLOBAL_CX, JS::ObjectValue(**self->jsFunction));
JS::RootedVector<JS::Value> 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
}
if (!jsArgsVector.append(jsValue)) {
setSpiderMonkeyException(cx);
return NULL;
}
}
JS::HandleValueArray jsArgs(jsArgsVector);
JS::Rooted<JS::Value> *jsReturnVal = new JS::Rooted<JS::Value>(cx);
if (!JS_CallFunctionValue(cx, *thisObj, *jsFunc, jsArgs, jsReturnVal)) {
setSpiderMonkeyException(cx);
return NULL;
}
return pyTypeFactory(cx, thisObj, jsReturnVal)->getPyObject();
}
int JSFunctionProxyMethodDefinitions::JSFunctionProxy_traverse(JSFunctionProxy *self, visitproc visit, void *arg) {
Py_VISIT(self->func.m_self);
Py_VISIT(self->func.m_module);
return 0;
}