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.

Signed-off-by: Damien George <damien@micropython.org>
Damien George 2024-05-09 15:11:21 +10:00
rodzic 0a07cd8a91
commit fb4cde3060
3 zmienionych plików z 62 dodań i 0 usunięć

Wyświetl plik

@ -58,6 +58,8 @@ enum {
PROXY_KIND_JS_PYPROXY = 7,
};
static 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;
}

Wyświetl plik

@ -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
print(1)
try:
await js.foo()
except Exception as er:
error = er
print(error)
print(3)
`);
const error = mp.globals.get("error");
console.log(error instanceof Error, error.name, error.message);

Wyświetl plik

@ -0,0 +1,5 @@
1
2
(<JsProxy 6>, 'Error', 'test')
3
true Error test