mirror of
https://github.com/jerryscript-project/jerryscript.git
synced 2025-12-15 16:29:21 +00:00
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
This commit is contained in:
parent
bb84466fcf
commit
3664d9ddd1
@ -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
|
||||
|
||||
|
||||
@ -2462,6 +2462,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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
133
tests/unit-core/test-objects-foreach.c
Normal file
133
tests/unit-core/test-objects-foreach.c
Normal file
@ -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 */
|
||||
Loading…
x
Reference in New Issue
Block a user