diff options
| author | Damien George <damien@micropython.org> | 2024-05-09 15:11:21 +1000 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2024-05-13 11:52:17 +1000 |
| commit | a67e326cb9753f53e14a42b347133a571f972140 (patch) | |
| tree | eeea3e4a1a8e76b67914e16b1ef87bd62b4788eb | |
| parent | 3f34be69c77dfbd1533af1f04ea460d7da2d118a (diff) | |
webassembly/proxy_c: Ensure objs thrown into generators are exceptions.
This commit defines a new `JsException` exception type which is used on the
Python side to wrap JavaScript errors. That's then used when a JavaScript
Promise is rejected, and the reason is then converted to a `JsException`
for the Python side to handle.
This new exception is exposed as `jsffi.JsException`.
Signed-off-by: Damien George <damien@micropython.org>
| -rw-r--r-- | ports/webassembly/modjsffi.c | 1 | ||||
| -rw-r--r-- | ports/webassembly/proxy_c.c | 35 | ||||
| -rw-r--r-- | ports/webassembly/proxy_c.h | 1 | ||||
| -rw-r--r-- | tests/ports/webassembly/await_error_handling.mjs | 22 | ||||
| -rw-r--r-- | tests/ports/webassembly/await_error_handling.mjs.exp | 5 |
5 files changed, 64 insertions, 0 deletions
diff --git a/ports/webassembly/modjsffi.c b/ports/webassembly/modjsffi.c index cb2f578f8..c77d899fb 100644 --- a/ports/webassembly/modjsffi.c +++ b/ports/webassembly/modjsffi.c @@ -79,6 +79,7 @@ static const mp_rom_map_elem_t mp_module_jsffi_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_jsffi) }, { MP_ROM_QSTR(MP_QSTR_JsProxy), MP_ROM_PTR(&mp_type_jsproxy) }, + { MP_ROM_QSTR(MP_QSTR_JsException), MP_ROM_PTR(&mp_type_JsException) }, { MP_ROM_QSTR(MP_QSTR_create_proxy), MP_ROM_PTR(&mp_jsffi_create_proxy_obj) }, { MP_ROM_QSTR(MP_QSTR_to_js), MP_ROM_PTR(&mp_jsffi_to_js_obj) }, { MP_ROM_QSTR(MP_QSTR_async_timeout_ms), MP_ROM_PTR(&mp_jsffi_async_timeout_ms_obj) }, diff --git a/ports/webassembly/proxy_c.c b/ports/webassembly/proxy_c.c index 4e2fdc8f2..3097d6d3a 100644 --- a/ports/webassembly/proxy_c.c +++ b/ports/webassembly/proxy_c.c @@ -58,6 +58,8 @@ enum { PROXY_KIND_JS_PYPROXY = 7, }; +MP_DEFINE_EXCEPTION(JsException, Exception) + void proxy_c_init(void) { 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); @@ -120,6 +122,15 @@ void proxy_convert_mp_to_js_obj_cside(mp_obj_t obj, uint32_t *out) { } else if (mp_obj_is_jsproxy(obj)) { kind = PROXY_KIND_MP_JSPROXY; out[1] = mp_obj_jsproxy_get_ref(obj); + } else if (mp_obj_get_type(obj) == &mp_type_JsException) { + mp_obj_exception_t *exc = MP_OBJ_TO_PTR(obj); + if (exc->args->len > 0 && mp_obj_is_jsproxy(exc->args->items[0])) { + kind = PROXY_KIND_MP_JSPROXY; + out[1] = mp_obj_jsproxy_get_ref(exc->args->items[0]); + } else { + kind = PROXY_KIND_MP_OBJECT; + out[1] = proxy_c_add_obj(obj); + } } else { if (mp_obj_is_callable(obj)) { kind = PROXY_KIND_MP_CALLABLE; @@ -288,6 +299,24 @@ void proxy_c_to_js_get_dict(uint32_t c_ref, uint32_t *out) { out[1] = (uintptr_t)map->table; } +EM_JS(void, js_get_error_info, (int jsref, uint32_t * out_name, uint32_t * out_message), { + const error = proxy_js_ref[jsref]; + proxy_convert_js_to_mp_obj_jsside(error.name, out_name); + proxy_convert_js_to_mp_obj_jsside(error.message, out_message); +}); + +mp_obj_t mp_obj_jsproxy_make_js_exception(mp_obj_t error) { + uint32_t out_name[PVN]; + uint32_t out_message[PVN]; + js_get_error_info(mp_obj_jsproxy_get_ref(error), out_name, out_message); + mp_obj_t args[3] = { + error, + proxy_convert_js_to_mp_obj_cside(out_name), + proxy_convert_js_to_mp_obj_cside(out_message), + }; + return mp_obj_new_exception_args(&mp_type_JsException, MP_ARRAY_SIZE(args), args); +} + /******************************************************************************/ // Bridge Python iterator to JavaScript iterator protocol. @@ -371,6 +400,12 @@ static mp_obj_t proxy_resume_execute(mp_obj_t self_in, mp_obj_t send_value, mp_o if (send_value == mp_const_none) { send_value = MP_OBJ_NULL; } + // Ensure that the `throw_value` is a proper Python exception instance. + if (mp_obj_is_jsproxy(throw_value)) { + throw_value = mp_obj_jsproxy_make_js_exception(throw_value); + } else { + throw_value = mp_make_raise_obj(throw_value); + } } else { throw_value = MP_OBJ_NULL; } diff --git a/ports/webassembly/proxy_c.h b/ports/webassembly/proxy_c.h index 3e68d2504..751d14332 100644 --- a/ports/webassembly/proxy_c.h +++ b/ports/webassembly/proxy_c.h @@ -37,6 +37,7 @@ typedef struct _mp_obj_jsproxy_t { } mp_obj_jsproxy_t; extern const mp_obj_type_t mp_type_jsproxy; +extern const mp_obj_type_t mp_type_JsException; void proxy_c_init(void); mp_obj_t proxy_convert_js_to_mp_obj_cside(uint32_t *value); diff --git a/tests/ports/webassembly/await_error_handling.mjs b/tests/ports/webassembly/await_error_handling.mjs new file mode 100644 index 000000000..067bc2513 --- /dev/null +++ b/tests/ports/webassembly/await_error_handling.mjs @@ -0,0 +1,22 @@ +// Test await'ing on a JavaScript async function that throws a JavaScript Error. + +const mp = await (await import(process.argv[2])).loadMicroPython(); + +globalThis.foo = async () => { + console.log(2); + throw Error("test"); +}; + +await mp.runPythonAsync(` +import js, jsffi +print(1) +try: + await js.foo() +except jsffi.JsException as er: + error = er +print(error) +print(3) +`); + +const error = mp.globals.get("error"); +console.log(error instanceof Error, error.name, error.message); diff --git a/tests/ports/webassembly/await_error_handling.mjs.exp b/tests/ports/webassembly/await_error_handling.mjs.exp new file mode 100644 index 000000000..149f8914d --- /dev/null +++ b/tests/ports/webassembly/await_error_handling.mjs.exp @@ -0,0 +1,5 @@ +1 +2 +(<JsProxy 6>, 'Error', 'test') +3 +true Error test |
