diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index a1aee15ec..2a88488a2 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -101,6 +101,25 @@ created by API functions has the error flag set. typedef uint32_t jerry_value_t; ``` +## jerry_context_data_manager_t + +**Summary** + +Structure that defines how a context data item will be initialized and deinitialized. JerryScript zeroes out the memory +for the item by default, and if the `init_cb` field is not NULL, it will be called with the pointer to the memory as +an additional custom initializer. + +**Prototype** + +```c +typedef struct +{ + void (*init_cb) (void *); /**< callback responsible for initializing a context item, or NULL */ + void (*deinit_cb) (void *); /**< callback responsible for deinitializing a context item */ + size_t bytes_needed; /**< number of bytes to allocate for this manager */ +} jerry_context_data_manager_t; +``` + ## jerry_property_descriptor_t **Summary** @@ -264,9 +283,7 @@ typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p); **Summary** Initializes the JerryScript engine, making it possible to run JavaScript code and perform operations -on JavaScript values. See also [jerry_init_with_user_context](#jerry_init_with_user_context) if you -wish to initialize the JerryScript engine in such a way that its context contains a custom pointer -which you can later retrieve using [jerry_get_user_context](#jerry_get_user_context). +on JavaScript values. **Prototype** @@ -299,91 +316,13 @@ jerry_init (jerry_init_flag_t flags) **See also** - [jerry_cleanup](#jerry_cleanup) -- [jerry_init_with_user_context](#jerry_init_with_user_context) - - -## jerry_init_with_user_context - -**Summary** - -Calls [jerry_init](#jerry_init) to initialize the JerryScript engine, thereby making it possible -to run JavaScript code and perform operations on JavaScript values. In addition to the first -parameter this function accepts two more parameters with which it allows the caller to store a -`void *` pointer inside the context being initialized with `jerry_init ()`. The function calls the -callback given in its `init_cb` parameter to allocate the memory for the pointer and it stores the -function pointer given in the `deinit_cb` parameter along with the pointer so that it may be called -to free the stored pointer when `jerry_cleanup ()` is later called to dispose of the context. - -**Prototype** - -```c -void -jerry_init_with_user_context (jerry_init_flag_t flags, - jerry_user_context_init_cb init_cb, - jerry_user_context_deinit_cb deinit_cb); -``` - -`flags` - combination of various engine configuration flags: - -- `JERRY_INIT_EMPTY` - no flags, just initialize in default configuration. -- `JERRY_INIT_SHOW_OPCODES` - print compiled byte-code. -- `JERRY_INIT_SHOW_REGEXP_OPCODES` - print compiled regexp byte-code. -- `JERRY_INIT_MEM_STATS` - dump memory statistics. -- `JERRY_INIT_MEM_STATS_SEPARATE` - dump memory statistics and reset peak values after parse. -- `JERRY_INIT_DEBUGGER` - enable all features required by debugging. - -`init_cb` - a function pointer that will be called to allocate the custom pointer. - -`deinit_cb` - a function pointer that will be called when the custom pointer must be freed. - -**Example** - -```c -void * -init_user_context (void) -{ - void *return_value; - - /* allocate and initialize return_value */ - - return return_value; -} /* init_user_context */ - -void -free_user_context (void *context) -{ - - /* free the value allocated above */ - -} /* free_user_context */ - -{ - /* init_user_context () will be called before the call below returns */ - jerry_init_with_user_context (JERRY_INIT_SHOW_OPCODES | JERRY_INIT_SHOW_REGEXP_OPCODES, - init_user_context, - free_user_context); - - /* ... */ - - /* free_user_context () will be called before the call below returns */ - jerry_cleanup (); -} -``` - -**See also** - -- [jerry_cleanup](#jerry_cleanup) -- [jerry_get_user_context](#jerry_get_user_context) ## jerry_cleanup **Summary** -Finish JavaScript engine execution, freeing memory and JavaScript values. If the context was -initialized with `jerry_init_with_user_context ()` and a `deinit_cb` was provided, then it will -be called to free the memory at the custom pointer which was associated with the context being -cleaned up. +Finish JavaScript engine execution, freeing memory and JavaScript values. *Note*: JavaScript values, received from engine, will be inaccessible after the cleanup. @@ -397,38 +336,81 @@ jerry_cleanup (void); **See also** - [jerry_init](#jerry_init) -- [jerry_init_with_user_context](#jerry_init_with_user_context) -## jerry_get_user_context +## jerry_get_context_data **Summary** -Retrieve the pointer stored within the current context. +Retrieve a pointer to the item stored within the current context by the given manager. + +*Note*: Since internally the pointer to a manager's context data item is linked to the next such pointer in a linked + list, it is inadvisable to invoke too many different managers, because doing so will increase the time it takes + to retrieve a manager's context data item, degrading performance. For example, try to keep the number of + managers below five. **Prototype** ```c void * -jerry_get_user_context (void); +jerry_get_context_data (const jerry_context_data_manager *manager_p); ``` -- return value: the pointer that was assigned during `jerry_init_with_user_context ()` +- `manager_p`: the manager of this context data item. +- return value: the item created by `manager_p` when `jerry_get_context_data ()` was first called, or a new item created + by `manager_p`, which will be stored for future identical calls to `jerry_get_context_data ()`, and which will be + deinitialized using the `deinit_cb` callback provided by `manager_p` when the context will be destroyed. **Example** ```c +typedef struct { - /* ... */ - my_context *custom_data = (my_context *) jerry_get_user_context (); - /* ... */ + int my_data1; + double my_data2; + char *my_data3; +} my_context_data_t; + +/* Define how context items will be initialized. */ +static void +my_context_data_new (void *user_data_p) +{ + my_context_data_t *my_data_p = (my_context_data_t *) user_data_p; + + /* + * Initialize my_data_p. JerryScript will store it on the current context and return it whenever + * jerry_get_context_data () is called with a pointer to my_manager as defined below. + */ +} + +/* Define how context items will be deinitialized */ +static void +my_context_data_free (void *user_data_p) +{ + my_context_data_t *my_data_p = ((my_context_data_t *) user_data_p); + + /* Perform any necessary cleanup on my_data. JerryScript will free the pointer after this function completes. */ +} + +/* Wrap the creation and destruction functions into a manager */ +static const jerry_context_data_manager_t my_manager = +{ + .init_cb = my_context_data_new, + .deinit_cb = my_context_data_free, + .bytes_needed = sizeof (my_context_data_t) +}; + +/* + * Then, in some function in your code, you can retrieve an item of type my_context_data_t from the currently active + * context such that JerryScript will create and store such an item if one was not previously created + */ +void someplace_in_the_code (void) +{ + my_context_data_t *my_data = (my_context_data_t *) jerry_get_context_data (&my_manager); + /* Perform useful things using the data found in my_data */ } ``` -**See also** -- [jerry_init_with_user_context](#jerry_init_with_user_context) -- [jerry_cleanup](#jerry_cleanup) - ## jerry_register_magic_strings diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index cf9ec3bfd..345fa45bb 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -166,22 +166,6 @@ jerry_init (jerry_init_flag_t flags) /**< combination of Jerry flags */ #endif /* JERRY_DEBUGGER */ } /* jerry_init */ -/** - * Initialize Jerry engine with custom user context. - */ -void -jerry_init_with_user_context (jerry_init_flag_t flags, /**< combination of Jerry flags */ - jerry_user_context_init_t init_cb, /**< callback to call to create the user context or - * NULL, in which case no user context will be - * created */ - jerry_user_context_deinit_t deinit_cb) /**< callback to call to free the user context or - * NULL if it does not need to be freed */ -{ - jerry_init (flags); - JERRY_CONTEXT (user_context_p) = (init_cb ? init_cb () : NULL); - JERRY_CONTEXT (user_context_deinit_cb) = deinit_cb; -} /* jerry_init_with_user_context */ - /** * Terminate Jerry engine */ @@ -190,6 +174,15 @@ jerry_cleanup (void) { jerry_assert_api_available (); + for (jerry_context_data_header_t *this_p = JERRY_CONTEXT (context_data_p), *next_p = NULL; + this_p != NULL; + this_p = next_p) + { + next_p = this_p->next_p; + this_p->manager_p->deinit_cb (JERRY_CONTEXT_DATA_HEADER_USER_DATA (this_p)); + jmem_heap_free_block (this_p, sizeof (jerry_context_data_header_t) + this_p->manager_p->bytes_needed); + } + ecma_finalize (); #ifdef JERRY_DEBUGGER @@ -201,23 +194,44 @@ jerry_cleanup (void) jmem_finalize (); jerry_make_api_unavailable (); - - if (JERRY_CONTEXT (user_context_deinit_cb)) - { - JERRY_CONTEXT (user_context_deinit_cb) (JERRY_CONTEXT (user_context_p)); - } } /* jerry_cleanup */ /** - * Retrieve user context. + * Retrieve a context data item, or create a new one. * - * @return the user-provided context-specific pointer + * @param manager_p pointer to the manager whose context data item should be returned. + * + * @return a pointer to the user-provided context-specific data item for the given manager, creating such a pointer if + * none was found. */ void * -jerry_get_user_context (void) +jerry_get_context_data (const jerry_context_data_manager_t *manager_p) { - return JERRY_CONTEXT (user_context_p); -} /* jerry_get_user_context */ + void *ret = NULL; + jerry_context_data_header_t *item_p; + + for (item_p = JERRY_CONTEXT (context_data_p); item_p != NULL; item_p = item_p->next_p) + { + if (item_p->manager_p == manager_p) + { + return JERRY_CONTEXT_DATA_HEADER_USER_DATA (item_p); + } + } + + item_p = jmem_heap_alloc_block (sizeof (jerry_context_data_header_t) + manager_p->bytes_needed); + item_p->manager_p = manager_p; + item_p->next_p = JERRY_CONTEXT (context_data_p); + JERRY_CONTEXT (context_data_p) = item_p; + ret = JERRY_CONTEXT_DATA_HEADER_USER_DATA (item_p); + + memset (ret, 0, manager_p->bytes_needed); + if (manager_p->init_cb) + { + manager_p->init_cb (ret); + } + + return ret; +} /* jerry_get_context_data */ /** * Register external magic string array diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 738cf5970..f450aa2c7 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -195,15 +195,16 @@ typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p); typedef bool (*jerry_object_property_foreach_t) (const jerry_value_t property_name, const jerry_value_t property_value, void *user_data_p); -/** - * Function type for user context allocation - */ -typedef void *(*jerry_user_context_init_t) (void); /** - * Function type for user context deallocation + * User context item manager */ -typedef void (*jerry_user_context_deinit_t) (void *user_context_p); +typedef struct +{ + void (*init_cb) (void *); /**< callback responsible for initializing a context item, or NULL to zero out the memory */ + void (*deinit_cb) (void *); /**< callback responsible for deinitializing a context item */ + size_t bytes_needed; /**< number of bytes to allocate for this manager */ +} jerry_context_data_manager_t; /** * Function type for allocating buffer for JerryScript instance. @@ -227,15 +228,12 @@ typedef struct jerry_instance_t jerry_instance_t; * General engine functions. */ void jerry_init (jerry_init_flag_t flags); -void jerry_init_with_user_context (jerry_init_flag_t flags, - jerry_user_context_init_t init_cb, - jerry_user_context_deinit_t deinit_cb); void jerry_cleanup (void); void jerry_register_magic_strings (const jerry_char_ptr_t *ex_str_items_p, uint32_t count, const jerry_length_t *str_lengths_p); void jerry_get_memory_limits (size_t *out_data_bss_brk_limit_p, size_t *out_stack_limit_p); void jerry_gc (void); -void *jerry_get_user_context (void); +void *jerry_get_context_data (const jerry_context_data_manager_t *manager_p); /** * Parser and executor functions. diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 2f8bf6389..1a03b604f 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -26,6 +26,7 @@ #include "jmem.h" #include "re-bytecode.h" #include "vm-defines.h" +#include "jerryscript.h" /** \addtogroup context Context * @{ @@ -36,6 +37,18 @@ */ #define JERRY_CONTEXT_FIRST_MEMBER ecma_builtin_objects +/** + * User context item + */ +typedef struct jerry_context_data_header +{ + struct jerry_context_data_header *next_p; /**< pointer to next context item */ + const jerry_context_data_manager_t *manager_p; /**< manager responsible for deleting this item */ +} jerry_context_data_header_t; + +#define JERRY_CONTEXT_DATA_HEADER_USER_DATA(item_p) \ + ((uint8_t *) (item_p + 1)) + /** * JerryScript context * @@ -63,8 +76,7 @@ typedef struct ecma_lit_storage_item_t *number_list_first_p; /**< first item of the literal number list */ ecma_object_t *ecma_global_lex_env_p; /**< global lexical environment */ vm_frame_ctx_t *vm_top_context_p; /**< top (current) interpreter context */ - void *user_context_p; /**< user-provided context-specific pointer */ - ecma_user_context_deinit_t user_context_deinit_cb; /**< user-provided deleter for context-specific pointer */ + jerry_context_data_header_t *context_data_p; /**< linked list of user-provided context-specific pointers */ 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 */ diff --git a/tests/unit-core/test-context-data.c b/tests/unit-core/test-context-data.c new file mode 100644 index 000000000..5c0c604a8 --- /dev/null +++ b/tests/unit-core/test-context-data.c @@ -0,0 +1,95 @@ +/* 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 "config.h" +#include "jerryscript.h" +#include "test-common.h" + +static bool test_context_data1_new_called = false; +static bool test_context_data2_new_called = false; +static bool test_context_data1_free_called = false; +static bool test_context_data2_free_called = false; + +/* Context item 1 */ +const char *string1 = "item1"; + +static void +test_context_data1_new (void *user_data_p) +{ + test_context_data1_new_called = true; + *((const char **) user_data_p) = string1; +} /* test_context_data1_new */ + +static void +test_context_data1_free (void *user_data_p) +{ + test_context_data1_free_called = true; + TEST_ASSERT ((*(const char **) user_data_p) == string1); +} /* test_context_data1_free */ + +static const jerry_context_data_manager_t manager1 = +{ + .init_cb = test_context_data1_new, + .deinit_cb = test_context_data1_free, + .bytes_needed = sizeof (const char *) +}; + +/* Context item 2 */ +const char *string2 = "item2"; + +static void +test_context_data2_new (void *user_data_p) +{ + test_context_data2_new_called = true; + *((const char **) user_data_p) = string2; +} /* test_context_data2_new */ + +static void +test_context_data2_free (void *user_data_p) +{ + test_context_data2_free_called = true; + TEST_ASSERT ((*(const char **) user_data_p) == string2); +} /* test_context_data2_free */ + +static const jerry_context_data_manager_t manager2 = +{ + .init_cb = test_context_data2_new, + .deinit_cb = test_context_data2_free, + .bytes_needed = sizeof (const char *) +}; + +int +main (void) +{ + TEST_INIT (); + + jerry_init (JERRY_INIT_EMPTY); + + TEST_ASSERT (!strcmp (*((const char **) jerry_get_context_data (&manager1)), "item1")); + TEST_ASSERT (!strcmp (*((const char **) jerry_get_context_data (&manager2)), "item2")); + + TEST_ASSERT (test_context_data1_new_called); + TEST_ASSERT (test_context_data2_new_called); + + TEST_ASSERT (!test_context_data1_free_called); + TEST_ASSERT (!test_context_data2_free_called); + + jerry_cleanup (); + + TEST_ASSERT (test_context_data1_free_called); + TEST_ASSERT (test_context_data2_free_called); + + return 0; +} /* main */ diff --git a/tests/unit-core/test-user-context.c b/tests/unit-core/test-user-context.c deleted file mode 100644 index d2062fbc8..000000000 --- a/tests/unit-core/test-user-context.c +++ /dev/null @@ -1,53 +0,0 @@ -/* 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 "config.h" -#include "jerryscript.h" -#include "test-common.h" - -static const char *identifying_string = "identifying string"; -static bool user_context_new_was_called = false; -static bool user_context_free_was_called = false; - -static void * -user_context_new (void) -{ - user_context_new_was_called = true; - return (void *) identifying_string; -} /* user_context_new */ - -static void -user_context_free (void *user_context_p) -{ - user_context_free_was_called = true; - TEST_ASSERT (((const char *) user_context_p) == identifying_string); -} /* user_context_free */ - -int -main (void) -{ - TEST_INIT (); - - jerry_init_with_user_context (JERRY_INIT_EMPTY, user_context_new, user_context_free); - - TEST_ASSERT ((((const char *)(jerry_get_user_context ()))) == identifying_string); - - jerry_cleanup (); - - TEST_ASSERT (user_context_new_was_called); - TEST_ASSERT (user_context_free_was_called); - - return 0; -} /* main */