Add a callback which is called when Error objects are created (#4465)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2021-01-18 16:56:38 +01:00 committed by GitHub
parent e00964176d
commit 3b77117a2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 249 additions and 51 deletions

View File

@ -586,6 +586,33 @@ typedef void (*jerry_object_native_free_callback_t) (void *native_p);
- [jerry_object_native_info_t](#jerry_object_native_info_t)
- [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external)
## jerry_error_object_created_callback_t
**Summary**
Decorator callback for Error objects. This native callback is called every time when an Error
object is created and the decorator can create or update any properties of the newly created
Error object.
*Note*:
- The callback function cannot be called recursively, so the Error objects created
when the callback is running are not updated.
*New in version [[NEXT_RELEASE]]*.
**Prototype**
```c
typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error_object, void *user_p);
```
- `error_object` - the newly created Error object.
- `user_p` - pointer passed to [jerry_set_error_object_created_callback](#jerry_set_error_object_created_callback).
**See also**
- [jerry_set_error_object_created_callback](#jerry_set_error_object_created_callback)
## jerry_object_native_info_t
**Summary**
@ -2996,6 +3023,54 @@ jerry_get_value_from_error (jerry_value_t value, bool release)
- [jerry_create_error_from_value](#jerry_create_error_from_value)
- [jerry_create_abort_from_value](#jerry_create_abort_from_value)
## jerry_set_error_object_created_callback
**Summary**
Set the decorator callback for newly created Error objects. The operation of the callback
is described in [jerry_error_object_created_callback_t](#jerry_error_object_created_callback_t).
**Prototype**
```c
void jerry_set_error_object_created_callback (jerry_error_object_created_callback_t callback, void *user_p);
```
- `callback` - callback function, the previously set value is overwritten, and setting NULL
disables the operation
- `user_p` - pointer passed to the callback function, can be NULL
*New in version [[NEXT_RELEASE]]*.
**Example**
```c
static void
error_object_created_callback (const jerry_value_t error_object) /**< new error object */
void *user_p) /**< user pointer */
{
(void) error_object;
(void) user_p;
printf ("Notification: a new error is created\n");
} /* error_object_created_callback */
void main(void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_set_error_object_created_callback (error_object_created_callback, NULL);
jerry_release_value (jerry_create_error (JERRY_ERROR_COMMON,
(const jerry_char_t *) "Message"));
jerry_cleanup ();
} /* main */
```
**See also**
- [jerry_error_object_created_callback_t](#jerry_error_object_created_callback_t)
# Getter functions of 'jerry_value_t'
Get raw data from API values.

View File

@ -314,8 +314,8 @@ static_snapshot_error_unsupported_literal (snapshot_globals_t *globals_p, /**< s
ecma_deref_ecma_string (literal_string_p);
ecma_object_t *error_object_p = ecma_new_standard_error_with_message (ECMA_ERROR_RANGE,
ecma_stringbuilder_finalize (&builder));
ecma_object_t *error_object_p = ecma_new_standard_error (ECMA_ERROR_RANGE,
ecma_stringbuilder_finalize (&builder));
globals_p->snapshot_error = ecma_create_error_object_reference (error_object_p);
} /* static_snapshot_error_unsupported_literal */

View File

@ -1601,6 +1601,20 @@ jerry_get_value_from_error (jerry_value_t value, /**< api value */
return ret_val;
} /* jerry_get_value_from_error */
/**
* Set new decorator callback for Error objects. The decorator can
* create or update any properties of the newly created Error object.
*/
void
jerry_set_error_object_created_callback (jerry_error_object_created_callback_t callback, /**< new callback */
void *user_p) /**< user pointer passed to the callback */
{
jerry_assert_api_available ();
JERRY_CONTEXT (error_object_created_callback_p) = callback;
JERRY_CONTEXT (error_object_created_callback_user_p) = user_p;
} /* jerry_set_error_object_created_callback */
/**
* Return the type of the Error object if possible.
*
@ -1990,15 +2004,15 @@ jerry_create_error_sz (jerry_error_t error_type, /**< type of error */
if (message_p == NULL || message_size == 0)
{
return ecma_create_error_object_reference (ecma_new_standard_error ((ecma_standard_error_t) error_type));
return ecma_create_error_object_reference (ecma_new_standard_error ((ecma_standard_error_t) error_type, NULL));
}
else
{
ecma_string_t *message_string_p = ecma_new_ecma_string_from_utf8 ((lit_utf8_byte_t *) message_p,
(lit_utf8_size_t) message_size);
ecma_object_t *error_object_p = ecma_new_standard_error_with_message ((ecma_standard_error_t) error_type,
message_string_p);
ecma_object_t *error_object_p = ecma_new_standard_error ((ecma_standard_error_t) error_type,
message_string_p);
ecma_deref_ecma_string (message_string_p);

View File

@ -72,6 +72,7 @@ typedef enum
#endif /* ENABLED (JERRY_PROPRETY_HASHMAP) */
ECMA_STATUS_EXCEPTION = (1u << 3), /**< last exception is a normal exception */
ECMA_STATUS_ABORT = (1u << 4), /**< last exception is an abort */
ECMA_STATUS_ERROR_UPDATE = (1u << 5), /**< the error_object_created_callback_p is called */
} ecma_status_flag_t;
/**

View File

@ -53,14 +53,13 @@ ecma_builtin_helper_error_dispatch_call (ecma_standard_error_t error_type, /**<
return ECMA_VALUE_ERROR;
}
ecma_object_t *new_error_object_p = ecma_new_standard_error_with_message (error_type,
message_string_p);
ecma_object_t *new_error_object_p = ecma_new_standard_error (error_type, message_string_p);
ecma_deref_ecma_string (message_string_p);
return ecma_make_object_value (new_error_object_p);
}
ecma_object_t *new_error_object_p = ecma_new_standard_error (error_type);
ecma_object_t *new_error_object_p = ecma_new_standard_error (error_type, NULL);
return ecma_make_object_value (new_error_object_p);
} /* ecma_builtin_helper_error_dispatch_call */

View File

@ -69,6 +69,9 @@ const ecma_error_mapping_t ecma_error_mappings[] =
* Standard ecma-error object constructor.
*
* Note:
* message_string_p can be NULL.
*
* Note:
* calling with ECMA_ERROR_NONE does not make sense thus it will
* cause a fault in the system.
*
@ -76,7 +79,8 @@ const ecma_error_mapping_t ecma_error_mappings[] =
* with reference counter set to one.
*/
ecma_object_t *
ecma_new_standard_error (ecma_standard_error_t error_type) /**< native error type */
ecma_new_standard_error (ecma_standard_error_t error_type, /**< native error type */
ecma_string_t *message_string_p) /**< message string */
{
#if ENABLED (JERRY_BUILTIN_ERRORS)
ecma_builtin_id_t prototype_id = ECMA_BUILTIN_ID__COUNT;
@ -140,23 +144,45 @@ ecma_new_standard_error (ecma_standard_error_t error_type) /**< native error typ
((ecma_extended_object_t *) new_error_obj_p)->u.class_prop.class_id = LIT_MAGIC_STRING_ERROR_UL;
if (message_string_p != NULL)
{
ecma_property_value_t *prop_value_p;
prop_value_p = ecma_create_named_data_property (new_error_obj_p,
ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE),
ECMA_PROPERTY_CONFIGURABLE_WRITABLE,
NULL);
ecma_ref_ecma_string (message_string_p);
prop_value_p->value = ecma_make_string_value (message_string_p);
}
/* Avoid calling the decorator function recursively. */
if (JERRY_CONTEXT (error_object_created_callback_p) != NULL
&& !(JERRY_CONTEXT (status_flags) & ECMA_STATUS_ERROR_UPDATE))
{
JERRY_CONTEXT (status_flags) |= ECMA_STATUS_ERROR_UPDATE;
JERRY_CONTEXT (error_object_created_callback_p) (ecma_make_object_value (new_error_obj_p),
JERRY_CONTEXT (error_object_created_callback_user_p));
JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_ERROR_UPDATE;
}
else
{
#if ENABLED (JERRY_LINE_INFO)
/* The "stack" identifier is not a magic string. */
const char * const stack_id_p = "stack";
/* Default decorator when line info is enabled. */
ecma_string_t *stack_str_p = ecma_get_magic_string (LIT_MAGIC_STRING_STACK);
ecma_string_t *stack_str_p = ecma_new_ecma_string_from_utf8 ((const lit_utf8_byte_t *) stack_id_p, 5);
ecma_property_value_t *prop_value_p = ecma_create_named_data_property (new_error_obj_p,
stack_str_p,
ECMA_PROPERTY_CONFIGURABLE_WRITABLE,
NULL);
ecma_deref_ecma_string (stack_str_p);
ecma_property_value_t *prop_value_p = ecma_create_named_data_property (new_error_obj_p,
stack_str_p,
ECMA_PROPERTY_CONFIGURABLE_WRITABLE,
NULL);
ecma_deref_ecma_string (stack_str_p);
ecma_value_t backtrace_value = vm_get_backtrace (0, NULL);
ecma_value_t backtrace_value = vm_get_backtrace (0, NULL);
prop_value_p->value = backtrace_value;
ecma_deref_object (ecma_get_object_from_value (backtrace_value));
prop_value_p->value = backtrace_value;
ecma_deref_object (ecma_get_object_from_value (backtrace_value));
#endif /* ENABLED (JERRY_LINE_INFO) */
}
return new_error_obj_p;
} /* ecma_new_standard_error */
@ -190,30 +216,6 @@ ecma_get_error_type (ecma_object_t *error_object) /**< possible error object */
return ECMA_ERROR_NONE;
} /* ecma_get_error_type */
/**
* Standard ecma-error object constructor.
*
* @return pointer to ecma-object representing specified error
* with reference counter set to one.
*/
ecma_object_t *
ecma_new_standard_error_with_message (ecma_standard_error_t error_type, /**< native error type */
ecma_string_t *message_string_p) /**< message string */
{
ecma_object_t *new_error_obj_p = ecma_new_standard_error (error_type);
ecma_property_value_t *prop_value_p;
prop_value_p = ecma_create_named_data_property (new_error_obj_p,
ecma_get_magic_string (LIT_MAGIC_STRING_MESSAGE),
ECMA_PROPERTY_CONFIGURABLE_WRITABLE,
NULL);
ecma_ref_ecma_string (message_string_p);
prop_value_p->value = ecma_make_string_value (message_string_p);
return new_error_obj_p;
} /* ecma_new_standard_error_with_message */
/**
* Raise a standard ecma-error with the given type and message.
*
@ -230,12 +232,12 @@ ecma_raise_standard_error (ecma_standard_error_t error_type, /**< error type */
{
ecma_string_t *error_msg_p = ecma_new_ecma_string_from_utf8 (msg_p,
lit_zt_utf8_string_size (msg_p));
error_obj_p = ecma_new_standard_error_with_message (error_type, error_msg_p);
error_obj_p = ecma_new_standard_error (error_type, error_msg_p);
ecma_deref_ecma_string (error_msg_p);
}
else
{
error_obj_p = ecma_new_standard_error (error_type);
error_obj_p = ecma_new_standard_error (error_type, NULL);
}
jcontext_raise_exception (ecma_make_object_value (error_obj_p));
@ -320,7 +322,7 @@ ecma_raise_standard_error_with_format (ecma_standard_error_t error_type, /**< er
ecma_string_t *builder_str_p = ecma_stringbuilder_finalize (&builder);
ecma_object_t *error_obj_p = ecma_new_standard_error_with_message (error_type, builder_str_p);
ecma_object_t *error_obj_p = ecma_new_standard_error (error_type, builder_str_p);
ecma_deref_ecma_string (builder_str_p);

View File

@ -51,8 +51,7 @@ typedef enum
} ecma_standard_error_t;
ecma_standard_error_t ecma_get_error_type (ecma_object_t *error_object);
ecma_object_t *ecma_new_standard_error (ecma_standard_error_t error_type);
ecma_object_t *ecma_new_standard_error_with_message (ecma_standard_error_t error_type, ecma_string_t *message_string_p);
ecma_object_t *ecma_new_standard_error (ecma_standard_error_t error_type, ecma_string_t *message_string_p);
#if ENABLED (JERRY_ERROR_MESSAGES)
ecma_value_t ecma_raise_standard_error_with_format (ecma_standard_error_t error_type, const char *msg_p, ...);
#endif /* ENABLED (JERRY_ERROR_MESSAGES) */

View File

@ -231,6 +231,12 @@ typedef jerry_value_t (*jerry_external_handler_t) (const jerry_value_t function_
*/
typedef void (*jerry_object_native_free_callback_t) (void *native_p);
/**
* Decorator callback for Error objects. The decorator can create
* or update any properties of the newly created Error object.
*/
typedef void (*jerry_error_object_created_callback_t) (const jerry_value_t error_object, void *user_p);
/**
* Callback which tells whether the ECMAScript execution should be stopped.
*
@ -512,6 +518,7 @@ jerry_value_t jerry_binary_operation (jerry_binary_operation_t op,
jerry_value_t jerry_create_abort_from_value (jerry_value_t value, bool release);
jerry_value_t jerry_create_error_from_value (jerry_value_t value, bool release);
jerry_value_t jerry_get_value_from_error (jerry_value_t value, bool release);
void jerry_set_error_object_created_callback (jerry_error_object_created_callback_t callback, void *user_p);
/**
* Error object function(s).

View File

@ -156,6 +156,8 @@ struct jerry_context_t
vm_frame_ctx_t *vm_top_context_p; /**< top (current) interpreter context */
jerry_context_data_header_t *context_data_p; /**< linked list of user-provided context-specific pointers */
void *error_object_created_callback_user_p; /**< user pointer for error_object_update_callback_p */
jerry_error_object_created_callback_t error_object_created_callback_p; /**< decorator callback for Error objects */
size_t ecma_gc_objects_number; /**< number of currently allocated objects */
size_t ecma_gc_new_objects; /**< number of newly allocated objects since last GC session */
size_t jmem_heap_allocated_size; /**< size of allocated regions */

View File

@ -300,6 +300,9 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SLICE, "slice")
|| ENABLED (JERRY_ESNEXT)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_SPLIT, "split")
#endif
#if ENABLED (JERRY_LINE_INFO)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_STACK, "stack")
#endif
#if ENABLED (JERRY_ESNEXT)
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_THROW, "throw")
#endif

View File

@ -129,6 +129,7 @@ LIT_MAGIC_STRING_ROUND = "round"
LIT_MAGIC_STRING_SHIFT = "shift"
LIT_MAGIC_STRING_SLICE = "slice"
LIT_MAGIC_STRING_SPLIT = "split"
LIT_MAGIC_STRING_STACK = "stack"
LIT_MAGIC_STRING_THROW = "throw"
LIT_MAGIC_STRING_TRUNC = "trunc"
LIT_MAGIC_STRING_VALUE = "value"

View File

@ -1714,7 +1714,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
case VM_OC_THROW_SYNTAX_ERROR:
{
ecma_string_t *msg_p = ecma_get_string_from_value (left_value);
ecma_object_t *error_obj_p = ecma_new_standard_error_with_message (ECMA_ERROR_SYNTAX, msg_p);
ecma_object_t *error_obj_p = ecma_new_standard_error (ECMA_ERROR_SYNTAX, msg_p);
jcontext_raise_exception (ecma_make_object_value (error_obj_p));
result = ECMA_VALUE_ERROR;
goto error;

View File

@ -69,6 +69,7 @@ set(SOURCE_UNIT_TEST_MAIN_MODULES
test-proxy.c
test-realm.c
test-regexp-dotall-unicode.c
test-error-callback.c
test-regexp.c
test-regression-3588.c
test-resource-name.c

View File

@ -0,0 +1,94 @@
/* 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.h"
#include "test-common.h"
static bool error_object_created_callback_is_running = false;
static int error_object_created_callback_count = 0;
static void
error_object_created_callback (const jerry_value_t error_object_t, /**< new error object */
void *user_p) /**< user pointer */
{
TEST_ASSERT (!error_object_created_callback_is_running);
TEST_ASSERT (user_p == (void *) &error_object_created_callback_count);
error_object_created_callback_is_running = true;
error_object_created_callback_count++;
jerry_value_t name = jerry_create_string ((const jerry_char_t *) "message");
jerry_value_t message = jerry_create_string ((const jerry_char_t *) "Replaced message!");
jerry_value_t result = jerry_set_property (error_object_t, name, message);
TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result) == true);
jerry_release_value (result);
/* This SyntaxError must not trigger a recusrsive call of the this callback. */
const char *source_p = "Syntax Error in JS!";
result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), 0);
TEST_ASSERT (jerry_value_is_error (result));
jerry_release_value (result);
jerry_release_value (message);
jerry_release_value (name);
error_object_created_callback_is_running = false;
} /* error_object_created_callback */
static void
run_test (const char *source_p)
{
/* Run the code 5 times. */
for (int i = 0; i < 5; i++)
{
jerry_value_t result = jerry_eval ((const jerry_char_t *) source_p, strlen (source_p), 0);
TEST_ASSERT (jerry_value_is_boolean (result) && jerry_get_boolean_value (result) == true);
jerry_release_value (result);
}
} /* run_test */
/**
* Unit test's main function.
*/
int
main (void)
{
TEST_INIT ();
jerry_init (JERRY_INIT_EMPTY);
jerry_set_error_object_created_callback (error_object_created_callback,
(void *) &error_object_created_callback_count);
run_test ("var result = false\n"
"try {\n"
" ref_error;\n"
"} catch(e) {\n"
" result = (e.message === 'Replaced message!')\n"
"}\n"
"result\n");
run_test ("var error = new Error()\n"
"error.message === 'Replaced message!'\n");
jerry_release_value (jerry_create_error (JERRY_ERROR_COMMON, (const jerry_char_t *) "Message"));
TEST_ASSERT (error_object_created_callback_count == 11);
jerry_cleanup ();
return 0;
} /* main */