diff --git a/include/errors.h b/include/errors.h index 6d2ba36..bceb161 100644 --- a/include/errors.h +++ b/include/errors.h @@ -31,6 +31,7 @@ void js_clear_error_site(ant_t *js); void js_print_stack_trace_vm(ant_t *js, FILE *stream); void js_set_error_site_from_vm_top(ant_t *js); void js_capture_stack(ant_t *js, ant_value_t err_obj); +bool js_mark_errorlike_no_stack(ant_t *js, ant_value_t value); void js_get_call_location( ant_t *js, const char **out_filename, diff --git a/src/errors.c b/src/errors.c index 2106c56..dd86de8 100644 --- a/src/errors.c +++ b/src/errors.c @@ -23,12 +23,15 @@ typedef struct { char *buf; size_t size; } errbuf_t; static void print_error_value(ant_t *js, ant_value_t value, ant_value_t fallback_stack, const char *prefix) { ant_value_t obj = is_err(value) ? js_as_obj(value) : value; - const char *stack = NULL; + const char *stack = NULL; bool no_stack = false; - if (vtype(obj) == T_OBJ) - stack = get_str_prop(js, obj, "stack", 5, NULL); + if (vtype(obj) == T_OBJ) { + ant_value_t err_type = js_get_slot(obj, SLOT_ERR_TYPE); + no_stack = vtype(err_type) == T_NUM && ((int)js_getnum(err_type) & JS_ERR_NO_STACK); + if (!no_stack) stack = get_str_prop(js, obj, "stack", 5, NULL); + } - if (!stack && vtype(fallback_stack) == T_STR) { + if (!no_stack && !stack && vtype(fallback_stack) == T_STR) { ant_offset_t slen; ant_offset_t soff = vstr(js, fallback_stack, &slen); stack = (const char *)(uintptr_t)(soff); @@ -68,6 +71,23 @@ bool print_unhandled_promise_rejection(ant_t *js, ant_value_t value) { return true; } +bool js_mark_errorlike_no_stack(ant_t *js, ant_value_t value) { + if (vtype(value) != T_OBJ) return false; + if (js_get_slot(value, SLOT_ERROR_BRAND) == js_true) return false; + + const char *name = get_str_prop(js, value, "name", 4, NULL); + const char *message = get_str_prop(js, value, "message", 7, NULL); + if ((!name || !*name) && (!message || !*message)) return false; + + ant_value_t err_type = js_get_slot(value, SLOT_ERR_TYPE); + int base_type = vtype(err_type) == T_NUM ? (int)js_getnum(err_type) : JS_ERR_GENERIC; + + js_set(js, value, "stack", js_mkundef()); + js_set_slot(value, SLOT_ERR_TYPE, js_mknum((double)(base_type | JS_ERR_NO_STACK))); + + return true; +} + static const char *get_error_type_name(js_err_type_t err_type) { static const char *names[] = { [JS_ERR_GENERIC] = "Error", diff --git a/src/modules/wasm.c b/src/modules/wasm.c index 614ff32..fc58d97 100644 --- a/src/modules/wasm.c +++ b/src/modules/wasm.c @@ -517,6 +517,7 @@ static ant_value_t js_wasm_exported_func_call(ant_t *js, ant_value_t *args, int if (trap) { if (g_wasm_pending_import_throw_exists) { result = wasm_consume_pending_import_throw(); + js_mark_errorlike_no_stack(js, result); wasm_val_vec_delete(&wasm_args); wasm_val_vec_delete(&wasm_results); wasm_functype_delete(type); @@ -950,6 +951,7 @@ static ant_value_t wasm_instantiate_module(ant_t *js, ant_value_t module_obj, an if (trap) { if (g_wasm_pending_import_throw_exists) { ant_value_t thrown = wasm_consume_pending_import_throw(); + js_mark_errorlike_no_stack(js, thrown); wasm_trap_delete(trap); return js_throw(js, thrown); } diff --git a/tests/test_wasm_webassembly_api.mjs b/tests/test_wasm_webassembly_api.mjs index 74f222e..2430e32 100644 --- a/tests/test_wasm_webassembly_api.mjs +++ b/tests/test_wasm_webassembly_api.mjs @@ -86,6 +86,27 @@ try { } assert(sawErrorThrow, 'Error import throws should escape the wasm call'); +const thrownStatus = { + name: 'ExitStatus', + message: 'Program terminated with exit(1)', + status: 1 +}; +let sawErrorlikeThrow = false; +try { + new WebAssembly.Instance(throwingModule, { + env: { + fail() { + throw thrownStatus; + } + } + }).exports.run(); +} catch (error) { + sawErrorlikeThrow = true; + assert(error === thrownStatus, 'import throws should preserve original error-like objects'); + assert(error.stack === undefined, 'error-like import throws should not grow a synthetic stack'); +} +assert(sawErrorlikeThrow, 'error-like import throws should escape the wasm call'); + const compiled = await WebAssembly.compile(incrementer); assert(compiled instanceof WebAssembly.Module, 'WebAssembly.compile() should resolve a module');