@@ -43,7 +43,7 @@ EM_JS(bool, has_attr, (int jsref, const char *str), {
43
43
});
44
44
45
45
// *FORMAT-OFF*
46
- EM_JS (bool , lookup_attr , (int jsref , const char * str , uint32_t * out ), {
46
+ EM_JS (int , lookup_attr , (int jsref , const char * str , uint32_t * out ), {
47
47
const base = proxy_js_ref [jsref ];
48
48
const attr = UTF8ToString (str );
49
49
@@ -54,23 +54,17 @@ EM_JS(bool, lookup_attr, (int jsref, const char *str, uint32_t * out), {
54
54
// - Otherwise, the attribute does not exist.
55
55
let value = base [attr ];
56
56
if (value != = undefined || attr in base ) {
57
- if (typeof value == = "function" ) {
58
- if (base != = globalThis ) {
59
- if ("_ref" in value ) {
60
- // This is a proxy of a Python function, it doesn't need
61
- // binding. And not binding it means if it's passed back
62
- // to Python then it can be extracted from the proxy as a
63
- // true Python function.
64
- } else {
65
- // A function that is not a Python function. Bind it.
66
- value = value .bind (base );
67
- }
68
- }
69
- }
70
57
proxy_convert_js_to_mp_obj_jsside (value , out );
71
- return true;
58
+ if (typeof value == = "function" && !("_ref" in value )) {
59
+ // Attribute found and it's a JavaScript function.
60
+ return 2 ;
61
+ } else {
62
+ // Attribute found.
63
+ return 1 ;
64
+ }
72
65
} else {
73
- return false;
66
+ // Attribute not found.
67
+ return 0 ;
74
68
}
75
69
});
76
70
// *FORMAT-ON*
@@ -98,45 +92,65 @@ EM_JS(void, call0, (int f_ref, uint32_t * out), {
98
92
proxy_convert_js_to_mp_obj_jsside (ret , out );
99
93
});
100
94
101
- EM_JS (int , call1 , (int f_ref , uint32_t * a0 , uint32_t * out ), {
95
+ EM_JS (int , call1 , (int f_ref , bool via_call , uint32_t * a0 , uint32_t * out ), {
102
96
const a0_js = proxy_convert_mp_to_js_obj_jsside (a0 );
103
97
const f = proxy_js_ref [f_ref ];
104
- const ret = f (a0_js );
98
+ let ret ;
99
+ if (via_call ) {
100
+ ret = f .call (a0_js );
101
+ } else {
102
+ ret = f (a0_js );
103
+ }
105
104
proxy_convert_js_to_mp_obj_jsside (ret , out );
106
105
});
107
106
108
- EM_JS (int , call2 , (int f_ref , uint32_t * a0 , uint32_t * a1 , uint32_t * out ), {
107
+ EM_JS (int , call2 , (int f_ref , bool via_call , uint32_t * a0 , uint32_t * a1 , uint32_t * out ), {
109
108
const a0_js = proxy_convert_mp_to_js_obj_jsside (a0 );
110
109
const a1_js = proxy_convert_mp_to_js_obj_jsside (a1 );
111
110
const f = proxy_js_ref [f_ref ];
112
- const ret = f (a0_js , a1_js );
111
+ let ret ;
112
+ if (via_call ) {
113
+ ret = f .call (a0_js , a1_js );
114
+ } else {
115
+ ret = f (a0_js , a1_js );
116
+ }
113
117
proxy_convert_js_to_mp_obj_jsside (ret , out );
114
118
});
115
119
116
- EM_JS (int , calln , (int f_ref , uint32_t n_args , uint32_t * value , uint32_t * out ), {
120
+ EM_JS (int , calln , (int f_ref , bool via_call , uint32_t n_args , uint32_t * value , uint32_t * out ), {
117
121
const f = proxy_js_ref [f_ref ];
118
122
const a = [];
119
123
for (let i = 0 ; i < n_args ; ++ i ) {
120
124
const v = proxy_convert_mp_to_js_obj_jsside (value + i * 3 * 4 );
121
125
a .push (v );
122
126
}
123
- const ret = f (... a );
127
+ let ret ;
128
+ if (via_call ) {
129
+ ret = f .call (... a );
130
+ } else {
131
+ ret = f (... a );
132
+ }
124
133
proxy_convert_js_to_mp_obj_jsside (ret , out );
125
134
});
126
135
127
- EM_JS (void , call0_kwarg , (int f_ref , uint32_t n_kw , uint32_t * key , uint32_t * value , uint32_t * out ), {
136
+ EM_JS (void , call0_kwarg , (int f_ref , bool via_call , uint32_t n_kw , uint32_t * key , uint32_t * value , uint32_t * out ), {
128
137
const f = proxy_js_ref [f_ref ];
129
138
const a = {};
130
139
for (let i = 0 ; i < n_kw ; ++ i ) {
131
140
const k = UTF8ToString (getValue (key + i * 4 , "i32" ));
132
141
const v = proxy_convert_mp_to_js_obj_jsside (value + i * 3 * 4 );
133
142
a [k ] = v ;
134
143
}
135
- const ret = f (a );
144
+ let ret ;
145
+ if (via_call ) {
146
+ ret = f .call (a );
147
+ } else {
148
+ ret = f (a );
149
+ }
136
150
proxy_convert_js_to_mp_obj_jsside (ret , out );
137
151
});
138
152
139
- EM_JS (void , call1_kwarg , (int f_ref , uint32_t * arg0 , uint32_t n_kw , uint32_t * key , uint32_t * value , uint32_t * out ), {
153
+ EM_JS (void , call1_kwarg , (int f_ref , bool via_call , uint32_t * arg0 , uint32_t n_kw , uint32_t * key , uint32_t * value , uint32_t * out ), {
140
154
const f = proxy_js_ref [f_ref ];
141
155
const a0 = proxy_convert_mp_to_js_obj_jsside (arg0 );
142
156
const a = {};
@@ -145,7 +159,12 @@ EM_JS(void, call1_kwarg, (int f_ref, uint32_t * arg0, uint32_t n_kw, uint32_t *
145
159
const v = proxy_convert_mp_to_js_obj_jsside (value + i * 3 * 4 );
146
160
a [k ] = v ;
147
161
}
148
- const ret = f (a0 , a );
162
+ let ret ;
163
+ if (via_call ) {
164
+ ret = f .call (a0 , a );
165
+ } else {
166
+ ret = f (a0 , a );
167
+ }
149
168
proxy_convert_js_to_mp_obj_jsside (ret , out );
150
169
});
151
170
@@ -208,12 +227,12 @@ static mp_obj_t jsproxy_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
208
227
}
209
228
uint32_t out [3 ];
210
229
if (n_args == 0 ) {
211
- call0_kwarg (self -> ref , n_kw , key , value , out );
230
+ call0_kwarg (self -> ref , self -> bind_to_self , n_kw , key , value , out );
212
231
} else {
213
232
// n_args == 1
214
233
uint32_t arg0 [PVN ];
215
234
proxy_convert_mp_to_js_obj_cside (args [0 ], arg0 );
216
- call1_kwarg (self -> ref , arg0 , n_kw , key , value , out );
235
+ call1_kwarg (self -> ref , self -> bind_to_self , arg0 , n_kw , key , value , out );
217
236
}
218
237
return proxy_convert_js_to_mp_obj_cside (out );
219
238
}
@@ -226,23 +245,23 @@ static mp_obj_t jsproxy_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const
226
245
uint32_t arg0 [PVN ];
227
246
uint32_t out [PVN ];
228
247
proxy_convert_mp_to_js_obj_cside (args [0 ], arg0 );
229
- call1 (self -> ref , arg0 , out );
248
+ call1 (self -> ref , self -> bind_to_self , arg0 , out );
230
249
return proxy_convert_js_to_mp_obj_cside (out );
231
250
} else if (n_args == 2 ) {
232
251
uint32_t arg0 [PVN ];
233
252
proxy_convert_mp_to_js_obj_cside (args [0 ], arg0 );
234
253
uint32_t arg1 [PVN ];
235
254
proxy_convert_mp_to_js_obj_cside (args [1 ], arg1 );
236
255
uint32_t out [3 ];
237
- call2 (self -> ref , arg0 , arg1 , out );
256
+ call2 (self -> ref , self -> bind_to_self , arg0 , arg1 , out );
238
257
return proxy_convert_js_to_mp_obj_cside (out );
239
258
} else {
240
259
uint32_t value [PVN * n_args ];
241
260
for (int i = 0 ; i < n_args ; ++ i ) {
242
261
proxy_convert_mp_to_js_obj_cside (args [i ], & value [i * PVN ]);
243
262
}
244
263
uint32_t out [3 ];
245
- calln (self -> ref , n_args , value , out );
264
+ calln (self -> ref , self -> bind_to_self , n_args , value , out );
246
265
return proxy_convert_js_to_mp_obj_cside (out );
247
266
}
248
267
}
@@ -298,17 +317,26 @@ static mp_obj_t jsproxy_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value)
298
317
}
299
318
}
300
319
301
- void mp_obj_jsproxy_attr (mp_obj_t self_in , qstr attr , mp_obj_t * dest ) {
320
+ static void mp_obj_jsproxy_attr (mp_obj_t self_in , qstr attr , mp_obj_t * dest ) {
302
321
mp_obj_jsproxy_t * self = MP_OBJ_TO_PTR (self_in );
303
322
if (dest [0 ] == MP_OBJ_NULL ) {
304
323
// Load attribute.
324
+ int lookup_ret ;
305
325
uint32_t out [PVN ];
306
326
if (attr == MP_QSTR___del__ ) {
307
327
// For finaliser.
308
328
dest [0 ] = MP_OBJ_FROM_PTR (& jsproxy___del___obj );
309
329
dest [1 ] = self_in ;
310
- } else if (lookup_attr (self -> ref , qstr_str (attr ), out )) {
330
+ } else if (( lookup_ret = lookup_attr (self -> ref , qstr_str (attr ), out )) != 0 ) {
311
331
dest [0 ] = proxy_convert_js_to_mp_obj_cside (out );
332
+ if (lookup_ret == 2 ) {
333
+ // The loaded attribute is a JavaScript method, which should be called
334
+ // with f.call(self, ...). Indicate this via the bind_to_self member.
335
+ // This will either be called immediately (due to the mp_load_method
336
+ // optimisation) or turned into a bound_method and called later.
337
+ dest [1 ] = self_in ;
338
+ ((mp_obj_jsproxy_t * )dest [0 ])-> bind_to_self = true;
339
+ }
312
340
} else if (attr == MP_QSTR_new ) {
313
341
// Special case to handle construction of JS objects.
314
342
// JS objects don't have a ".new" attribute, doing "Obj.new" is a Pyodide idiom for "new Obj".
@@ -546,5 +574,25 @@ MP_DEFINE_CONST_OBJ_TYPE(
546
574
mp_obj_t mp_obj_new_jsproxy (int ref ) {
547
575
mp_obj_jsproxy_t * o = mp_obj_malloc_with_finaliser (mp_obj_jsproxy_t , & mp_type_jsproxy );
548
576
o -> ref = ref ;
577
+ o -> bind_to_self = false;
549
578
return MP_OBJ_FROM_PTR (o );
550
579
}
580
+
581
+ // Load/delete/store an attribute from/to the JavaScript globalThis entity.
582
+ void mp_obj_jsproxy_global_this_attr (qstr attr , mp_obj_t * dest ) {
583
+ if (dest [0 ] == MP_OBJ_NULL ) {
584
+ // Load attribute.
585
+ uint32_t out [PVN ];
586
+ if (lookup_attr (MP_OBJ_JSPROXY_REF_GLOBAL_THIS , qstr_str (attr ), out )) {
587
+ dest [0 ] = proxy_convert_js_to_mp_obj_cside (out );
588
+ }
589
+ } else if (dest [1 ] == MP_OBJ_NULL ) {
590
+ // Delete attribute.
591
+ } else {
592
+ // Store attribute.
593
+ uint32_t value [PVN ];
594
+ proxy_convert_mp_to_js_obj_cside (dest [1 ], value );
595
+ store_attr (MP_OBJ_JSPROXY_REF_GLOBAL_THIS , qstr_str (attr ), value );
596
+ dest [0 ] = MP_OBJ_NULL ;
597
+ }
598
+ }
0 commit comments