webassembly: Register PyProxy objects for JS-side finalisation.
And clear the corresponding `proxy_c_ref[c_ref]` entry when the finaliser runs. This then allows the C side to (eventually) garbage collect the corresponding Python object. Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
parent
5c7a414574
commit
57a9ffa632
@ -54,6 +54,7 @@ EXPORTED_FUNCTIONS_EXTRA += ,\
|
|||||||
_mp_js_do_exec_async,\
|
_mp_js_do_exec_async,\
|
||||||
_mp_js_do_import,\
|
_mp_js_do_import,\
|
||||||
_mp_js_register_js_module,\
|
_mp_js_register_js_module,\
|
||||||
|
_proxy_c_free_obj,\
|
||||||
_proxy_c_init,\
|
_proxy_c_init,\
|
||||||
_proxy_c_to_js_call,\
|
_proxy_c_to_js_call,\
|
||||||
_proxy_c_to_js_delete_attr,\
|
_proxy_c_to_js_delete_attr,\
|
||||||
|
|||||||
@ -32,6 +32,9 @@
|
|||||||
#include "py/runtime.h"
|
#include "py/runtime.h"
|
||||||
#include "proxy_c.h"
|
#include "proxy_c.h"
|
||||||
|
|
||||||
|
// Number of static entries at the start of proxy_c_ref.
|
||||||
|
#define PROXY_C_REF_NUM_STATIC (1)
|
||||||
|
|
||||||
// These constants should match the constants in proxy_js.js.
|
// These constants should match the constants in proxy_js.js.
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -71,16 +74,36 @@ static const mp_obj_base_t mp_const_undefined_obj = {&mp_type_undefined};
|
|||||||
|
|
||||||
MP_DEFINE_EXCEPTION(JsException, Exception)
|
MP_DEFINE_EXCEPTION(JsException, Exception)
|
||||||
|
|
||||||
|
// Index to start searching for the next available slot in proxy_c_ref.
|
||||||
|
static size_t proxy_c_ref_next;
|
||||||
|
|
||||||
void proxy_c_init(void) {
|
void proxy_c_init(void) {
|
||||||
MP_STATE_PORT(proxy_c_ref) = mp_obj_new_list(0, NULL);
|
MP_STATE_PORT(proxy_c_ref) = mp_obj_new_list(0, NULL);
|
||||||
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), MP_OBJ_NULL);
|
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), MP_OBJ_NULL);
|
||||||
|
proxy_c_ref_next = PROXY_C_REF_NUM_STATIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_ref);
|
MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_ref);
|
||||||
|
|
||||||
|
// obj cannot be MP_OBJ_NULL.
|
||||||
static inline size_t proxy_c_add_obj(mp_obj_t obj) {
|
static inline size_t proxy_c_add_obj(mp_obj_t obj) {
|
||||||
size_t id = ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->len;
|
// Search for the first free slot in proxy_c_ref.
|
||||||
|
mp_obj_list_t *l = (mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref));
|
||||||
|
while (proxy_c_ref_next < l->len) {
|
||||||
|
if (l->items[proxy_c_ref_next] == MP_OBJ_NULL) {
|
||||||
|
// Free slot found, reuse it.
|
||||||
|
size_t id = proxy_c_ref_next;
|
||||||
|
++proxy_c_ref_next;
|
||||||
|
l->items[id] = obj;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
++proxy_c_ref_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No free slots, so grow proxy_c_ref by one (append at the end of the list).
|
||||||
|
size_t id = l->len;
|
||||||
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj);
|
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj);
|
||||||
|
proxy_c_ref_next = l->len;
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +111,13 @@ static inline mp_obj_t proxy_c_get_obj(uint32_t c_ref) {
|
|||||||
return ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref];
|
return ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void proxy_c_free_obj(uint32_t c_ref) {
|
||||||
|
if (c_ref >= PROXY_C_REF_NUM_STATIC) {
|
||||||
|
((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->items[c_ref] = MP_OBJ_NULL;
|
||||||
|
proxy_c_ref_next = MIN(proxy_c_ref_next, c_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mp_obj_t proxy_convert_js_to_mp_obj_cside(uint32_t *value) {
|
mp_obj_t proxy_convert_js_to_mp_obj_cside(uint32_t *value) {
|
||||||
if (value[0] == PROXY_KIND_JS_UNDEFINED) {
|
if (value[0] == PROXY_KIND_JS_UNDEFINED) {
|
||||||
return mp_const_undefined;
|
return mp_const_undefined;
|
||||||
|
|||||||
@ -61,6 +61,11 @@ class PythonError extends Error {
|
|||||||
function proxy_js_init() {
|
function proxy_js_init() {
|
||||||
globalThis.proxy_js_ref = [globalThis, undefined];
|
globalThis.proxy_js_ref = [globalThis, undefined];
|
||||||
globalThis.proxy_js_ref_next = PROXY_JS_REF_NUM_STATIC;
|
globalThis.proxy_js_ref_next = PROXY_JS_REF_NUM_STATIC;
|
||||||
|
globalThis.pyProxyFinalizationRegistry = new FinalizationRegistry(
|
||||||
|
(cRef) => {
|
||||||
|
Module.ccall("proxy_c_free_obj", "null", ["number"], [cRef]);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// js_obj cannot be undefined
|
// js_obj cannot be undefined
|
||||||
@ -251,6 +256,7 @@ function proxy_convert_mp_to_js_obj_jsside(value) {
|
|||||||
const target = new PyProxy(id);
|
const target = new PyProxy(id);
|
||||||
obj = new Proxy(target, py_proxy_handler);
|
obj = new Proxy(target, py_proxy_handler);
|
||||||
}
|
}
|
||||||
|
globalThis.pyProxyFinalizationRegistry.register(obj, id);
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user