From: Dmitry Volyntsev Date: Fri, 19 Jan 2024 02:03:35 +0000 (-0800) Subject: Moving out HostPromiseRejectionTracker from njs core. X-Git-Tag: 0.8.3~12 X-Git-Url: http://www.kaiwu.me/postgresql/commit/static/gitweb.js?a=commitdiff_plain;h=6dae86bc2d822e64cd83d057b48077c2691006d4;p=njs.git 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(). --- diff --git a/external/njs_shell.c b/external/njs_shell.c index ba68f0f8..98d3caff 100644 --- a/external/njs_shell.c +++ b/external/njs_shell.c @@ -92,6 +92,12 @@ typedef struct { } njs_timelabel_t; +typedef struct { + void *promise; + njs_opaque_value_t message; +} njs_rejected_promise_t; + + typedef struct { njs_vm_t *vm; @@ -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 argc, char **argv) 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 argc, char **argv) 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_console_t *console) 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]); @@ -913,6 +974,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) { @@ -1112,7 +1207,7 @@ njs_process_script(njs_vm_t *vm, void *runtime, const njs_str_t *script) } } - 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 --git a/nginx/ngx_http_js_module.c b/nginx/ngx_http_js_module.c index 5384bd0a..327f0f28 100644 --- a/nginx/ngx_http_js_module.c +++ b/nginx/ngx_http_js_module.c @@ -4505,7 +4505,6 @@ ngx_http_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) 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 --git a/nginx/ngx_js.c b/nginx/ngx_js.c index 6ebc873b..27fbfd29 100644 --- a/nginx/ngx_js.c +++ b/nginx/ngx_js.c @@ -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_vm_t *vm, njs_value_t *args, 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_t *fname, ngx_log_t *log, } } - 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_loc_conf_t *conf, } +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_js_loc_conf_t *conf, 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_js_loc_conf_t *conf, } +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 --git a/nginx/ngx_js.h b/nginx/ngx_js.h index a50c3266..1f95c7c3 100644 --- a/nginx/ngx_js.h +++ b/nginx/ngx_js.h @@ -15,6 +15,7 @@ #include #include #include +#include #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 --git a/nginx/ngx_stream_js_module.c b/nginx/ngx_stream_js_module.c index 65128365..f064ab04 100644 --- a/nginx/ngx_stream_js_module.c +++ b/nginx/ngx_stream_js_module.c @@ -1778,7 +1778,6 @@ ngx_stream_js_init_conf_vm(ngx_conf_t *cf, ngx_js_loc_conf_t *conf) 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 --git a/src/njs.h b/src/njs.h index f52ce766..57e3610b 100644 --- a/src/njs.h +++ b/src/njs.h @@ -196,6 +196,9 @@ typedef void * njs_external_ptr_t; 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_vm_t *vm, njs_function_t *function, */ 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_value_t *value); 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 --git a/src/njs_promise.c b/src/njs_promise.c index db128db5..54da0a02 100644 --- a/src/njs_promise.c +++ b/src/njs_promise.c @@ -61,8 +61,6 @@ static njs_int_t njs_promise_value_constructor(njs_vm_t *vm, njs_value_t *value, 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_promise_t *promise, njs_value_t *value) 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_promise_t *promise, njs_value_t *reason) 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); } } @@ -547,58 +545,6 @@ njs_promise_reject(njs_vm_t *vm, njs_promise_t *promise, njs_value_t *reason) } -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, njs_value_t *value, 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, njs_value_t *value, 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 --git a/src/njs_value.c b/src/njs_value.c index fff7196f..cf3f8ffc 100644 --- a/src/njs_value.c +++ b/src/njs_value.c @@ -457,6 +457,13 @@ njs_value_native_function(const njs_value_t *value) } +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 --git a/src/njs_vm.c b/src/njs_vm.c index 947b8ce6..656839da 100644 --- a/src/njs_vm.c +++ b/src/njs_vm.c @@ -626,37 +626,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, njs_module_loader_t module_loader, } +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 --git a/src/njs_vm.h b/src/njs_vm.h index a875ef6f..2d72f939 100644 --- a/src/njs_vm.h +++ b/src/njs_vm.h @@ -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; };