forked from Distributive-Network/PythonMonkey
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpyTypeFactory.cc
More file actions
193 lines (179 loc) · 6.53 KB
/
pyTypeFactory.cc
File metadata and controls
193 lines (179 loc) · 6.53 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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
* @file pyTypeFactory.cc
* @author Caleb Aikens (caleb@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
*
*/
#include "include/pyTypeFactory.hh"
#include "include/BoolType.hh"
#include "include/BufferType.hh"
#include "include/DateType.hh"
#include "include/DictType.hh"
#include "include/ExceptionType.hh"
#include "include/FloatType.hh"
#include "include/FuncType.hh"
#include "include/IntType.hh"
#include "include/jsTypeFactory.hh"
#include "include/ListType.hh"
#include "include/NoneType.hh"
#include "include/NullType.hh"
#include "include/PromiseType.hh"
#include "include/PyProxyHandler.hh"
#include "include/PyType.hh"
#include "include/setSpiderMonkeyException.hh"
#include "include/StrType.hh"
#include "include/TupleType.hh"
#include "include/modules/pythonmonkey/pythonmonkey.hh"
#include <jsapi.h>
#include <js/Object.h>
#include <js/ValueArray.h>
#include <Python.h>
// TODO (Caleb Aikens) get below properties
static PyMethodDef callJSFuncDef = {"JSFunctionCallable", callJSFunc, METH_VARARGS, NULL};
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::Rooted<JSObject *> *thisObj, JS::Rooted<JS::Value> *rval) {
if (rval->isUndefined()) {
return new NoneType();
}
else if (rval->isNull()) {
return new NullType();
}
else if (rval->isBoolean()) {
return new BoolType(rval->toBoolean());
}
else if (rval->isNumber()) {
return new FloatType(rval->toNumber());
}
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()) {
printf("symbol type is not handled by PythonMonkey yet");
}
else if (rval->isBigInt()) {
return new IntType(cx, rval->toBigInt());
}
else if (rval->isObject()) {
JS::Rooted<JSObject *> 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() == &PyListProxyHandler::family) { // this is one of our proxies for python lists
return new ListType(((PyListProxyHandler *)js::GetProxyHandler(obj))->pyObject);
}
}
js::ESClass cls;
JS::GetBuiltinClass(cx, obj, &cls);
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::Function: {
// 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));
PyObject *pyFunc = PyCFunction_New(&callJSFuncDef, jsCxThisFuncTuple);
FuncType *f = new FuncType(pyFunc);
memoizePyTypeAndGCThing(f, *rval); // TODO (Caleb Aikens) consider putting this in the FuncType constructor
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);
StrType *s = new StrType(cx, unboxed.toString());
memoizePyTypeAndGCThing(s, *rval); // TODO (Caleb Aikens) consider putting this in the StrType constructor
return s;
}
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);
}
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));
errorString += JS_EncodeStringToUTF8(cx, str).get();
PyErr_SetString(PyExc_TypeError, errorString.c_str());
return NULL;
}
static PyObject *callJSFunc(PyObject *jsCxThisFuncTuple, PyObject *args) {
// TODO (Caleb Aikens) convert PyObject *args to JS::Rooted<JS::ValueArray> 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<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
}
jsArgsVector.append(jsValue);
}
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();
}