diff options
author | Gabriel "_|Nix|_" Schulhof <gabriel.schulhof@intel.com> | 2018-03-21 03:48:27 -0400 |
---|---|---|
committer | László Langó <llango.u-szeged@partner.samsung.com> | 2018-03-21 08:48:27 +0100 |
commit | 3664d9ddd137bbba3d6149c7352cb2d07aea221d (patch) | |
tree | 891c660247847c14948d4000a3ca39ae9acc4961 | |
parent | bb84466fcf7928abd8cefcc09b2ef95235df1f59 (diff) |
Add an API to traverse objects by their associated native data (#2236)
JerryScript-DCO-1.0-Signed-off-by: Gabriel Schulhof gabriel.schulhof@intel.com
-rw-r--r-- | docs/02.API-REFERENCE.md | 208 | ||||
-rw-r--r-- | jerry-core/api/jerry.c | 64 | ||||
-rw-r--r-- | jerry-core/include/jerryscript-core.h | 17 | ||||
-rw-r--r-- | tests/unit-core/test-objects-foreach.c | 133 |
4 files changed, 422 insertions, 0 deletions
diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 30840ae4..442bc6a9 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -291,6 +291,33 @@ typedef bool (*jerry_object_property_foreach_t) (const jerry_value_t property_na void *user_data_p); ``` +## jerry_objects_foreach_t + +**Summary** + +Function type applied for each object in the engine + +**Prototype** + +```c +typedef bool (*jerry_objects_foreach_t) (const jerry_value_t object, + void *user_data_p); +``` + +## jerry_objects_foreach_by_native_info_t + +**Summary** + +Function type applied for each matching object in the engine + +**Prototype** + +```c +typedef bool (*jerry_objects_foreach_by_native_info_t) (const jerry_value_t object, + void *object_data_p, + void *user_data_p); +``` + ## jerry_vm_exec_stop_callback_t **Summary** @@ -4564,6 +4591,187 @@ bool foreach_function (const jerry_value_t prop_name, - [jerry_object_property_foreach_t](#jerry_object_property_foreach_t) +## jerry_objects_foreach + +**Summary** + +Iterate over objects. + +*Note*: Values obtained in `foreach_p` must be retained using [jerry_acquire_value](#jerry_acquire_value). + +**Prototype** + +```c +bool jerry_objects_foreach (jerry_objects_foreach_t foreach_p, + void *user_data_p); +``` + +- `foreach_p` - function that will be invoked for each object. +- `user_data_p` - User data to pass to the function. +- return value + - `true`, if the search function terminated the traversal by returning `false` + - `false`, if the end of the list of objects was reached + +**Example** + +```c +typedef struct +{ + jerry_value_t property_name; + jerry_value_t result; +} find_my_object_info_t; + +/* + * Find the first object with the given property. + */ +static bool +find_my_object(const jerry_value_t candidate, + void *user_data_p) +{ + find_my_object_info_t *info_p = (find_my_object_info_t *) user_data_p; + jerry_value_t has_property = jerry_object_has_property (candidate, info_p->property_name); + bool keep_searching = (jerry_value_has_error_flag (has_property) || !jerry_get_boolean_value ()); + if (!keep_searching) + { + /* We found it, so we acquire the value and record it. */ + info_p->result = jerry_acquire_value (candidate); + } + jerry_release_value (has_property); + return keep_searching; +} /* find_my_object */ + +{ + find_my_object_info_t search_info = + { + .property_name = jerry_create_string ("desired_property") + }; + + if (jerry_object_foreach (find_my_object, &search_info)) + { + /* The search was successful. Do something useful with search_info.result. */ + ... + + /* Release the found object after we're done using it. */ + jerry_release_value (search_info.result); + } + else + { + /* The search has failed. */ + } + + jerry_release_value (search_info.desired_property); +} +``` +**See also** + +- [jerry_objects_foreach_t](#jerry_objects_foreach_t) + +## jerry_objects_foreach_by_native_info + +**Summary** + +Iterate over objects matching a certain native data type. + +*Note*: Values obtained in `foreach_p` must be retained using [jerry_acquire_value](#jerry_acquire_value). + +**Prototype** + +```c +bool jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_info_p, + jerry_objects_foreach_by_native_info_t foreach_p, + void *user_data_p); +``` + +- `native_info_p` - native pointer's type infomation. +- return value + - `true`, if the search function terminated the traversal by returning `false` + - `false`, if the end of the list of objects was reached + +**Example** + +```c +typedef struct +{ + int foo; + bool bar; +} native_obj_t; + +typedef struct +{ + jerry_value_t found_object; + void *match_data_p; +} find_object_data_t; + +static void native_freecb (void *native_p) +{ + ... // free the native pointer +} /* native_freecb */ + +// NOTE: The address (!) of type_info acts as a way to uniquely "identify" the +// C type `native_obj_t *`. +static const jerry_object_native_info_t native_obj_type_info = +{ + .free_cb = native_freecb +}; + +// Function creating JS object that is "backed" by a native_obj_t *: +{ + ... + + // construct object and native_set value: + jerry_value_t object = ...; + native_obj_t *native_obj_p = malloc (sizeof (*native_obj_p)); + jerry_set_object_native_pointer (object, native_obj_p, &native_obj_type_info); + + ... +} + +// Native method that retrieves the JavaScript object by way of its native data: +static bool find_object (const jerry_value_t candidate, void *data_p, void *user_data_p) +{ + find_object_data_t *find_data_p = (find_object_data_t *) user_data_p; + + if (find_data_p->match_data_p == data_p) + { + // If the object was found, acquire it and store it in the user data. + find_data_p->found_object = jerry_acquire_value (candidate); + + // Stop traversing over the objects. + return false; + } + + // Indicate that the object was not found, so traversal must continue. + return true; +} /* find_object */ +... +{ + find_object_data_t find_data = + { + .match_data = native_obj + }; + + if (jerry_objects_foreach_by_native_info (&native_obj_type_info, find_object, &find_data)) + { + // The object was found and is now stored in find_data.found_object. After using it, it must be released. + ... + jerry_release_value (find_data.found_object); + } + else + { + // The object was not found. + } + ... +} +``` + +**See also** + +- [jerry_create_object](#jerry_create_object) +- [jerry_set_object_native_pointer](#jerry_set_object_native_pointer) +- [jerry_get_object_native_pointer](#jerry_get_object_native_pointer) +- [jerry_object_native_info_t](#jerry_object_native_info_t) +- [jerry_objects_foreach](#jerry_objects_foreach) + # Input validator functions diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 4b9e002b..b0e285eb 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -2463,6 +2463,70 @@ jerry_set_object_native_handle (const jerry_value_t obj_val, /**< object to set } /* jerry_set_object_native_handle */ /** + * Traverse objects. + * + * @return true - traversal was interrupted by the callback. + * false - otherwise - traversal visited all objects. + */ +bool jerry_objects_foreach (jerry_objects_foreach_t foreach_p, + void *user_data_p) +{ + jerry_assert_api_available (); + + JERRY_ASSERT (foreach_p != NULL); + + for (ecma_object_t *iter_p = JERRY_CONTEXT (ecma_gc_objects_p); + iter_p != NULL; + iter_p = ECMA_GET_POINTER (ecma_object_t, iter_p->gc_next_cp)) + { + if (!ecma_is_lexical_environment (iter_p) + && !foreach_p (ecma_make_object_value (iter_p), user_data_p)) + { + return true; + } + } + + return false; +} /* jerry_objects_foreach */ + +/** + * Traverse objects having a given native type info. + * + * @return true - traversal was interrupted by the callback. + * false - otherwise - traversal visited all objects. + */ +bool +jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_info_p, + jerry_objects_foreach_by_native_info_t foreach_p, + void *user_data_p) +{ + jerry_assert_api_available (); + + JERRY_ASSERT (native_info_p != NULL); + JERRY_ASSERT (foreach_p != NULL); + + ecma_native_pointer_t *native_pointer_p; + + for (ecma_object_t *iter_p = JERRY_CONTEXT (ecma_gc_objects_p); + iter_p != NULL; + iter_p = ECMA_GET_POINTER (ecma_object_t, iter_p->gc_next_cp)) + { + if (!ecma_is_lexical_environment (iter_p)) + { + native_pointer_p = ecma_get_native_pointer_value (iter_p, LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER); + if (native_pointer_p + && ((const jerry_object_native_info_t *) native_pointer_p->u.info_p) == native_info_p + && !foreach_p (ecma_make_object_value (iter_p), native_pointer_p->data_p, user_data_p)) + { + return true; + } + } + } + + return false; +} /* jerry_objects_foreach_by_native_info */ + +/** * Get native pointer and its type information, associated with specified object. * * Note: diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index e58b7d64..1919abba 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -210,6 +210,18 @@ 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 applied for each object in the engine. + */ +typedef bool (*jerry_objects_foreach_t) (const jerry_value_t object, + void *user_data_p); + +/** + * Function type applied for each matching object in the engine. + */ +typedef bool (*jerry_objects_foreach_by_native_info_t) (const jerry_value_t object, + void *object_data_p, + void *user_data_p); /** * User context item manager @@ -439,6 +451,11 @@ void jerry_set_object_native_handle (const jerry_value_t obj_val, uintptr_t hand bool jerry_get_object_native_pointer (const jerry_value_t obj_val, void **out_native_pointer_p, const jerry_object_native_info_t **out_pointer_info_p); +bool jerry_objects_foreach (jerry_objects_foreach_t foreach_p, + void *user_data); +bool jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_info_p, + jerry_objects_foreach_by_native_info_t foreach_p, + void *user_data_p); void jerry_set_object_native_pointer (const jerry_value_t obj_val, void *native_pointer_p, const jerry_object_native_info_t *native_info_p); diff --git a/tests/unit-core/test-objects-foreach.c b/tests/unit-core/test-objects-foreach.c new file mode 100644 index 00000000..fef7094d --- /dev/null +++ b/tests/unit-core/test-objects-foreach.c @@ -0,0 +1,133 @@ +/* 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 int test_data = 1; + +static void free_test_data (void *data_p) +{ + TEST_ASSERT ((int *) data_p == &test_data); +} /* free_test_data */ + +static const jerry_object_native_info_t test_info = +{ + .free_cb = free_test_data +}; + +static const char *strict_equal_source = "var x = function(a, b) {return a === b;}; x"; + +static bool +find_test_object_by_data (const jerry_value_t candidate, + void *object_data_p, + void *context_p) +{ + if (object_data_p == &test_data) + { + *((jerry_value_t *) context_p) = jerry_acquire_value (candidate); + return false; + } + return true; +} /* find_test_object_by_data */ + +static bool +find_test_object_by_property (const jerry_value_t candidate, + void *context_p) +{ + jerry_value_t *args_p = (jerry_value_t *) context_p; + jerry_value_t result = jerry_has_property (candidate, args_p[0]); + + bool has_property = (!jerry_value_has_error_flag (result) && jerry_get_boolean_value (result)); + + /* If the object has the desired property, store a new reference to it in args_p[1]. */ + if (has_property) + { + args_p[1] = jerry_acquire_value (candidate); + } + + jerry_release_value (result); + + /* Stop iterating if we've found our object. */ + return !has_property; +} /* find_test_object_by_property */ + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + /* Render strict-equal as a function. */ + jerry_value_t parse_result = jerry_parse ((jerry_char_t *) strict_equal_source, strlen (strict_equal_source), true); + TEST_ASSERT (!jerry_value_has_error_flag (parse_result)); + jerry_value_t strict_equal = jerry_run (parse_result); + TEST_ASSERT (!jerry_value_has_error_flag (strict_equal)); + jerry_release_value (parse_result); + + /* Create an object and associate some native data with it. */ + jerry_value_t object = jerry_create_object (); + jerry_set_object_native_pointer (object, &test_data, &test_info); + + /* Retrieve the object by its native pointer. */ + + jerry_value_t found_object; + TEST_ASSERT (jerry_objects_foreach_by_native_info (&test_info, find_test_object_by_data, &found_object)); + jerry_value_t args[2] = {object, found_object}; + + /* Assert that the correct object was retrieved. */ + jerry_value_t undefined = jerry_create_undefined (); + jerry_value_t strict_equal_result = jerry_call_function (strict_equal, undefined, args, 2); + TEST_ASSERT (jerry_value_is_boolean (strict_equal_result) && jerry_get_boolean_value (strict_equal_result)); + jerry_release_value (strict_equal_result); + jerry_release_value (found_object); + jerry_release_value (object); + + /* Collect garbage. */ + jerry_gc (); + + /* Attempt to retrieve the object by its native pointer again. */ + TEST_ASSERT (!jerry_objects_foreach_by_native_info (&test_info, find_test_object_by_data, &found_object)); + + /* Create an object and set a property on it. */ + object = jerry_create_object (); + jerry_value_t property_name = jerry_create_string ((jerry_char_t *) "xyzzy"); + jerry_value_t property_value = jerry_create_number (42); + jerry_release_value (jerry_set_property (object, property_name, property_value)); + jerry_release_value (property_value); + + /* Retrieve the object by the presence of its property, placing it at args[1]. */ + args[0] = property_name; + TEST_ASSERT (jerry_objects_foreach (find_test_object_by_property, args)); + + /* Assert that the right object was retrieved and release both the original reference to it and the retrieved one. */ + args[0] = object; + strict_equal_result = jerry_call_function (strict_equal, undefined, args, 2); + TEST_ASSERT (jerry_value_is_boolean (strict_equal_result) && jerry_get_boolean_value (strict_equal_result)); + jerry_release_value (strict_equal_result); + jerry_release_value (args[0]); + jerry_release_value (args[1]); + + /* Collect garbage. */ + jerry_gc (); + + /* Attempt to retrieve the object by the presence of its property again. */ + args[0] = property_name; + TEST_ASSERT (!jerry_objects_foreach (find_test_object_by_property, args)); + + jerry_release_value (property_name); + jerry_release_value (undefined); + jerry_release_value (strict_equal); + jerry_cleanup (); +} /* main */ |