Skip to content

Commit

Permalink
webassembly/proxy_c: Ensure objs thrown into generators are exceptions.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
dpgeorge committed May 13, 2024
1 parent 3f34be6 commit a67e326
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 0 deletions.
1 change: 1 addition & 0 deletions ports/webassembly/modjsffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) },
Expand Down
35 changes: 35 additions & 0 deletions ports/webassembly/proxy_c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.

Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions ports/webassembly/proxy_c.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 22 additions & 0 deletions tests/ports/webassembly/await_error_handling.mjs
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 5 additions & 0 deletions tests/ports/webassembly/await_error_handling.mjs.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
1
2
(<JsProxy 6>, 'Error', 'test')
3
true Error test

0 comments on commit a67e326

Please sign in to comment.