details: https://hg.nginx.org/njs/rev/8aad26845b18 branches: changeset: 2269:8aad26845b18 user: Dmitry Volyntsev <xei...@nginx.com> date: Thu Jan 18 18:03:35 2024 -0800 description: Moving out HostPromiseRejectionTracker from njs core.
HostPromiseRejectionTracker should be implemented by host environment according to ECMAScript specs. The following method was removed: njs_vm_unhandled_rejection(). The following method was introduced: njs_vm_set_rejection_tracker(). diffstat: external/njs_shell.c | 103 +++++++++++++++++++++++++++++++++++++++++- nginx/ngx_http_js_module.c | 1 - nginx/ngx_js.c | 101 +++++++++++++++++++++++++++++++++++++++-- nginx/ngx_js.h | 2 + nginx/ngx_stream_js_module.c | 1 - src/njs.h | 15 ++--- src/njs_promise.c | 74 ++++-------------------------- src/njs_value.c | 7 ++ src/njs_vm.c | 40 +++------------ src/njs_vm.h | 4 +- 10 files changed, 230 insertions(+), 118 deletions(-) diffs (651 lines): diff -r da8b044e1c61 -r 8aad26845b18 external/njs_shell.c --- a/external/njs_shell.c Thu Jan 18 18:03:24 2024 -0800 +++ b/external/njs_shell.c Thu Jan 18 18:03:35 2024 -0800 @@ -93,6 +93,12 @@ typedef struct { typedef struct { + void *promise; + njs_opaque_value_t message; +} njs_rejected_promise_t; + + +typedef struct { njs_vm_t *vm; uint32_t event_id; @@ -101,6 +107,8 @@ typedef struct { njs_queue_t labels; + njs_arr_t *rejected_promises; + njs_bool_t suppress_stdout; njs_completion_t completion; @@ -422,7 +430,7 @@ njs_options_parse(njs_opts_t *opts, int opts->denormals = 1; opts->exit_code = EXIT_FAILURE; - opts->unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW; + opts->unhandled_rejection = 1; p = getenv("NJS_EXIT_CODE"); if (p != NULL) { @@ -528,7 +536,7 @@ njs_options_parse(njs_opts_t *opts, int break; case 'r': - opts->unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE; + opts->unhandled_rejection = 0; break; case 's': @@ -636,6 +644,8 @@ njs_console_init(njs_vm_t *vm, njs_conso njs_queue_init(&console->posted_events); njs_queue_init(&console->labels); + console->rejected_promises = NULL; + console->completion.completions = njs_vm_completions(vm, NULL); if (console->completion.completions == NULL) { return NJS_ERROR; @@ -749,6 +759,53 @@ njs_externals_init(njs_vm_t *vm) } +static void +njs_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t external, + njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason) +{ + void *promise_obj; + uint32_t i, length; + njs_console_t *console; + njs_rejected_promise_t *rejected_promise; + + console = external; + + if (is_handled && console->rejected_promises != NULL) { + rejected_promise = console->rejected_promises->start; + length = console->rejected_promises->items; + + promise_obj = njs_value_ptr(promise); + + for (i = 0; i < length; i++) { + if (rejected_promise[i].promise == promise_obj) { + njs_arr_remove(console->rejected_promises, + &rejected_promise[i]); + + break; + } + } + + return; + } + + if (console->rejected_promises == NULL) { + console->rejected_promises = njs_arr_create(njs_vm_memory_pool(vm), 4, + sizeof(njs_rejected_promise_t)); + if (njs_slow_path(console->rejected_promises == NULL)) { + return; + } + } + + rejected_promise = njs_arr_add(console->rejected_promises); + if (njs_slow_path(rejected_promise == NULL)) { + return; + } + + rejected_promise->promise = njs_value_ptr(promise); + njs_value_assign(&rejected_promise->message, reason); +} + + static njs_vm_t * njs_create_vm(njs_opts_t *opts) { @@ -784,7 +841,6 @@ njs_create_vm(njs_opts_t *opts) vm_options.argv = opts->argv; vm_options.argc = opts->argc; vm_options.ast = opts->ast; - vm_options.unhandled_rejection = opts->unhandled_rejection; if (opts->stack_size != 0) { vm_options.max_stack_size = opts->stack_size; @@ -796,6 +852,11 @@ njs_create_vm(njs_opts_t *opts) return NULL; } + if (opts->unhandled_rejection) { + njs_vm_set_rejection_tracker(vm, njs_rejection_tracker, + njs_vm_external_ptr(vm)); + } + for (i = 0; i < opts->n_paths; i++) { path.start = (u_char *) opts->paths[i]; path.length = njs_strlen(opts->paths[i]); @@ -914,6 +975,40 @@ njs_process_events(void *runtime) static njs_int_t +njs_unhandled_rejection(void *runtime) +{ + njs_int_t ret; + njs_str_t message; + njs_console_t *console; + njs_rejected_promise_t *rejected_promise; + + console = runtime; + + if (console->rejected_promises == NULL + || console->rejected_promises->items == 0) + { + return 0; + } + + rejected_promise = console->rejected_promises->start; + + ret = njs_vm_value_to_string(console->vm, &message, + njs_value_arg(&rejected_promise->message)); + if (njs_slow_path(ret != NJS_OK)) { + return -1; + } + + njs_vm_error(console->vm, "unhandled promise rejection: %V", + &message); + + njs_arr_destroy(console->rejected_promises); + console->rejected_promises = NULL; + + return 1; +} + + +static njs_int_t njs_read_file(njs_opts_t *opts, njs_str_t *content) { int fd; @@ -1112,7 +1207,7 @@ njs_process_script(njs_vm_t *vm, void *r } } - if (njs_vm_unhandled_rejection(vm)) { + if (njs_unhandled_rejection(runtime)) { njs_process_output(vm, NULL, NJS_ERROR); if (!njs_vm_options(vm)->interactive) { diff -r da8b044e1c61 -r 8aad26845b18 nginx/ngx_http_js_module.c --- a/nginx/ngx_http_js_module.c Thu Jan 18 18:03:24 2024 -0800 +++ b/nginx/ngx_http_js_module.c Thu Jan 18 18:03:35 2024 -0800 @@ -4505,7 +4505,6 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_http_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; options.backtrace = 1; - options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW; options.metas = &ngx_http_js_metas; options.addons = njs_http_js_addon_modules; options.argv = ngx_argv; diff -r da8b044e1c61 -r 8aad26845b18 nginx/ngx_js.c --- a/nginx/ngx_js.c Thu Jan 18 18:03:24 2024 -0800 +++ b/nginx/ngx_js.c Thu Jan 18 18:03:35 2024 -0800 @@ -12,17 +12,23 @@ typedef struct { - ngx_queue_t labels; + ngx_queue_t labels; } ngx_js_console_t; typedef struct { - njs_str_t name; - uint64_t time; - ngx_queue_t queue; + njs_str_t name; + uint64_t time; + ngx_queue_t queue; } ngx_js_timelabel_t; +typedef struct { + void *promise; + njs_opaque_value_t message; +} ngx_js_rejected_promise_t; + + static njs_int_t ngx_js_ext_build(njs_vm_t *vm, njs_object_prop_t *prop, njs_value_t *value, njs_value_t *setval, njs_value_t *retval); static njs_int_t ngx_js_ext_conf_file_path(njs_vm_t *vm, @@ -49,6 +55,7 @@ static njs_int_t njs_set_immediate(njs_v njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_clear_timeout(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); +static njs_int_t ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx); static void ngx_js_cleanup_vm(void *data); static njs_int_t ngx_js_core_init(njs_vm_t *vm); @@ -429,15 +436,15 @@ ngx_js_name_invoke(njs_vm_t *vm, ngx_str } } - if (njs_vm_unhandled_rejection(vm)) { + ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); + + if (ngx_js_unhandled_rejection(ctx)) { ngx_js_exception(vm, &exception); ngx_log_error(NGX_LOG_ERR, log, 0, "js exception: %V", &exception); return NGX_ERROR; } - ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); - return njs_rbtree_is_empty(&ctx->waiting_events) ? NGX_OK : NGX_AGAIN; } @@ -1661,6 +1668,53 @@ ngx_js_merge_vm(ngx_conf_t *cf, ngx_js_l } +static void +ngx_js_rejection_tracker(njs_vm_t *vm, njs_external_ptr_t unused, + njs_bool_t is_handled, njs_value_t *promise, njs_value_t *reason) +{ + void *promise_obj; + uint32_t i, length; + ngx_js_ctx_t *ctx; + ngx_js_rejected_promise_t *rejected_promise; + + ctx = ngx_external_ctx(vm, njs_vm_external_ptr(vm)); + + if (is_handled && ctx->rejected_promises != NULL) { + rejected_promise = ctx->rejected_promises->start; + length = ctx->rejected_promises->items; + + promise_obj = njs_value_ptr(promise); + + for (i = 0; i < length; i++) { + if (rejected_promise[i].promise == promise_obj) { + njs_arr_remove(ctx->rejected_promises, + &rejected_promise[i]); + + break; + } + } + + return; + } + + if (ctx->rejected_promises == NULL) { + ctx->rejected_promises = njs_arr_create(njs_vm_memory_pool(vm), 4, + sizeof(ngx_js_rejected_promise_t)); + if (njs_slow_path(ctx->rejected_promises == NULL)) { + return; + } + } + + rejected_promise = njs_arr_add(ctx->rejected_promises); + if (njs_slow_path(rejected_promise == NULL)) { + return; + } + + rejected_promise->promise = njs_value_ptr(promise); + njs_value_assign(&rejected_promise->message, reason); +} + + ngx_int_t ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf, njs_vm_opt_t *options) @@ -1738,6 +1792,9 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_ cln->handler = ngx_js_cleanup_vm; cln->data = conf; + njs_vm_set_rejection_tracker(conf->vm, ngx_js_rejection_tracker, + NULL); + path.start = ngx_cycle->conf_prefix.data; path.length = ngx_cycle->conf_prefix.len; @@ -1810,6 +1867,36 @@ ngx_js_init_conf_vm(ngx_conf_t *cf, ngx_ } +static njs_int_t +ngx_js_unhandled_rejection(ngx_js_ctx_t *ctx) +{ + njs_int_t ret; + njs_str_t message; + ngx_js_rejected_promise_t *rejected_promise; + + if (ctx->rejected_promises == NULL + || ctx->rejected_promises->items == 0) + { + return 0; + } + + rejected_promise = ctx->rejected_promises->start; + + ret = njs_vm_value_to_string(ctx->vm, &message, + njs_value_arg(&rejected_promise->message)); + if (njs_slow_path(ret != NJS_OK)) { + return -1; + } + + njs_vm_error(ctx->vm, "unhandled promise rejection: %V", &message); + + njs_arr_destroy(ctx->rejected_promises); + ctx->rejected_promises = NULL; + + return 1; +} + + static void ngx_js_cleanup_vm(void *data) { diff -r da8b044e1c61 -r 8aad26845b18 nginx/ngx_js.h --- a/nginx/ngx_js.h Thu Jan 18 18:03:24 2024 -0800 +++ b/nginx/ngx_js.h Thu Jan 18 18:03:35 2024 -0800 @@ -15,6 +15,7 @@ #include <ngx_event.h> #include <njs.h> #include <njs_rbtree.h> +#include <njs_arr.h> #include "ngx_js_fetch.h" #include "ngx_js_shared_dict.h" @@ -111,6 +112,7 @@ struct ngx_js_event_s { #define NGX_JS_COMMON_CTX \ njs_vm_t *vm; \ + njs_arr_t *rejected_promises; \ njs_rbtree_t waiting_events; \ ngx_socket_t event_id diff -r da8b044e1c61 -r 8aad26845b18 nginx/ngx_stream_js_module.c --- a/nginx/ngx_stream_js_module.c Thu Jan 18 18:03:24 2024 -0800 +++ b/nginx/ngx_stream_js_module.c Thu Jan 18 18:03:35 2024 -0800 @@ -1778,7 +1778,6 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *c ngx_stream_js_uptr[NGX_JS_MAIN_CONF_INDEX] = (uintptr_t) jmcf; options.backtrace = 1; - options.unhandled_rejection = NJS_VM_OPT_UNHANDLED_REJECTION_THROW; options.metas = &ngx_stream_js_metas; options.addons = njs_stream_js_addon_modules; options.argv = ngx_argv; diff -r da8b044e1c61 -r 8aad26845b18 src/njs.h --- a/src/njs.h Thu Jan 18 18:03:24 2024 -0800 +++ b/src/njs.h Thu Jan 18 18:03:35 2024 -0800 @@ -196,6 +196,9 @@ typedef void * njs_ typedef njs_mod_t *(*njs_module_loader_t)(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name); +typedef void (*njs_rejection_tracker_t)(njs_vm_t *vm, + njs_external_ptr_t external, njs_bool_t is_handled, njs_value_t *promise, + njs_value_t *reason); typedef struct { @@ -225,9 +228,6 @@ typedef struct { njs_uint_t max_stack_size; -#define NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE 0 -#define NJS_VM_OPT_UNHANDLED_REJECTION_THROW 1 - /* * interactive - enables "interactive" mode. * (REPL). Allows starting parent VM without cloning. @@ -240,9 +240,6 @@ typedef struct { * - Function constructors. * module - ES6 "module" mode. Script mode is default. * ast - print AST. - * unhandled_rejection IGNORE | THROW - tracks unhandled promise rejections: - * - throwing inside a Promise without a catch block. - * - throwing inside in a finally or catch block. */ uint8_t interactive; /* 1 bit */ uint8_t trailer; /* 1 bit */ @@ -260,7 +257,6 @@ typedef struct { #ifdef NJS_DEBUG_GENERATOR uint8_t generator_debug; /* 1 bit */ #endif - uint8_t unhandled_rejection; } njs_vm_opt_t; @@ -304,7 +300,9 @@ NJS_EXPORT njs_int_t njs_vm_enqueue_job( */ NJS_EXPORT njs_int_t njs_vm_execute_pending_job(njs_vm_t *vm); NJS_EXPORT njs_int_t njs_vm_pending(njs_vm_t *vm); -NJS_EXPORT njs_int_t njs_vm_unhandled_rejection(njs_vm_t *vm); + +NJS_EXPORT void njs_vm_set_rejection_tracker(njs_vm_t *vm, + njs_rejection_tracker_t rejection_tracker, void *opaque); NJS_EXPORT void *njs_vm_completions(njs_vm_t *vm, njs_str_t *expression); @@ -468,6 +466,7 @@ NJS_EXPORT double njs_value_number(const NJS_EXPORT njs_function_t *njs_value_function(const njs_value_t *value); NJS_EXPORT njs_function_native_t njs_value_native_function( const njs_value_t *value); +NJS_EXPORT void *njs_value_ptr(const njs_value_t *value); njs_external_ptr_t njs_value_external(const njs_value_t *value); NJS_EXPORT njs_int_t njs_value_external_tag(const njs_value_t *value); diff -r da8b044e1c61 -r 8aad26845b18 src/njs_promise.c --- a/src/njs_promise.c Thu Jan 18 18:03:24 2024 -0800 +++ b/src/njs_promise.c Thu Jan 18 18:03:35 2024 -0800 @@ -61,8 +61,6 @@ static njs_int_t njs_promise_value_const static njs_int_t njs_promise_capability_executor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); -static njs_int_t njs_promise_host_rejection_tracker(njs_vm_t *vm, - njs_promise_t *promise, njs_promise_rejection_type_t operation); static njs_int_t njs_promise_resolve_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t unused, njs_value_t *retval); static njs_int_t njs_promise_reject_function(njs_vm_t *vm, njs_value_t *args, @@ -513,8 +511,8 @@ njs_promise_fulfill(njs_vm_t *vm, njs_pr njs_inline njs_value_t * njs_promise_reject(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *reason) { - njs_int_t ret; njs_queue_t queue; + njs_value_t promise_value; njs_promise_data_t *data; data = njs_data(&promise->value); @@ -523,10 +521,10 @@ njs_promise_reject(njs_vm_t *vm, njs_pro data->state = NJS_PROMISE_REJECTED; if (!data->is_handled) { - ret = njs_promise_host_rejection_tracker(vm, promise, - NJS_PROMISE_REJECT); - if (njs_slow_path(ret != NJS_OK)) { - return njs_value_arg(&njs_value_null); + if (vm->rejection_tracker != NULL) { + njs_set_promise(&promise_value, promise); + vm->rejection_tracker(vm, vm->rejection_tracker_opaque, 0, + &promise_value, reason); } } @@ -548,58 +546,6 @@ njs_promise_reject(njs_vm_t *vm, njs_pro static njs_int_t -njs_promise_host_rejection_tracker(njs_vm_t *vm, njs_promise_t *promise, - njs_promise_rejection_type_t operation) -{ - uint32_t i, length; - njs_value_t *value; - njs_promise_data_t *data; - - if (vm->options.unhandled_rejection - == NJS_VM_OPT_UNHANDLED_REJECTION_IGNORE) - { - return NJS_OK; - } - - if (vm->promise_reason == NULL) { - vm->promise_reason = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE); - if (njs_slow_path(vm->promise_reason == NULL)) { - return NJS_ERROR; - } - } - - data = njs_data(&promise->value); - - if (operation == NJS_PROMISE_REJECT) { - if (vm->promise_reason != NULL) { - return njs_array_add(vm, vm->promise_reason, &data->result); - } - - } else { - value = vm->promise_reason->start; - length = vm->promise_reason->length; - - for (i = 0; i < length; i++) { - if (njs_values_same(&value[i], &data->result)) { - length--; - - if (i < length) { - memmove(&value[i], &value[i + 1], - sizeof(njs_value_t) * (length - i)); - } - - break; - } - } - - vm->promise_reason->length = length; - } - - return NJS_OK; -} - - -static njs_int_t njs_promise_invoke_then(njs_vm_t *vm, njs_value_t *promise, njs_value_t *args, njs_int_t nargs, njs_value_t *retval) { @@ -896,7 +842,7 @@ njs_promise_perform_then(njs_vm_t *vm, n njs_promise_capability_t *capability, njs_value_t *retval) { njs_int_t ret; - njs_value_t arguments[2]; + njs_value_t arguments[2], promise_value; njs_promise_t *promise; njs_function_t *function; njs_promise_data_t *data; @@ -949,10 +895,10 @@ njs_promise_perform_then(njs_vm_t *vm, n if (data->state == NJS_PROMISE_REJECTED) { njs_set_data(&arguments[0], rejected_reaction, 0); - ret = njs_promise_host_rejection_tracker(vm, promise, - NJS_PROMISE_HANDLE); - if (njs_slow_path(ret != NJS_OK)) { - return ret; + if (vm->rejection_tracker != NULL) { + njs_set_promise(&promise_value, promise); + vm->rejection_tracker(vm, vm->rejection_tracker_opaque, 1, + &promise_value, &data->result); } } else { diff -r da8b044e1c61 -r 8aad26845b18 src/njs_value.c --- a/src/njs_value.c Thu Jan 18 18:03:24 2024 -0800 +++ b/src/njs_value.c Thu Jan 18 18:03:35 2024 -0800 @@ -457,6 +457,13 @@ njs_value_native_function(const njs_valu } +void * +njs_value_ptr(const njs_value_t *value) +{ + return njs_data(value); +} + + njs_external_ptr_t njs_value_external(const njs_value_t *value) { diff -r da8b044e1c61 -r 8aad26845b18 src/njs_vm.c --- a/src/njs_vm.c Thu Jan 18 18:03:24 2024 -0800 +++ b/src/njs_vm.c Thu Jan 18 18:03:35 2024 -0800 @@ -627,37 +627,6 @@ njs_vm_pending(njs_vm_t *vm) njs_int_t -njs_vm_unhandled_rejection(njs_vm_t *vm) -{ - njs_int_t ret; - njs_str_t str; - njs_value_t string; - - if (!(vm->options.unhandled_rejection - == NJS_VM_OPT_UNHANDLED_REJECTION_THROW - && vm->promise_reason != NULL - && vm->promise_reason->length != 0)) - { - return 0; - } - - njs_value_assign(&string, &vm->promise_reason->start[0]); - ret = njs_value_to_string(vm, &string, &string); - if (njs_slow_path(ret != NJS_OK)) { - return ret; - } - - njs_string_get(&string, &str); - njs_vm_error(vm, "unhandled promise rejection: %V", &str); - - njs_mp_free(vm->mem_pool, vm->promise_reason); - vm->promise_reason = NULL; - - return 1; -} - - -njs_int_t njs_vm_enqueue_job(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args, njs_uint_t nargs) { @@ -738,6 +707,15 @@ njs_vm_set_module_loader(njs_vm_t *vm, n } +void +njs_vm_set_rejection_tracker(njs_vm_t *vm, + njs_rejection_tracker_t rejection_tracker, void *opaque) +{ + vm->rejection_tracker = rejection_tracker; + vm->rejection_tracker_opaque = opaque; +} + + njs_int_t njs_vm_add_path(njs_vm_t *vm, const njs_str_t *path) { diff -r da8b044e1c61 -r 8aad26845b18 src/njs_vm.h --- a/src/njs_vm.h Thu Jan 18 18:03:24 2024 -0800 +++ b/src/njs_vm.h Thu Jan 18 18:03:35 2024 -0800 @@ -160,8 +160,6 @@ struct njs_vm_s { njs_regex_compile_ctx_t *regex_compile_ctx; njs_regex_match_data_t *single_match_data; - njs_array_t *promise_reason; - njs_parser_scope_t *global_scope; /* @@ -185,6 +183,8 @@ struct njs_vm_s { njs_module_loader_t module_loader; void *module_loader_opaque; + njs_rejection_tracker_t rejection_tracker; + void *rejection_tracker_opaque; }; _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org https://mailman.nginx.org/mailman/listinfo/nginx-devel