From e7ffb70ae0b90d2a538d0396ca4ad4c82e9576a4 Mon Sep 17 00:00:00 2001 From: Zoltan Herczeg Date: Tue, 17 Aug 2021 08:31:30 +0200 Subject: [PATCH] Rework external string free operation (#4690) JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com --- docs/02.API-REFERENCE.md | 119 +++++++++++++++++++-- jerry-core/api/jerry.c | 18 +++- jerry-core/ecma/base/ecma-globals.h | 4 +- jerry-core/ecma/base/ecma-helpers-string.c | 21 ++-- jerry-core/ecma/base/ecma-helpers.h | 2 +- jerry-core/include/jerryscript-core.h | 7 +- jerry-core/include/jerryscript-types.h | 6 ++ jerry-core/jcontext/jcontext.h | 1 + tests/unit-core/test-external-string.c | 53 ++++++--- 9 files changed, 187 insertions(+), 44 deletions(-) diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index d01fad135..e8ceb71f3 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -769,6 +769,34 @@ typedef void (*jerry_object_native_free_callback_t) (void *native_p, struct jerr - [jerry_object_native_info_t](#jerry_object_native_info_t) +## jerry_external_string_free_callback_t + +**Summary** + +Free callback for external strings. See +[jerry_string_set_external_string_free_callback](#jerry_string_set_external_string_free_callback) +for more information. + +**Prototype** + +```c +typedef void (*jerry_external_string_free_callback_t) (jerry_char_t *string_p, + jerry_size_t string_size, + void *user_p); +``` + +- `string_p` - extrenal string pointer +- `string_size` - size of external string +- `user_p` - pointer passed when the external string is created + +*New in version [[NEXT_RELEASE]]*. + +**See also** + +- [jerry_string_set_external_string_free_callback](#jerry_string_set_external_string_free_callback) +- [jerry_create_external_string](#jerry_create_external_string) +- [jerry_create_external_string_sz](#jerry_create_external_string_sz) + ## jerry_error_object_created_callback_t **Summary** @@ -4195,6 +4223,70 @@ jerry_substring_to_utf8_char_buffer (const jerry_value_t value, - [jerry_is_valid_utf8_string](#jerry_is_valid_utf8_string) +# jerry_string_set_external_string_free_callback + +**Summary** + +Sets the global callback which is called when the string data of an external +string is no longer used. It is recommended to set this function before the +first external string is created. + +*Note*: + - When the callback is NULL, no function is called when an external string is freed. + - In some cases (e.g. when the string is also a magic string registered by + [jerry_register_magic_strings](#jerry_register_magic_strings)), the callback + is called when the string is created, not when it is released. + +**Prototype** + +```c +void jerry_string_set_external_string_free_callback (jerry_external_string_free_callback_t callback_p); +``` + +- `callback_p` - callback which is called when an external string is freed. + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # () + +```c +#include "jerryscript.h" +#include + +static void +external_string_free_callback (jerry_char_t *string_p, /**< string pointer */ + jerry_size_t string_size, /**< size of the string */ + void *user_p) /**< user pointer */ +{ + printf ("External string is freed!\n"); +} + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_string_set_external_string_free_callback (external_string_free_callback); + + const char *string_p = "This is a long external string, should not be duplicated!"; + jerry_value_t external_string = jerry_create_external_string ((jerry_char_t *) string_p, NULL); + /* The external_string_free_callback is called. */ + jerry_release_value (external_string); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_external_string_free_callback_t](#jerry_external_string_free_callback_t) +- [jerry_create_external_string](#jerry_create_external_string) +- [jerry_create_external_string_sz](#jerry_create_external_string_sz) + + # Functions for array object values ## jerry_get_array_length @@ -7293,24 +7385,25 @@ Create an external string from a valid CESU8 string. The string buffer passed to should not be modified until the free callback is called. This function can be used to avoid the duplication of large strings. -*Note*: Returned value must be freed with [jerry_release_value](#jerry_release_value) when it -is no longer needed. +*Note*: + - The free callback can be set by [jerry_string_set_external_string_free_callback](#jerry_string_set_external_string_free_callback) + - Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. **Prototype** ```c jerry_value_t -jerry_create_external_string (const jerry_char_t *str_p, - jerry_value_free_callback_t free_cb) +jerry_create_external_string (const jerry_char_t *str_p, void *user_p); ``` - `str_p` - non-null pointer to string -- `free_cb` - optional callback which is called right before the string is freed +- `user_p` - user pointer passed to the callback when the string is freed - return value - value of the created string *New in version 2.4*. -*Changed in version [[NEXT_RELEASE]]*: type of `free_cb` has been changed. +*Changed in version [[NEXT_RELEASE]]*: `free_cb` is replaced by `user_p`. **Example** @@ -7330,6 +7423,7 @@ jerry_create_external_string (const jerry_char_t *str_p, - [jerry_is_valid_cesu8_string](#jerry_is_valid_cesu8_string) - [jerry_create_external_string_sz](#jerry_create_external_string_sz) +- [jerry_string_set_external_string_free_callback](#jerry_string_set_external_string_free_callback) ## jerry_create_external_string_sz @@ -7340,8 +7434,10 @@ Create an external string from a valid CESU8 string. The string buffer passed to should not be modified until the free callback is called. This function can be used to avoid the duplication of large strings. -*Note*: Returned value must be freed with [jerry_release_value](#jerry_release_value) when it -is no longer needed. +*Note*: + - The free callback can be set by [jerry_string_set_external_string_free_callback](#jerry_string_set_external_string_free_callback) + - Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. **Prototype** @@ -7349,17 +7445,17 @@ is no longer needed. jerry_value_t jerry_create_external_string_sz (const jerry_char_t *str_p, jerry_size_t str_size, - jerry_value_free_callback_t free_cb) + void *user_p); ``` - `str_p` - non-null pointer to string - `str_size` - size of the string -- `free_cb` - optional callback which is called right before the string is freed +- `user_p` - user pointer passed to the callback when the string is freed - return value - value of the created string *New in version 2.4*. -*Changed in version [[NEXT_RELEASE]]*: type of `free_cb` has been changed. +*Changed in version [[NEXT_RELEASE]]*: `free_cb` is replaced by `user_p`. **Example** @@ -7381,6 +7477,7 @@ jerry_create_external_string_sz (const jerry_char_t *str_p, - [jerry_is_valid_cesu8_string](#jerry_is_valid_cesu8_string) - [jerry_create_external_string](#jerry_create_external_string) +- [jerry_string_set_external_string_free_callback](#jerry_string_set_external_string_free_callback) ## jerry_create_symbol diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index de0f3ec20..38ed97caa 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -2700,9 +2700,9 @@ jerry_create_string_sz (const jerry_char_t *str_p, /**< pointer to string */ */ jerry_value_t jerry_create_external_string (const jerry_char_t *str_p, /**< pointer to string */ - jerry_value_free_callback_t free_cb) /**< free callback */ + void *user_p) /**< user pointer passed to the callback when the string is freed */ { - return jerry_create_external_string_sz (str_p, lit_zt_utf8_string_size ((lit_utf8_byte_t *) str_p), free_cb); + return jerry_create_external_string_sz (str_p, lit_zt_utf8_string_size ((lit_utf8_byte_t *) str_p), user_p); } /* jerry_create_external_string */ /** @@ -2716,13 +2716,13 @@ jerry_create_external_string (const jerry_char_t *str_p, /**< pointer to string jerry_value_t jerry_create_external_string_sz (const jerry_char_t *str_p, /**< pointer to string */ jerry_size_t str_size, /**< string size */ - jerry_value_free_callback_t free_cb) /**< free callback */ + void *user_p) /**< user pointer passed to the callback when the string is freed */ { jerry_assert_api_available (); ecma_string_t *ecma_str_p = ecma_new_ecma_external_string_from_cesu8 ((lit_utf8_byte_t *) str_p, (lit_utf8_size_t) str_size, - free_cb); + user_p); return ecma_make_string_value (ecma_str_p); } /* jerry_create_external_string_sz */ @@ -3107,6 +3107,16 @@ jerry_substring_to_utf8_char_buffer (const jerry_value_t value, /**< input strin buffer_size); } /* jerry_substring_to_utf8_char_buffer */ +/** + * Sets the global callback which is called when an external string is freed. + */ +void +jerry_string_set_external_string_free_callback (jerry_external_string_free_callback_t callback_p) /**< free + * callback */ +{ + JERRY_CONTEXT (external_string_free_callback_p) = callback_p; +} /* jerry_string_set_external_string_free_callback */ + /** * Checks whether the object or it's prototype objects have the given property. * diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index aa142ed4e..1ecfba9f6 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -1804,8 +1804,8 @@ typedef struct */ typedef struct { - ecma_long_string_t header; - jerry_value_free_callback_t free_cb; /**< free callback */ + ecma_long_string_t header; /**< long string header */ + void *user_p; /**< user pointer passed to the callback when the string is freed */ } ecma_external_string_t; /** diff --git a/jerry-core/ecma/base/ecma-helpers-string.c b/jerry-core/ecma/base/ecma-helpers-string.c index 1d123a246..d034c105a 100644 --- a/jerry-core/ecma/base/ecma-helpers-string.c +++ b/jerry-core/ecma/base/ecma-helpers-string.c @@ -19,6 +19,7 @@ #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" +#include "jcontext.h" #include "jrt.h" #include "jrt-libc-includes.h" #include "lit-char-helpers.h" @@ -455,7 +456,8 @@ ecma_new_ecma_string_from_utf8_converted_to_cesu8 (const lit_utf8_byte_t *string ecma_string_t * ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, /**< cesu-8 string */ lit_utf8_size_t string_size, /**< string size */ - jerry_value_free_callback_t free_cb) /**< free callback */ + void *user_p) /**< user pointer passed to the callback + * when the string is freed */ { JERRY_ASSERT (string_p != NULL || string_size == 0); JERRY_ASSERT (lit_is_valid_cesu8_string (string_p, string_size)); @@ -465,9 +467,11 @@ ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, /**< /* Normal strings are created for short strings. */ ecma_string_t *string_desc_p = ecma_new_ecma_string_from_utf8 (string_p, string_size); + jerry_external_string_free_callback_t free_cb = JERRY_CONTEXT (external_string_free_callback_p); + if (free_cb != NULL) { - free_cb ((void *) string_p); + free_cb ((lit_utf8_byte_t *) string_p, string_size, user_p); } return string_desc_p; } @@ -476,9 +480,11 @@ ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, /**< if (string_desc_p != NULL) { + jerry_external_string_free_callback_t free_cb = JERRY_CONTEXT (external_string_free_callback_p); + if (free_cb != NULL) { - free_cb ((void *) string_p); + free_cb ((lit_utf8_byte_t *) string_p, string_size, user_p); } return string_desc_p; } @@ -491,7 +497,7 @@ ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, /**< long_string_p->string_p = string_p; long_string_p->size = string_size; long_string_p->length = lit_utf8_string_length (string_p, string_size); - external_string_p->free_cb = free_cb; + external_string_p->user_p = user_p; return (ecma_string_t *) external_string_p; } /* ecma_new_ecma_external_string_from_cesu8 */ @@ -961,10 +967,13 @@ ecma_destroy_ecma_string (ecma_string_t *string_p) /**< ecma-string */ } ecma_external_string_t *external_string_p = (ecma_external_string_t *) string_p; + jerry_external_string_free_callback_t free_cb = JERRY_CONTEXT (external_string_free_callback_p); - if (external_string_p->free_cb != NULL) + if (free_cb != NULL) { - external_string_p->free_cb ((void *) external_string_p->header.string_p); + free_cb ((lit_utf8_byte_t *) external_string_p->header.string_p, + external_string_p->header.size, + external_string_p->user_p); } ecma_dealloc_external_string (external_string_p); diff --git a/jerry-core/ecma/base/ecma-helpers.h b/jerry-core/ecma/base/ecma-helpers.h index f7ac1e62b..ec189759e 100644 --- a/jerry-core/ecma/base/ecma-helpers.h +++ b/jerry-core/ecma/base/ecma-helpers.h @@ -303,7 +303,7 @@ ecma_string_t *ecma_new_ecma_string_from_utf8 (const lit_utf8_byte_t *string_p, ecma_string_t *ecma_new_ecma_string_from_utf8_converted_to_cesu8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size); ecma_string_t *ecma_new_ecma_external_string_from_cesu8 (const lit_utf8_byte_t *string_p, lit_utf8_size_t string_size, - jerry_value_free_callback_t free_cb); + void *user_p); ecma_string_t *ecma_new_ecma_string_from_code_unit (ecma_char_t code_unit); #if JERRY_ESNEXT ecma_string_t *ecma_new_ecma_string_from_code_units (ecma_char_t first_code_unit, ecma_char_t second_code_unit); diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 96161a79b..e5376d75c 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -135,6 +135,7 @@ jerry_size_t jerry_substring_to_utf8_char_buffer (const jerry_value_t value, jerry_length_t end_pos, jerry_char_t *buffer_p, jerry_size_t buffer_size); +void jerry_string_set_external_string_free_callback (jerry_external_string_free_callback_t callback_p); /** * Functions for array object values. @@ -188,10 +189,8 @@ jerry_value_t jerry_create_string_from_utf8 (const jerry_char_t *str_p); jerry_value_t jerry_create_string_sz_from_utf8 (const jerry_char_t *str_p, jerry_size_t str_size); jerry_value_t jerry_create_string (const jerry_char_t *str_p); jerry_value_t jerry_create_string_sz (const jerry_char_t *str_p, jerry_size_t str_size); -jerry_value_t jerry_create_external_string (const jerry_char_t *str_p, - jerry_value_free_callback_t free_cb); -jerry_value_t jerry_create_external_string_sz (const jerry_char_t *str_p, jerry_size_t str_size, - jerry_value_free_callback_t free_cb); +jerry_value_t jerry_create_external_string (const jerry_char_t *str_p, void *user_p); +jerry_value_t jerry_create_external_string_sz (const jerry_char_t *str_p, jerry_size_t str_size, void *user_p); jerry_value_t jerry_create_symbol (const jerry_value_t value); jerry_value_t jerry_create_bigint (const uint64_t *digits_p, uint32_t size, bool sign); jerry_value_t jerry_create_undefined (void); diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index 707ccdc09..0628573b1 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -291,6 +291,12 @@ struct jerry_object_native_info_t; */ typedef void (*jerry_object_native_free_callback_t) (void *native_p, struct jerry_object_native_info_t *info_p); +/** + * Free callback for external strings. + */ +typedef void (*jerry_external_string_free_callback_t) (jerry_char_t *string_p, jerry_size_t string_size, + void *user_p); + /** * Decorator callback for Error objects. The decorator can create * or update any properties of the newly created Error object. diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 5ecb17cfc..e2a45ae39 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -158,6 +158,7 @@ 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 */ + jerry_external_string_free_callback_t external_string_free_callback_p; /**< free callback for external strings */ 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 */ diff --git a/tests/unit-core/test-external-string.c b/tests/unit-core/test-external-string.c index ac8edf7c2..7fa37171c 100644 --- a/tests/unit-core/test-external-string.c +++ b/tests/unit-core/test-external-string.c @@ -25,25 +25,37 @@ static const char *external_3 = "x!?:s"; static const char *external_4 = "Object property external string! Object property external string!"; static void -free_external1 (void *ptr) +external_string_free_callback_1 (jerry_char_t *string_p, /**< string pointer */ + jerry_size_t string_size, /**< size of the string */ + void *user_p) /**< user pointer */ { - TEST_ASSERT (ptr == external_1); + TEST_ASSERT ((const char *) string_p == external_1); + TEST_ASSERT (string_size == strlen (external_1)); + TEST_ASSERT (user_p == NULL); free_count++; -} /* free_external1 */ +} /* external_string_free_callback_1 */ static void -free_external2 (void *ptr) +external_string_free_callback_2 (jerry_char_t *string_p, /**< string pointer */ + jerry_size_t string_size, /**< size of the string */ + void *user_p) /**< user pointer */ { - TEST_ASSERT (ptr == external_2); + TEST_ASSERT ((const char *) string_p == external_2); + TEST_ASSERT (string_size == strlen (external_2)); + TEST_ASSERT (user_p == (void *) &free_count); free_count++; -} /* free_external2 */ +} /* external_string_free_callback_2 */ static void -free_external3 (void *ptr) +external_string_free_callback_3 (jerry_char_t *string_p, /**< string pointer */ + jerry_size_t string_size, /**< size of the string */ + void *user_p) /**< user pointer */ { - TEST_ASSERT (ptr == external_3); + TEST_ASSERT ((const char *) string_p == external_3); + TEST_ASSERT (string_size == strlen (external_3)); + TEST_ASSERT (user_p == (void *) string_p); free_count++; -} /* free_external3 */ +} /* external_string_free_callback_3 */ int main (void) @@ -53,38 +65,45 @@ main (void) jerry_init (JERRY_INIT_EMPTY); /* Test external callback calls. */ - jerry_value_t external_string = jerry_create_external_string ((jerry_char_t *) external_1, free_external1); + jerry_string_set_external_string_free_callback (external_string_free_callback_1); + jerry_value_t external_string = jerry_create_external_string ((jerry_char_t *) external_1, NULL); TEST_ASSERT (free_count == 0); jerry_release_value (external_string); TEST_ASSERT (free_count == 1); + jerry_string_set_external_string_free_callback (NULL); external_string = jerry_create_external_string ((jerry_char_t *) external_1, NULL); TEST_ASSERT (free_count == 1); jerry_release_value (external_string); TEST_ASSERT (free_count == 1); - external_string = jerry_create_external_string ((jerry_char_t *) external_2, free_external2); + jerry_string_set_external_string_free_callback (external_string_free_callback_2); + external_string = jerry_create_external_string ((jerry_char_t *) external_2, (void *) &free_count); TEST_ASSERT (free_count == 2); jerry_release_value (external_string); TEST_ASSERT (free_count == 2); - external_string = jerry_create_external_string ((jerry_char_t *) external_2, NULL); + jerry_string_set_external_string_free_callback (NULL); + external_string = jerry_create_external_string ((jerry_char_t *) external_2, (void *) &free_count); TEST_ASSERT (free_count == 2); jerry_release_value (external_string); TEST_ASSERT (free_count == 2); - external_string = jerry_create_external_string ((jerry_char_t *) external_3, free_external3); + jerry_string_set_external_string_free_callback (external_string_free_callback_3); + external_string = jerry_create_external_string ((jerry_char_t *) external_3, (void *) external_3); TEST_ASSERT (free_count == 3); jerry_release_value (external_string); TEST_ASSERT (free_count == 3); - external_string = jerry_create_external_string ((jerry_char_t *) external_3, NULL); + jerry_string_set_external_string_free_callback (NULL); + external_string = jerry_create_external_string ((jerry_char_t *) external_3, (void *) external_3); TEST_ASSERT (free_count == 3); jerry_release_value (external_string); TEST_ASSERT (free_count == 3); /* Test string comparison. */ - external_string = jerry_create_external_string ((jerry_char_t *) external_1, free_external1); + jerry_string_set_external_string_free_callback (external_string_free_callback_1); + external_string = jerry_create_external_string ((jerry_char_t *) external_1, NULL); jerry_value_t other_string = jerry_create_string ((jerry_char_t *) external_1); jerry_value_t result = jerry_binary_operation (JERRY_BIN_OP_STRICT_EQUAL, external_string, other_string); @@ -103,7 +122,8 @@ main (void) jerry_release_value (other_string); /* Test getting string. */ - external_string = jerry_create_external_string ((jerry_char_t *) external_1, free_external1); + jerry_string_set_external_string_free_callback (external_string_free_callback_1); + external_string = jerry_create_external_string ((jerry_char_t *) external_1, NULL); size_t length = strlen (external_1); TEST_ASSERT (jerry_value_is_string (external_string)); @@ -119,6 +139,7 @@ main (void) TEST_ASSERT (free_count == 5); /* Test property access. */ + jerry_string_set_external_string_free_callback (NULL); external_string = jerry_create_external_string ((jerry_char_t *) external_4, NULL); other_string = jerry_create_string ((jerry_char_t *) external_4);