From: Dmitry Volyntsev Date: Sat, 7 Feb 2026 00:29:20 +0000 (-0800) Subject: Attach JS stack trace for exceptions thrown by C code. X-Git-Tag: 0.9.6~21 X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=0b9cf986aa87b7f17f274e615904351ac065051f;p=njs.git Attach JS stack trace for exceptions thrown by C code. This fixes #1019 issue on Github. --- diff --git a/nginx/t/js_fetch_error_stack.t b/nginx/t/js_fetch_error_stack.t new file mode 100644 index 00000000..8fc4900f --- /dev/null +++ b/nginx/t/js_fetch_error_stack.t @@ -0,0 +1,111 @@ +#!/usr/bin/perl + +# (C) Dmitry Volyntsev +# (C) F5, Inc. + +# Tests for http njs module, fetch method error stack traces. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http/) + ->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + js_import test.js; + + server { + listen 127.0.0.1:8080; + server_name localhost; + + location /testAsync { + js_content test.testAsync; + } + + location /testSync { + js_content test.testSync; + } + + location /testAsyncThrow { + js_content test.testAsyncThrow; + } + } +} + +EOF + +$t->write_file('test.js', <<'EOF'); + async function testAsync(r) { + try { + await ngx.fetch('http://127.0.0.1:12345'); + + } catch(e) { + r.return(200, `message: ${e.message}\n stack: ${e.stack}\n`); + } + } + + function testSync(r) { + try { + throw Error('oops'); + + } catch(e) { + r.return(200, `message: ${e.message}\n stack: ${e.stack}\n`); + } + } + + async function testAsyncThrow(r) { + try { + throw Error('oops'); + + } catch(e) { + r.return(200, `message: ${e.message}\n stack: ${e.stack}\n`); + } + } + + export default { testAsync, testSync, testAsyncThrow } +EOF + +$t->try_run('no njs'); + +$t->plan(6); + +############################################################################### + +like(http_get('/testSync'), qr/message: oops/s, 'sync stack exists'); +like(http_get('/testSync'), qr/at testSync \([^)]*test\.js:12/s, + 'sync stack line number'); + +like(http_get('/testAsync'), qr/message: connect failed/s, + 'async stack exists'); +like(http_get('/testAsync'), qr/at testAsync \([^)]*test\.js:3/s, + 'async stack line number'); + +like(http_get('/testAsyncThrow'), qr/message: oops/s, + 'async throw stack exists'); +like(http_get('/testAsyncThrow'), qr/at testAsyncThrow \([^)]*test\.js:21/s, + 'async throw stack line number'); + +############################################################################### diff --git a/src/njs_async.c b/src/njs_async.c index c352e86e..89f7f384 100644 --- a/src/njs_async.c +++ b/src/njs_async.c @@ -82,6 +82,7 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, vm->active_frame = async_frame; if (exception) { + ctx->throw_flag = 1; njs_vm_throw(vm, value); } else { @@ -128,27 +129,6 @@ njs_int_t njs_await_rejected(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval) { - njs_value_t *value; - njs_async_ctx_t *ctx; - - ctx = vm->top_frame->function->context; - - value = njs_arg(args, nargs, 1); - - if (ctx->await->native.pc == ctx->pc) { - /* No catch block was set before await. */ - (void) njs_function_call(vm, njs_function(&ctx->capability->reject), - &njs_value_undefined, value, 1, retval); - - njs_async_context_free(vm, ctx); - - return NJS_ERROR; - } - - /* ctx->await->native.pc points to a catch block here. */ - - ctx->pc = ctx->await->native.pc; - return njs_await_fulfilled(vm, args, nargs, 1, retval); } diff --git a/src/njs_async.h b/src/njs_async.h index 3addb2d6..ca7df2ac 100644 --- a/src/njs_async.h +++ b/src/njs_async.h @@ -13,6 +13,7 @@ typedef struct { njs_frame_t *await; uintptr_t index; u_char *pc; + int throw_flag; } njs_async_ctx_t; diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c index 6d991042..2dac79e6 100644 --- a/src/njs_vmcode.c +++ b/src/njs_vmcode.c @@ -229,6 +229,10 @@ njs_vmcode_interpreter(njs_vm_t *vm, u_char *pc, njs_value_t *rval, #endif + if (async_ctx != NULL && ((njs_async_ctx_t *) async_ctx)->throw_flag) { + goto error; + } + vmcode = (njs_vmcode_generic_t *) pc; NEXT_LBL; @@ -2722,7 +2726,6 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, { size_t size; njs_int_t ret; - njs_frame_t *frame; njs_value_t ctor, val, on_fulfilled, on_rejected, *value, retval; njs_function_t *fulfilled, *rejected; njs_native_frame_t *active; @@ -2773,15 +2776,7 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await, ctx->pc = (u_char *) await + sizeof(njs_vmcode_await_t); ctx->index = await->retval; - - frame = (njs_frame_t *) active; - - if (frame->exception.catch != NULL) { - ctx->await->native.pc = frame->exception.catch; - - } else { - ctx->await->native.pc = ctx->pc; - } + ctx->throw_flag = 0; fulfilled->context = ctx; fulfilled->args_count = 1;