1
1
/* *
2
2
* @file JSObjectProxy.cc
3
- * @author Caleb Aikens ([email protected] ) & Tom Tang ([email protected] )
3
+ * @author Caleb Aikens ([email protected] ), Tom Tang (xmader@distributive.network) and Philippe Laporte (philippe @distributive.network)
4
4
* @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.
5
5
* @version 0.1
6
6
* @date 2023-06-26
@@ -44,6 +44,8 @@ void JSObjectProxyMethodDefinitions::JSObjectProxy_dealloc(JSObjectProxy *self)
44
44
{
45
45
// TODO (Caleb Aikens): intentional override of PyDict_Type's tp_dealloc. Probably results in leaking dict memory
46
46
self->jsObject .set (nullptr );
47
+ PyObject_GC_UnTrack (self);
48
+ Py_TYPE (self)->tp_free ((PyObject *)self);
47
49
return ;
48
50
}
49
51
@@ -56,6 +58,9 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_new(PyTypeObject *subtyp
56
58
57
59
int JSObjectProxyMethodDefinitions::JSObjectProxy_init (JSObjectProxy *self, PyObject *args, PyObject *kwds)
58
60
{
61
+ if (PyDict_Type.tp_init ((PyObject *)self, args, kwds) < 0 ) {
62
+ return -1 ;
63
+ }
59
64
// make fresh JSObject for proxy
60
65
self->jsObject .set (JS_NewPlainObject (GLOBAL_CX));
61
66
return 0 ;
@@ -83,9 +88,29 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get(JSObjectProxy *self,
83
88
84
89
JS::RootedValue *value = new JS::RootedValue (GLOBAL_CX);
85
90
JS_GetPropertyById (GLOBAL_CX, self->jsObject , id, value);
91
+
86
92
JS::RootedObject *global = new JS::RootedObject (GLOBAL_CX, JS::GetNonCCWObjectGlobal (self->jsObject ));
87
- return pyTypeFactory (GLOBAL_CX, global, value)->getPyObject ();
88
- // TODO value and global appear to be leaking, but deleting them causes crashes
93
+
94
+ if (!value->isUndefined ()) {
95
+ return pyTypeFactory (GLOBAL_CX, global, value)->getPyObject ();
96
+ }
97
+ else {
98
+ // look through the methods for dispatch
99
+ for (size_t index = 0 ;; index ++) {
100
+ const char *methodName = JSObjectProxyType.tp_methods [index ].ml_name ;
101
+ if (methodName == NULL ) { // reached end of list
102
+ return pyTypeFactory (GLOBAL_CX, global, value)->getPyObject ();
103
+ }
104
+ else if (PyUnicode_Check (key)) {
105
+ if (strcmp (methodName, PyUnicode_AsUTF8 (key)) == 0 ) {
106
+ return PyObject_GenericGetAttr ((PyObject *)self, key);
107
+ }
108
+ }
109
+ else {
110
+ return pyTypeFactory (GLOBAL_CX, global, value)->getPyObject ();
111
+ }
112
+ }
113
+ }
89
114
}
90
115
91
116
int JSObjectProxyMethodDefinitions::JSObjectProxy_contains (JSObjectProxy *self, PyObject *key)
@@ -207,12 +232,11 @@ bool JSObjectProxyMethodDefinitions::JSObjectProxy_richcompare_helper(JSObjectPr
207
232
}
208
233
209
234
PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter (JSObjectProxy *self) {
210
- JSContext *cx = GLOBAL_CX;
211
- JS::RootedObject *global = new JS::RootedObject (cx, JS::GetNonCCWObjectGlobal (self->jsObject ));
235
+ JS::RootedObject *global = new JS::RootedObject (GLOBAL_CX, JS::GetNonCCWObjectGlobal (self->jsObject ));
212
236
213
237
// Get **enumerable** own properties
214
- JS::RootedIdVector props (cx );
215
- if (!js::GetPropertyKeys (cx , self->jsObject , JSITER_OWNONLY, &props)) {
238
+ JS::RootedIdVector props (GLOBAL_CX );
239
+ if (!js::GetPropertyKeys (GLOBAL_CX , self->jsObject , JSITER_OWNONLY, &props)) {
216
240
return NULL ;
217
241
}
218
242
@@ -222,11 +246,11 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_iter(JSObjectProxy *self
222
246
PyObject *seq = PyTuple_New (length);
223
247
for (size_t i = 0 ; i < length; i++) {
224
248
JS::HandleId id = props[i];
225
- PyObject *key = idToKey (cx , id);
249
+ PyObject *key = idToKey (GLOBAL_CX , id);
226
250
227
- JS::RootedValue *jsVal = new JS::RootedValue (cx );
228
- JS_GetPropertyById (cx , self->jsObject , id, jsVal);
229
- PyObject *value = pyTypeFactory (cx , global, jsVal)->getPyObject ();
251
+ JS::RootedValue *jsVal = new JS::RootedValue (GLOBAL_CX );
252
+ JS_GetPropertyById (GLOBAL_CX , self->jsObject , id, jsVal);
253
+ PyObject *value = pyTypeFactory (GLOBAL_CX , global, jsVal)->getPyObject ();
230
254
231
255
PyTuple_SetItem (seq, i, PyTuple_Pack (2 , key, value));
232
256
}
@@ -318,3 +342,125 @@ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_ior(JSObjectProxy *self,
318
342
Py_INCREF (self);
319
343
return (PyObject *)self;
320
344
}
345
+
346
+ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_get_method (JSObjectProxy *self, PyObject *const *args, Py_ssize_t nargs) {
347
+ PyObject *key;
348
+ PyObject *default_value = Py_None;
349
+
350
+ if (!_PyArg_CheckPositional (" get" , nargs, 1 , 2 )) {
351
+ return NULL ;
352
+ }
353
+ key = args[0 ];
354
+ if (nargs < 2 ) {
355
+ goto skip_optional;
356
+ }
357
+
358
+ default_value = args[1 ];
359
+
360
+ skip_optional:
361
+
362
+ PyObject *value = JSObjectProxy_get (self, key);
363
+ if (value == Py_None) {
364
+ value = default_value;
365
+ }
366
+ Py_XINCREF (value);
367
+ return value;
368
+ }
369
+
370
+ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_setdefault_method (JSObjectProxy *self, PyObject *const *args, Py_ssize_t nargs) {
371
+ PyObject *key;
372
+ PyObject *default_value = Py_None;
373
+
374
+ if (!_PyArg_CheckPositional (" setdefault" , nargs, 1 , 2 )) {
375
+ return NULL ;
376
+ }
377
+ key = args[0 ];
378
+ if (nargs < 2 ) {
379
+ goto skip_optional;
380
+ }
381
+
382
+ default_value = args[1 ];
383
+
384
+ skip_optional:
385
+
386
+ PyObject* value = JSObjectProxy_get (self, key);
387
+ if (value == Py_None) {
388
+ JSObjectProxy_assign (self, key, default_value);
389
+ Py_XINCREF (default_value);
390
+ return default_value;
391
+ }
392
+
393
+ Py_XINCREF (value);
394
+ return value;
395
+ }
396
+
397
+ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_pop_method (JSObjectProxy *self, PyObject *const *args, Py_ssize_t nargs) {
398
+ PyObject *key;
399
+ PyObject *default_value = NULL ;
400
+
401
+ if (!_PyArg_CheckPositional (" pop" , nargs, 1 , 2 )) {
402
+ return NULL ;
403
+ }
404
+ key = args[0 ];
405
+ if (nargs < 2 ) {
406
+ goto skip_optional;
407
+ }
408
+ default_value = args[1 ];
409
+
410
+ skip_optional:
411
+ JS::RootedId id (GLOBAL_CX);
412
+ if (!keyToId (key, &id)) {
413
+ // TODO (Caleb Aikens): raise exception here PyObject *seq = PyTuple_New(length);
414
+ return NULL ;
415
+ }
416
+
417
+ JS::RootedValue *value = new JS::RootedValue (GLOBAL_CX);
418
+ JS_GetPropertyById (GLOBAL_CX, self->jsObject , id, value);
419
+ if (value->isUndefined ()) {
420
+ if (default_value != NULL ) {
421
+ Py_INCREF (default_value);
422
+ return default_value;
423
+ }
424
+ _PyErr_SetKeyError (key);
425
+ return NULL ;
426
+ } else {
427
+ JS::ObjectOpResult ignoredResult;
428
+ JS_DeletePropertyById (GLOBAL_CX, self->jsObject , id, ignoredResult);
429
+
430
+ return pyTypeFactory (GLOBAL_CX, global, value)->getPyObject ();
431
+ }
432
+ }
433
+
434
+ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_clear_method (JSObjectProxy *self) {
435
+ JS::RootedIdVector props (GLOBAL_CX);
436
+ if (!js::GetPropertyKeys (GLOBAL_CX, self->jsObject , JSITER_OWNONLY, &props))
437
+ {
438
+ // @TODO (Caleb Aikens) raise exception here
439
+ return NULL ;
440
+ }
441
+
442
+ JS::ObjectOpResult ignoredResult;
443
+ size_t length = props.length ();
444
+ for (size_t index = 0 ; index < length; index ++) {
445
+ JS_DeletePropertyById (GLOBAL_CX, self->jsObject , props[index ], ignoredResult);
446
+ }
447
+
448
+ Py_RETURN_NONE;
449
+ }
450
+
451
+ PyObject *JSObjectProxyMethodDefinitions::JSObjectProxy_copy_method (JSObjectProxy *self) {
452
+ JS::Rooted<JS::ValueArray<2 >> args (GLOBAL_CX);
453
+ args[0 ].setObjectOrNull (JS_NewPlainObject (GLOBAL_CX));
454
+ args[1 ].setObjectOrNull (self->jsObject );
455
+
456
+ JS::RootedObject *global = new JS::RootedObject (GLOBAL_CX, JS::GetNonCCWObjectGlobal (self->jsObject ));
457
+
458
+ // call Object.assign
459
+ JS::RootedValue Object (GLOBAL_CX);
460
+ JS_GetProperty (GLOBAL_CX, *global, " Object" , &Object);
461
+
462
+ JS::RootedObject rootedObject (GLOBAL_CX, Object.toObjectOrNull ());
463
+ JS::RootedValue ret (GLOBAL_CX);
464
+ if (!JS_CallFunctionName (GLOBAL_CX, rootedObject, " assign" , args, &ret)) return NULL ;
465
+ return pyTypeFactory (GLOBAL_CX, global, &ret)->getPyObject ();
466
+ }
0 commit comments