webassembly/objpyproxy: Implement JS iterator protocol for Py iterables.
This allows using JavaScript for..of on Python iterables. Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
parent
e860e32e24
commit
c056840ee8
@ -60,8 +60,10 @@ EXPORTED_FUNCTIONS_EXTRA += ,\
|
|||||||
_proxy_c_to_js_dir,\
|
_proxy_c_to_js_dir,\
|
||||||
_proxy_c_to_js_get_array,\
|
_proxy_c_to_js_get_array,\
|
||||||
_proxy_c_to_js_get_dict,\
|
_proxy_c_to_js_get_dict,\
|
||||||
|
_proxy_c_to_js_get_iter,\
|
||||||
_proxy_c_to_js_get_type,\
|
_proxy_c_to_js_get_type,\
|
||||||
_proxy_c_to_js_has_attr,\
|
_proxy_c_to_js_has_attr,\
|
||||||
|
_proxy_c_to_js_iternext,\
|
||||||
_proxy_c_to_js_lookup_attr,\
|
_proxy_c_to_js_lookup_attr,\
|
||||||
_proxy_c_to_js_resume,\
|
_proxy_c_to_js_resume,\
|
||||||
_proxy_c_to_js_store_attr,\
|
_proxy_c_to_js_store_attr,\
|
||||||
|
|||||||
@ -162,10 +162,37 @@ const py_proxy_handler = {
|
|||||||
if (prop === "then") {
|
if (prop === "then") {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prop === Symbol.iterator) {
|
||||||
|
// Get the Python object iterator, and return a JavaScript generator.
|
||||||
|
const iter_ref = Module.ccall(
|
||||||
|
"proxy_c_to_js_get_iter",
|
||||||
|
"number",
|
||||||
|
["number"],
|
||||||
|
[target._ref],
|
||||||
|
);
|
||||||
|
return function* () {
|
||||||
|
const value = Module._malloc(3 * 4);
|
||||||
|
while (true) {
|
||||||
|
const valid = Module.ccall(
|
||||||
|
"proxy_c_to_js_iternext",
|
||||||
|
"number",
|
||||||
|
["number", "pointer"],
|
||||||
|
[iter_ref, value],
|
||||||
|
);
|
||||||
|
if (!valid) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
yield proxy_convert_mp_to_js_obj_jsside(value);
|
||||||
|
}
|
||||||
|
Module._free(value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const value = Module._malloc(3 * 4);
|
const value = Module._malloc(3 * 4);
|
||||||
Module.ccall(
|
Module.ccall(
|
||||||
"proxy_c_to_js_lookup_attr",
|
"proxy_c_to_js_lookup_attr",
|
||||||
"number",
|
"null",
|
||||||
["number", "string", "pointer"],
|
["number", "string", "pointer"],
|
||||||
[target._ref, prop, value],
|
[target._ref, prop, value],
|
||||||
);
|
);
|
||||||
|
|||||||
@ -65,6 +65,12 @@ void proxy_c_init(void) {
|
|||||||
|
|
||||||
MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_ref);
|
MP_REGISTER_ROOT_POINTER(mp_obj_t proxy_c_ref);
|
||||||
|
|
||||||
|
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;
|
||||||
|
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
static inline mp_obj_t proxy_c_get_obj(uint32_t c_ref) {
|
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];
|
||||||
}
|
}
|
||||||
@ -122,9 +128,7 @@ void proxy_convert_mp_to_js_obj_cside(mp_obj_t obj, uint32_t *out) {
|
|||||||
} else {
|
} else {
|
||||||
kind = PROXY_KIND_MP_OBJECT;
|
kind = PROXY_KIND_MP_OBJECT;
|
||||||
}
|
}
|
||||||
size_t id = ((mp_obj_list_t *)MP_OBJ_TO_PTR(MP_STATE_PORT(proxy_c_ref)))->len;
|
out[1] = proxy_c_add_obj(obj);
|
||||||
mp_obj_list_append(MP_STATE_PORT(proxy_c_ref), obj);
|
|
||||||
out[1] = id;
|
|
||||||
}
|
}
|
||||||
out[0] = kind;
|
out[0] = kind;
|
||||||
}
|
}
|
||||||
@ -284,6 +288,38 @@ void proxy_c_to_js_get_dict(uint32_t c_ref, uint32_t *out) {
|
|||||||
out[1] = (uintptr_t)map->table;
|
out[1] = (uintptr_t)map->table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
// Bridge Python iterator to JavaScript iterator protocol.
|
||||||
|
|
||||||
|
uint32_t proxy_c_to_js_get_iter(uint32_t c_ref) {
|
||||||
|
mp_obj_t obj = proxy_c_get_obj(c_ref);
|
||||||
|
mp_obj_t iter = mp_getiter(obj, NULL);
|
||||||
|
return proxy_c_add_obj(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool proxy_c_to_js_iternext(uint32_t c_ref, uint32_t *out) {
|
||||||
|
mp_obj_t obj = proxy_c_get_obj(c_ref);
|
||||||
|
nlr_buf_t nlr;
|
||||||
|
if (nlr_push(&nlr) == 0) {
|
||||||
|
mp_obj_t iter = mp_iternext_allow_raise(obj);
|
||||||
|
if (iter == MP_OBJ_STOP_ITERATION) {
|
||||||
|
nlr_pop();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
nlr_pop();
|
||||||
|
proxy_convert_mp_to_js_obj_cside(iter, out);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// uncaught exception
|
||||||
|
proxy_convert_mp_to_js_exc_cside(nlr.ret_val, out);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
// Bridge Python generator to JavaScript thenable.
|
// Bridge Python generator to JavaScript thenable.
|
||||||
|
|
||||||
|
|||||||
21
tests/ports/webassembly/iterator.mjs
Normal file
21
tests/ports/webassembly/iterator.mjs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Test accessing Python iterables from JavaScript via the JavaScript iterator protocol.
|
||||||
|
|
||||||
|
const mp = await (await import(process.argv[2])).loadMicroPython();
|
||||||
|
|
||||||
|
mp.runPython(`
|
||||||
|
s = "abc"
|
||||||
|
l = [1, 2, 3]
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Iterate a Python string.
|
||||||
|
for (const value of mp.globals.get("s")) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate a Python list.
|
||||||
|
for (const value of mp.globals.get("l")) {
|
||||||
|
console.log(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate a Python list from a built-in JavaScript constructor.
|
||||||
|
mp.runPython("import js; print(js.Set.new([1, 2, 3]).has(3))");
|
||||||
7
tests/ports/webassembly/iterator.mjs.exp
Normal file
7
tests/ports/webassembly/iterator.mjs.exp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
True
|
||||||
Loading…
x
Reference in New Issue
Block a user