From: Dmitry Volyntsev Date: Wed, 4 Feb 2026 02:17:16 +0000 (-0800) Subject: Modules: fixed stack attach for native fetch exceptions for qjs. X-Git-Tag: 0.9.6~22 X-Git-Url: http://www.kaiwu.me/postgresql/commit/?a=commitdiff_plain;h=726bd811761618b800d683ba23cdff9ad0737f5b;p=njs.git Modules: fixed stack attach for native fetch exceptions for qjs. JS_ThrowInternalError() captures backtrace outside of JS call, resulting in empty "stack" string. Later, while unwinding, is_backtrace_needed() returns false because "stack" property exists. JS_NewError() does not try to attach stack, as a result a stack is attached later during unwinding. --- diff --git a/auto/quickjs b/auto/quickjs index f14617d5..630f9870 100644 --- a/auto/quickjs +++ b/auto/quickjs @@ -187,6 +187,33 @@ if [ $NJS_TRY_QUICKJS = YES ]; then . auto/feature + njs_feature="QuickJS JS_NewError() attaches stack" + njs_feature_run=value + njs_feature_name=NJS_HAVE_QUICKJS_NEW_ERROR_STACK + njs_feature_test="#include + + int main() { + int rc; + JSAtom atom; + JSValue err; + JSRuntime *rt; + JSContext *ctx; + + rt = JS_NewRuntime(); + ctx = JS_NewContext(rt); + err = JS_NewError(ctx); + atom = JS_NewAtom(ctx, \"stack\"); + rc = JS_HasProperty(ctx, err, atom); + printf(\"%d\", rc); + JS_FreeAtom(ctx, atom); + JS_FreeValue(ctx, err); + JS_FreeContext(ctx); + JS_FreeRuntime(rt); + return 0; + }" + + . auto/feature + njs_feature="QuickJS version" njs_feature_name=NJS_QUICKJS_VERSION njs_feature_run=value diff --git a/external/qjs_fs_module.c b/external/qjs_fs_module.c index af929db3..89c5281c 100644 --- a/external/qjs_fs_module.c +++ b/external/qjs_fs_module.c @@ -2805,7 +2805,7 @@ qjs_fs_error(JSContext *cx, const char *syscall, const char *description, { JSValue value; - value = JS_NewError(cx); + value = qjs_new_error(cx); if (JS_IsException(value)) { return JS_EXCEPTION; } diff --git a/nginx/ngx_qjs_fetch.c b/nginx/ngx_qjs_fetch.c index 731fba44..8e010bec 100644 --- a/nginx/ngx_qjs_fetch.c +++ b/nginx/ngx_qjs_fetch.c @@ -1195,13 +1195,30 @@ fail: static void ngx_qjs_fetch_error(ngx_js_http_t *http, const char *err) { + JSValue reason; ngx_qjs_fetch_t *fetch; fetch = (ngx_qjs_fetch_t *) http; - JS_ThrowInternalError(fetch->cx, "%s", err); + fetch->response_value = qjs_new_error(fetch->cx); + if (JS_IsException(fetch->response_value)) { + fetch->response_value = JS_UNDEFINED; + goto done; + } + + reason = JS_NewString(fetch->cx, err); + if (JS_IsException(reason)) { + goto done; + } + + if (JS_SetPropertyStr(fetch->cx, fetch->response_value, "message", + reason) < 0) + { + JS_FreeValue(fetch->cx, reason); + goto done; + } - fetch->response_value = JS_GetException(fetch->cx); +done: ngx_qjs_fetch_done(fetch, fetch->response_value, NGX_ERROR); } diff --git a/src/qjs.h b/src/qjs.h index 240e150b..0d911a86 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -138,6 +138,33 @@ static inline JS_BOOL JS_IsNullOrUndefined(JSValueConst v) || JS_VALUE_GET_TAG(v) == JS_TAG_UNDEFINED; } +/* + * QuickJS-NG attaches the "stack" property to an Error object too early, + * which results in empty stack trace when called from C code. + * Removing it allows the stack to be attached later during unwinding. + */ +static inline JSValue qjs_new_error2(JSContext *cx) +{ + JSAtom stack; + JSValue error; + + stack = JS_NewAtom(cx, "stack"); + if (stack == JS_ATOM_NULL) { + return JS_EXCEPTION; + } + + error = JS_NewError(cx); + if (JS_IsException(error)) { + JS_FreeAtom(cx, stack); + return JS_EXCEPTION; + } + + JS_DeleteProperty(cx, error, stack, 0); + JS_FreeAtom(cx, stack); + + return error; +} + #ifdef NJS_HAVE_QUICKJS_IS_SAME_VALUE #define qjs_is_same_value(cx, a, b) JS_IsSameValue(cx, a, b) #else @@ -156,6 +183,12 @@ static inline JS_BOOL JS_IsNullOrUndefined(JSValueConst v) #define qjs_is_error(cx, a) JS_IsError(cx, a) #endif +#ifdef NJS_HAVE_QUICKJS_NEW_ERROR_STACK +#define qjs_new_error(cx) qjs_new_error2(cx) +#else +#define qjs_new_error(cx) JS_NewError(cx) +#endif + extern qjs_module_t *qjs_modules[]; #endif /* _QJS_H_INCLUDED_ */