From c4676a21fe14ce40ca79dd3315bd0ea09fe29997 Mon Sep 17 00:00:00 2001 From: Robert Fancsik Date: Mon, 18 Jan 2021 14:59:42 +0100 Subject: [PATCH] Introduce jerry_port_track_promise_rejection (#4451) This patch resolves #2931. JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu --- docs/05.PORT-API.md | 55 +++++++++++++++++++ .../ecma/operations/ecma-promise-object.c | 22 ++++++++ .../ecma/operations/ecma-promise-object.h | 1 + jerry-core/include/jerryscript-port.h | 22 ++++++++ jerry-port/default/CMakeLists.txt | 1 + jerry-port/default/default-promise.c | 39 +++++++++++++ targets/esp-idf/promise.c | 45 +++++++++++++++ targets/nuttx-stm32f4/jerry_port.c | 29 ++++++++++ 8 files changed, 214 insertions(+) create mode 100644 jerry-port/default/default-promise.c create mode 100644 targets/esp-idf/promise.c diff --git a/docs/05.PORT-API.md b/docs/05.PORT-API.md index 1f96482ee..ab058eee7 100644 --- a/docs/05.PORT-API.md +++ b/docs/05.PORT-API.md @@ -151,6 +151,32 @@ jerry_port_get_native_module (jerry_value_t name) /**< module specifier */ } ``` +## Promise + +```c +/** + * HostPromiseRejectionTracker operations + */ +typedef enum +{ + JERRY_PROMISE_REJECTION_OPERATION_REJECT, /**< promise is rejected without any handlers */ + JERRY_PROMISE_REJECTION_OPERATION_HANDLE, /**< handler is added to a rejected promise for the first time */ +} jerry_promise_rejection_operation_t; + +/** + * Track unhandled promise rejections. + * + * Note: + * This port function is called by jerry-core when JERRY_BUILTIN_PROMISE + * is enabled. + * + * @param promise rejected promise + * @param operation HostPromiseRejectionTracker operation + */ +void jerry_port_track_promise_rejection (const jerry_value_t promise, + const jerry_promise_rejection_operation_t operation); +``` + ## Date ```c @@ -392,3 +418,32 @@ void jerry_port_sleep (uint32_t sleep_time) } /* jerry_port_sleep */ #endif /* defined (JERRY_DEBUGGER) && (JERRY_DEBUGGER == 1) */ ``` + +## Promise + +```c +#include "jerryscript-port.h" + +/** + * Default implementation of jerry_port_track_promise_rejection. + * Prints the reason of the unhandled rejections. + */ +void +jerry_port_track_promise_rejection (const jerry_value_t promise, /**< rejected promise */ + const jerry_promise_rejection_operation_t operation) /**< operation */ +{ + (void) operation; /* unused */ + + jerry_value_t reason = jerry_get_promise_result (promise); + jerry_value_t reason_to_string = jerry_value_to_string (reason); + jerry_size_t req_sz = jerry_get_utf8_string_size (reason_to_string); + jerry_char_t str_buf_p[req_sz + 1]; + jerry_string_to_utf8_char_buffer (reason_to_string, str_buf_p, req_sz); + str_buf_p[req_sz] = '\0'; + + jerry_release_value (reason_to_string); + jerry_release_value (reason); + + printf ("Uncaught (in promise) %s\n", str_buf_p); +} /* jerry_port_track_promise_rejection */ +``` diff --git a/jerry-core/ecma/operations/ecma-promise-object.c b/jerry-core/ecma/operations/ecma-promise-object.c index 13b512ae1..8e0b7cb31 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.c +++ b/jerry-core/ecma/operations/ecma-promise-object.c @@ -181,6 +181,23 @@ ecma_is_resolver_already_called (ecma_object_t *promise_obj_p) /**< promise */ return (ecma_promise_get_flags (promise_obj_p) & ECMA_PROMISE_ALREADY_RESOLVED) != 0; } /* ecma_is_resolver_already_called */ +/** + * HostPromiseRejectionTracker + * + * See also: ES11 25.6.1.9 + */ +static void +ecma_track_promise_rejection (ecma_object_t *obj_p, /**< rejected promise */ + jerry_promise_rejection_operation_t operation) /**< operation */ +{ + JERRY_ASSERT (ecma_is_promise (obj_p)); + + if (!(ecma_promise_get_flags (obj_p) & ECMA_PROMISE_HANDLED)) + { + jerry_port_track_promise_rejection (ecma_make_object_value (obj_p), operation); + } +} /* ecma_track_promise_rejection */ + /** * Reject a Promise with a reason. * @@ -208,6 +225,7 @@ ecma_reject_promise (ecma_value_t promise, /**< promise */ promise_p->reactions = ecma_new_collection (); ecma_collection_destroy (reactions); + ecma_track_promise_rejection (obj_p, JERRY_PROMISE_REJECTION_OPERATION_REJECT); } /* ecma_reject_promise */ /** @@ -818,10 +836,14 @@ ecma_promise_do_then (ecma_value_t promise, /**< the promise which call 'then' * { /* 9. */ ecma_value_t reason = ecma_promise_get_result (promise_obj_p); + ecma_track_promise_rejection (promise_obj_p, JERRY_PROMISE_REJECTION_OPERATION_HANDLE); ecma_enqueue_promise_reaction_job (ecma_make_object_value (result_capability_obj_p), on_rejected, reason); ecma_free_value (reason); } + /* ES11: 11. */ + promise_p->header.u.class_prop.extra_info |= ECMA_PROMISE_HANDLED; + /* 10. */ return ecma_copy_value (capability_p->header.u.class_prop.u.promise); } /* ecma_promise_do_then */ diff --git a/jerry-core/ecma/operations/ecma-promise-object.h b/jerry-core/ecma/operations/ecma-promise-object.h index 658dc2003..3a3026a5d 100644 --- a/jerry-core/ecma/operations/ecma-promise-object.h +++ b/jerry-core/ecma/operations/ecma-promise-object.h @@ -34,6 +34,7 @@ typedef enum ECMA_PROMISE_IS_PENDING = (1 << 0), /**< pending state */ ECMA_PROMISE_IS_FULFILLED = (1 << 1), /**< fulfilled state */ ECMA_PROMISE_ALREADY_RESOLVED = (1 << 2), /**< already resolved */ + ECMA_PROMISE_HANDLED = (1 << 3), /**< ES11: 25.6.6 [[PromiseIsHandled]] internal slot */ } ecma_promise_flags_t; /** diff --git a/jerry-core/include/jerryscript-port.h b/jerry-core/include/jerryscript-port.h index 8ac4414aa..1f22ef080 100644 --- a/jerry-core/include/jerryscript-port.h +++ b/jerry-core/include/jerryscript-port.h @@ -252,6 +252,28 @@ size_t jerry_port_normalize_path (const char *in_path_p, */ jerry_value_t jerry_port_get_native_module (jerry_value_t name); +/** + * HostPromiseRejectionTracker operations + */ +typedef enum +{ + JERRY_PROMISE_REJECTION_OPERATION_REJECT, /**< promise is rejected without any handlers */ + JERRY_PROMISE_REJECTION_OPERATION_HANDLE, /**< handler is added to a rejected promise for the first time */ +} jerry_promise_rejection_operation_t; + +/** + * Track unhandled promise rejections. + * + * Note: + * This port function is called by jerry-core when JERRY_BUILTIN_PROMISE + * is enabled. + * + * @param promise rejected promise + * @param operation HostPromiseRejectionTracker operation + */ +void jerry_port_track_promise_rejection (const jerry_value_t promise, + const jerry_promise_rejection_operation_t operation); + /** * @} */ diff --git a/jerry-port/default/CMakeLists.txt b/jerry-port/default/CMakeLists.txt index cdaacd427..3560b072f 100644 --- a/jerry-port/default/CMakeLists.txt +++ b/jerry-port/default/CMakeLists.txt @@ -27,6 +27,7 @@ set(SOURCE_PORT_DEFAULT default-fatal.c default-io.c default-module.c + default-promise.c ) # Amalgamated JerryScript source/header build. diff --git a/jerry-port/default/default-promise.c b/jerry-port/default/default-promise.c new file mode 100644 index 000000000..fec8e1859 --- /dev/null +++ b/jerry-port/default/default-promise.c @@ -0,0 +1,39 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jerryscript-port.h" + +/** + * Default implementation of jerry_port_track_promise_rejection. + * Prints the reason of the unhandled rejections. + */ +void +jerry_port_track_promise_rejection (const jerry_value_t promise, /**< rejected promise */ + const jerry_promise_rejection_operation_t operation) /**< operation */ +{ + (void) operation; /* unused */ + + jerry_value_t reason = jerry_get_promise_result (promise); + jerry_value_t reason_to_string = jerry_value_to_string (reason); + jerry_size_t req_sz = jerry_get_utf8_string_size (reason_to_string); + JERRY_VLA (jerry_char_t, str_buf_p, req_sz + 1); + jerry_string_to_utf8_char_buffer (reason_to_string, str_buf_p, req_sz); + str_buf_p[req_sz] = '\0'; + + jerry_release_value (reason_to_string); + jerry_release_value (reason); + + jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Uncaught (in promise) %s\n", str_buf_p); +} /* jerry_port_track_promise_rejection */ diff --git a/targets/esp-idf/promise.c b/targets/esp-idf/promise.c new file mode 100644 index 000000000..543c13439 --- /dev/null +++ b/targets/esp-idf/promise.c @@ -0,0 +1,45 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jerryscript-port.h" + +/** + * Track unhandled promise rejections. + * + * Note: + * This port function is called by jerry-core when JERRY_BUILTIN_PROMISE + * is enabled. + * + * @param promise rejected promise + * @param operation HostPromiseRejectionTracker operation + */ +void +jerry_port_track_promise_rejection (const jerry_value_t promise, + const jerry_promise_rejection_operation_t operation) +{ + (void) operation; /* unused */ + + jerry_value_t reason = jerry_get_promise_result (promise); + jerry_value_t reason_to_string = jerry_value_to_string (reason); + jerry_size_t req_sz = jerry_get_utf8_string_size (reason_to_string); + jerry_char_t str_buf_p[req_sz + 1]; + jerry_string_to_utf8_char_buffer (reason_to_string, str_buf_p, req_sz); + str_buf_p[req_sz] = '\0'; + + jerry_release_value (reason_to_string); + jerry_release_value (reason); + + jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Uncaught (in promise) %s\n", str_buf_p); +} /* jerry_port_track_promise_rejection */ diff --git a/targets/nuttx-stm32f4/jerry_port.c b/targets/nuttx-stm32f4/jerry_port.c index bdf21f7dd..39af6e9c0 100644 --- a/targets/nuttx-stm32f4/jerry_port.c +++ b/targets/nuttx-stm32f4/jerry_port.c @@ -227,3 +227,32 @@ jerry_port_get_current_context (void) { return current_context_p; } /* jerry_port_get_current_context */ + +/** + * Track unhandled promise rejections. + * + * Note: + * This port function is called by jerry-core when JERRY_BUILTIN_PROMISE + * is enabled. + * + * @param promise rejected promise + * @param operation HostPromiseRejectionTracker operation + */ +void +jerry_port_track_promise_rejection (const jerry_value_t promise, + const jerry_promise_rejection_operation_t operation) +{ + (void) operation; /* unused */ + + jerry_value_t reason = jerry_get_promise_result (promise); + jerry_value_t reason_to_string = jerry_value_to_string (reason); + jerry_size_t req_sz = jerry_get_utf8_string_size (reason_to_string); + jerry_char_t str_buf_p[req_sz + 1]; + jerry_string_to_utf8_char_buffer (reason_to_string, str_buf_p, req_sz); + str_buf_p[req_sz] = '\0'; + + jerry_release_value (reason_to_string); + jerry_release_value (reason); + + jerry_port_log (JERRY_LOG_LEVEL_WARNING, "Uncaught (in promise) %s\n", str_buf_p); +} /* jerry_port_track_promise_rejection */