Add reference support for native pointers. (#4615)

JerryScript-DCO-1.0-Signed-off-by: Zoltan Herczeg zherczeg.u-szeged@partner.samsung.com
This commit is contained in:
Zoltan Herczeg 2021-04-19 14:47:17 +02:00 committed by GitHub
parent cc1a263657
commit ec3ed65b56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 681 additions and 150 deletions

View File

@ -902,33 +902,56 @@ typedef bool (*jerry_backtrace_callback_t) (jerry_backtrace_frame_t *frame_p, vo
**Summary**
The type information of the native pointer.
It includes the free callback that will be called when associated JavaScript object is garbage collected. It can be left NULL in case it is not needed.
Type information for native pointers. Since each native pointer has a type information,
multiple native pointers can be assigned to an object, and these can be updated or
deleted independently.
Typically, one would create a `static const jerry_object_native_info_t` for
each distinct C type for which a pointer is used with
`jerry_set_object_native_pointer ()` and `jerry_get_object_native_pointer ()`.
This way, each `const jerry_object_native_info_t *` pointer address value itself
uniquely identifies the C type of the native pointer.
The type information has a free callback, which is called when the object is freed
by the garbage collector. If the callback is NULL, the application is not notified
about the destruction of the object.
See [jerry_get_object_native_pointer](#jerry_get_object_native_pointer)
for a best-practice code example.
The buffer pointed by the native pointer can have a fixed number of jerry values,
which refer to other values as long as the object is alive. The starting byte
offset and the number of these values are specified by `offset_of_references` and
`number_of_references` fields respectively. Before a buffer is attached to an
object by [jerry_set_object_native_pointer](#jerry_set_object_native_pointer),
the values must be initialized to undefined by
[jerry_native_pointer_init_references](#jerry_native_pointer_init_references).
When a buffer is no longer attached to any object, the
[jerry_native_pointer_release_references](#jerry_native_pointer_release_references)
must be called to release the values. A single buffer can be attached to any
number of living objects. When a buffer is currently attached to at least
one object, the references can be updated by
[jerry_native_pointer_set_reference](#jerry_native_pointer_set_reference).
However, if the buffer is no longer attached to an object, the finalize function
must be called even if the buffer is reattached to another object later. In this
case, calling the init function after the finalization is optional, because the
finalize function also initializes all values to undefined.
**Prototype**
```c
typedef struct
{
jerry_object_native_free_callback_t free_cb;
jerry_object_native_free_callback_t free_cb; /**< the free callback of the native pointer */
uint16_t number_of_references; /**< the number of value references which are marked by the garbage collector */
uint16_t offset_of_references; /**< byte offset indicating the start offset of value
* references in the user allocated buffer */
} jerry_object_native_info_t;
```
*New in version 2.0*.
*Changed in version [[NEXT_RELEASE]]*: Added `number_of_references`, and `offset_of_references` fields.
**See also**
- [jerry_set_object_native_pointer](#jerry_set_object_native_pointer)
- [jerry_get_object_native_pointer](#jerry_get_object_native_pointer)
- [jerry_delete_object_native_pointer](#jerry_delete_object_native_pointer)
- [jerry_native_pointer_init_references](#jerry_native_pointer_init_references)
- [jerry_native_pointer_release_references](#jerry_native_pointer_release_references)
- [jerry_native_pointer_set_reference](#jerry_native_pointer_set_reference)
## jerry_object_property_foreach_t
@ -9021,6 +9044,171 @@ best-practice example.
- [jerry_object_native_info_t](#jerry_object_native_info_t)
## jerry_native_pointer_init_references
**Summary**
Initialize the references stored in a buffer pointed by a native pointer.
The references are initialized to undefined. This function must be called
before the buffer is attached to an object by
[jerry_set_object_native_pointer](#jerry_set_object_native_pointer).
*Note*:
- The description of [jerry_object_native_info_t](#jerry_object_native_info_t)
provides detailed information about these references.
**Prototype**
```c
void
jerry_native_pointer_init_references (void *native_pointer_p,
const jerry_object_native_info_t *native_info_p);
```
- `native_pointer_p` - a valid non-NULL pointer to a native buffer.
- `native_info_p` - native pointer's type information.
*New in version [[NEXT_RELEASE]]*.
**Example**
[doctest]: # ()
```c
#include <stdlib.h>
#include "jerryscript.h"
typedef struct
{
uint32_t user_data;
jerry_value_t a;
jerry_value_t b;
uint32_t user_other_data;
} user_buffer_t;
static void
native_references_free_callback (void *native_p, /**< native pointer */
jerry_object_native_info_t *info_p) /**< native info */
{
/* References must be finalized when a buffer is no longer attached. */
jerry_native_pointer_release_references (native_p, info_p);
free (native_p);
} /* native_references_free_callback */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_references_free_callback,
.number_of_references = 2,
.offset_of_references = offsetof(user_buffer_t, a),
};
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t object_value = jerry_create_object ();
user_buffer_t *buffer_p = (user_buffer_t *) malloc (sizeof (user_buffer_t));
/* References must be initialized before a buffer is attached. */
jerry_native_pointer_init_references ((void *) buffer_p, &native_info);
jerry_set_object_native_pointer (object_value, (void *) buffer_p, &native_info);
/* References can be modified after the buffer is attached.
* This example sets a self reference. */
jerry_native_pointer_set_reference (&buffer_p->a, object_value);
jerry_release_value (object_value);
jerry_cleanup ();
return 0;
}
```
**See also**
- [jerry_set_object_native_pointer](#jerry_set_object_native_pointer)
- [jerry_native_pointer_release_references](#jerry_native_pointer_release_references)
- [jerry_native_pointer_set_reference](#jerry_native_pointer_set_reference)
## jerry_native_pointer_release_references
**Summary**
Release the value references stored in a buffer pointed by a native pointer.
This function must be called after a buffer is no longer attached to any
object, even if the buffer is attached to another object again. This
function also initializes the values to undefined, so calling
[jerry_native_pointer_init_references](#jerry_native_pointer_init_references)
is optional before the buffer is attached again.
*Note*:
- The description of [jerry_object_native_info_t](#jerry_object_native_info_t)
provides detailed information about these references.
**Prototype**
```c
void
jerry_native_pointer_release_references (void *native_pointer_p,
const jerry_object_native_info_t *native_info_p);
```
- `native_pointer_p` - a valid non-NULL pointer to a native buffer.
- `native_info_p` - native pointer's type information.
*New in version [[NEXT_RELEASE]]*.
**Example**
See the example of [jerry_native_pointer_init_references](#jerry_native_pointer_init_references).
**See also**
- [jerry_set_object_native_pointer](#jerry_set_object_native_pointer)
- [jerry_native_pointer_init_references](#jerry_native_pointer_init_references)
- [jerry_native_pointer_set_reference](#jerry_native_pointer_set_reference)
## jerry_native_pointer_set_reference
**Summary**
Updates a value reference inside the area specified by the `number_of_references` and
`offset_of_references` fields in its corresponding
[jerry_object_native_info_t](#jerry_object_native_info_t) data. The area must be
part of a buffer which is currently assigned to an object.
*Note*:
- The description of [jerry_object_native_info_t](#jerry_object_native_info_t)
provides detailed information about these references.
**Prototype**
```c
void
jerry_native_pointer_set_reference (jerry_value_t *reference_p,
jerry_value_t value)
```
- `reference_p` - a valid non-NULL pointer to a reference in a native buffer.
- `value` - new value of the reference.
*New in version [[NEXT_RELEASE]]*.
**Example**
See the example of [jerry_native_pointer_init_references](#jerry_native_pointer_init_references).
**See also**
- [jerry_set_object_native_pointer](#jerry_set_object_native_pointer)
- [jerry_native_pointer_init_references](#jerry_native_pointer_init_references)
- [jerry_native_pointer_release_references](#jerry_native_pointer_release_references)
## jerry_object_get_property_names
**Summary**

View File

@ -4316,7 +4316,7 @@ jerry_set_object_native_pointer (const jerry_value_t obj_val, /**< object to set
{
ecma_object_t *object_p = ecma_get_object_from_value (obj_val);
ecma_create_native_pointer_property (object_p, native_pointer_p, (void *) native_info_p);
ecma_create_native_pointer_property (object_p, native_pointer_p, native_info_p);
}
} /* jerry_set_object_native_pointer */
@ -4349,6 +4349,87 @@ jerry_delete_object_native_pointer (const jerry_value_t obj_val, /**< object to
return false;
} /* jerry_delete_object_native_pointer */
/**
* Initialize the references stored in a buffer pointed by a native pointer.
* The references are initialized to undefined.
*/
void
jerry_native_pointer_init_references (void *native_pointer_p, /**< a valid non-NULL pointer to a native buffer */
const jerry_object_native_info_t *native_info_p) /**< the type info of
* the native pointer */
{
jerry_assert_api_available ();
if (native_pointer_p == NULL || native_info_p == NULL)
{
return;
}
ecma_value_t *value_p = (ecma_value_t *) (((uint8_t *) native_pointer_p) + native_info_p->offset_of_references);
ecma_value_t *end_p = value_p + native_info_p->number_of_references;
while (value_p < end_p)
{
*value_p++ = ECMA_VALUE_UNDEFINED;
}
} /* jerry_native_pointer_init_references */
/**
* Release the value references after a buffer pointed by a native pointer
* is not attached to an object anymore. All references are set to undefined
* similar to jerry_native_pointer_init_references.
*/
void
jerry_native_pointer_release_references (void *native_pointer_p, /**< a valid non-NULL pointer to a native buffer */
const jerry_object_native_info_t *native_info_p) /**< the type info of
* the native pointer */
{
jerry_assert_api_available ();
if (native_pointer_p == NULL || native_info_p == NULL)
{
return;
}
ecma_value_t *value_p = (ecma_value_t *) (((uint8_t *) native_pointer_p) + native_info_p->offset_of_references);
ecma_value_t *end_p = value_p + native_info_p->number_of_references;
while (value_p < end_p)
{
ecma_free_value_if_not_object (*value_p);
*value_p++ = ECMA_VALUE_UNDEFINED;
}
} /* jerry_native_pointer_release_references */
/**
* Updates a value reference inside the area specified by the number_of_references and
* offset_of_references fields in its corresponding jerry_object_native_info_t data.
* The area must be part of a buffer which is currently assigned to an object.
*
* Note:
* Error references are not supported, they are replaced by undefined values.
*/
void
jerry_native_pointer_set_reference (jerry_value_t *reference_p, /**< a valid non-NULL pointer to
* a reference in a native buffer. */
jerry_value_t value) /**< new value of the reference */
{
jerry_assert_api_available ();
if (reference_p == NULL)
{
return;
}
if (ecma_is_value_error_reference (value))
{
value = ECMA_VALUE_UNDEFINED;
}
ecma_free_value_if_not_object (*reference_p);
*reference_p = ecma_copy_value_if_not_object (value);
} /* jerry_native_pointer_set_reference */
/**
* Applies the given function to the every property in the object.
*

View File

@ -226,91 +226,25 @@ ecma_gc_mark_arguments_object (ecma_extended_object_t *ext_object_p) /**< argume
/**
* Mark referenced object from property
*/
static inline void JERRY_ATTR_ALWAYS_INLINE
ecma_gc_mark_properties (ecma_property_pair_t *property_pair_p) /**< property pair */
{
for (uint32_t index = 0; index < ECMA_PROPERTY_PAIR_ITEM_COUNT; index++)
{
uint8_t property = property_pair_p->header.types[index];
if (JERRY_LIKELY (ECMA_PROPERTY_IS_RAW (property)))
{
if (property & ECMA_PROPERTY_FLAG_DATA)
{
ecma_value_t value = property_pair_p->values[index].value;
if (ecma_is_value_object (value))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (value));
}
continue;
}
ecma_property_value_t *accessor_objs_p = property_pair_p->values + index;
ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p);
if (get_set_pair_p->getter_cp != JMEM_CP_NULL)
{
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp));
}
if (get_set_pair_p->setter_cp != JMEM_CP_NULL)
{
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp));
}
continue;
}
if (!ECMA_PROPERTY_IS_INTERNAL (property))
{
JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_DELETED
|| property == ECMA_PROPERTY_TYPE_HASHMAP);
continue;
}
JERRY_ASSERT (property_pair_p->names_cp[index] >= LIT_INTERNAL_MAGIC_STRING_FIRST_DATA
&& property_pair_p->names_cp[index] < LIT_MAGIC_STRING__COUNT);
#if JERRY_ESNEXT
if (property_pair_p->names_cp[index] == LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD)
{
ecma_environment_record_t *environment_record_p;
environment_record_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t,
property_pair_p->values[index].value);
if (environment_record_p->this_binding != ECMA_VALUE_UNINITIALIZED)
{
JERRY_ASSERT (ecma_is_value_object (environment_record_p->this_binding));
ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->this_binding));
}
JERRY_ASSERT (ecma_is_value_object (environment_record_p->function_object));
ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->function_object));
}
#endif /* JERRY_ESNEXT */
}
} /* ecma_gc_mark_properties */
#if JERRY_MODULE_SYSTEM
/**
* Mark objects with references (e.g. namespace objects)
*/
static void
ecma_gc_mark_properties_with_references (ecma_object_t *object_p) /**< object */
ecma_gc_mark_properties (ecma_object_t *object_p, /**< object */
bool mark_references) /**< mark references */
{
JERRY_UNUSED (mark_references);
#if !JERRY_MODULE_SYSTEM
JERRY_ASSERT (!mark_references);
#endif /* !JERRY_MODULE_SYSTEM */
jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp;
#if JERRY_PROPRETY_HASHMAP
if (prop_iter_cp != JMEM_CP_NULL)
{
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t,
prop_iter_cp);
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
prop_iter_cp = object_p->u1.property_list_cp;
prop_iter_cp = prop_iter_p->next_property_cp;
}
}
#endif /* JERRY_PROPRETY_HASHMAP */
@ -320,27 +254,128 @@ ecma_gc_mark_properties_with_references (ecma_object_t *object_p) /**< object */
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
for (int i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++)
ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p;
for (uint32_t index = 0; index < ECMA_PROPERTY_PAIR_ITEM_COUNT; index++)
{
ecma_property_t *property_p = (ecma_property_t *) (prop_iter_p->types + i);
uint8_t property = property_pair_p->header.types[index];
if (ECMA_PROPERTY_IS_RAW (*property_p)
&& (*property_p & ECMA_PROPERTY_FLAG_DATA))
if (JERRY_LIKELY (ECMA_PROPERTY_IS_RAW (property)))
{
ecma_value_t value = ((ecma_property_pair_t *) prop_iter_p)->values[i].value;
if (ecma_is_value_object (value))
if (property & ECMA_PROPERTY_FLAG_DATA)
{
ecma_gc_set_object_visited (ecma_get_object_from_value (value));
ecma_value_t value = property_pair_p->values[index].value;
if (ecma_is_value_object (value))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (value));
}
continue;
}
#if JERRY_MODULE_SYSTEM
if (mark_references)
{
continue;
}
#endif /* JERRY_MODULE_SYSTEM */
ecma_property_value_t *accessor_objs_p = property_pair_p->values + index;
ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p);
if (get_set_pair_p->getter_cp != JMEM_CP_NULL)
{
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp));
}
if (get_set_pair_p->setter_cp != JMEM_CP_NULL)
{
ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp));
}
continue;
}
if (!ECMA_PROPERTY_IS_INTERNAL (property))
{
JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_DELETED
|| property == ECMA_PROPERTY_TYPE_HASHMAP);
continue;
}
JERRY_ASSERT (property_pair_p->names_cp[index] >= LIT_INTERNAL_MAGIC_STRING_FIRST_DATA
&& property_pair_p->names_cp[index] < LIT_MAGIC_STRING__COUNT);
switch (property_pair_p->names_cp[index])
{
#if JERRY_ESNEXT
case LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD:
{
ecma_environment_record_t *environment_record_p;
environment_record_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t,
property_pair_p->values[index].value);
if (environment_record_p->this_binding != ECMA_VALUE_UNINITIALIZED)
{
JERRY_ASSERT (ecma_is_value_object (environment_record_p->this_binding));
ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->this_binding));
}
JERRY_ASSERT (ecma_is_value_object (environment_record_p->function_object));
ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->function_object));
break;
}
#endif /* JERRY_ESNEXT */
case LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES:
{
jerry_value_t value = property_pair_p->values[index].value;
if (value == JMEM_CP_NULL)
{
JERRY_ASSERT (!(property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL));
break;
}
ecma_native_pointer_t *item_p;
item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value);
do
{
jerry_object_native_info_t *native_info_p = item_p->native_info_p;
JERRY_ASSERT (native_info_p != NULL && native_info_p->number_of_references > 0);
uint8_t *start_p = ((uint8_t *) item_p->native_p) + native_info_p->offset_of_references;
ecma_value_t *value_p = (ecma_value_t *) start_p;
ecma_value_t *end_p = value_p + native_info_p->number_of_references;
do
{
if (ecma_is_value_object (*value_p))
{
ecma_gc_set_object_visited (ecma_get_object_from_value (*value_p));
}
}
while (++value_p < end_p);
if (property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)
{
break;
}
item_p = &(((ecma_native_pointer_chain_t *) item_p)->next_p->data);
}
while (item_p != NULL);
break;
}
}
}
prop_iter_cp = prop_iter_p->next_property_cp;
}
} /* ecma_gc_mark_properties_with_references */
#endif /* JERRY_MODULE_SYSTEM */
} /* ecma_gc_mark_properties */
/**
* Mark objects referenced by bound function object.
@ -716,7 +751,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
#if JERRY_MODULE_SYSTEM
if (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA)
{
ecma_gc_mark_properties_with_references (object_p);
ecma_gc_mark_properties (object_p, true);
ecma_gc_set_object_visited (((ecma_lexical_environment_class_t *) object_p)->module_p);
return;
}
@ -809,7 +844,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
JERRY_ASSERT (proto_cp == JMEM_CP_NULL);
ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t,
ext_object_p->u.cls.u3.value));
ecma_gc_mark_properties_with_references (object_p);
ecma_gc_mark_properties (object_p, true);
return;
}
#endif /* JERRY_MODULE_SYSTEM */
@ -1147,28 +1182,7 @@ ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */
}
}
jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp;
#if JERRY_PROPRETY_HASHMAP
if (prop_iter_cp != JMEM_CP_NULL)
{
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP)
{
prop_iter_cp = prop_iter_p->next_property_cp;
}
}
#endif /* JERRY_PROPRETY_HASHMAP */
while (prop_iter_cp != JMEM_CP_NULL)
{
ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp);
JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p));
ecma_gc_mark_properties ((ecma_property_pair_t *) prop_iter_p);
prop_iter_cp = prop_iter_p->next_property_cp;
}
ecma_gc_mark_properties (object_p, false);
} /* ecma_gc_mark */
/**
@ -1185,11 +1199,14 @@ ecma_gc_free_native_pointer (ecma_property_t property, /**< property descriptor
ecma_native_pointer_t *native_pointer_p;
native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value);
jerry_object_native_free_callback_t free_cb = native_pointer_p->info_p->free_cb;
if (free_cb != NULL)
if (native_pointer_p->native_info_p != NULL)
{
free_cb (native_pointer_p->native_p, native_pointer_p->info_p);
jerry_object_native_free_callback_t free_cb = native_pointer_p->native_info_p->free_cb;
if (free_cb != NULL)
{
free_cb (native_pointer_p->native_p, native_pointer_p->native_info_p);
}
}
jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t));
@ -1206,13 +1223,13 @@ ecma_gc_free_native_pointer (ecma_property_t property, /**< property descriptor
do
{
if (item_p->data.info_p != NULL)
if (item_p->data.native_info_p != NULL)
{
jerry_object_native_free_callback_t free_cb = item_p->data.info_p->free_cb;
jerry_object_native_free_callback_t free_cb = item_p->data.native_info_p->free_cb;
if (free_cb != NULL)
{
free_cb (item_p->data.native_p, item_p->data.info_p);
free_cb (item_p->data.native_p, item_p->data.native_info_p);
}
}
@ -1534,7 +1551,8 @@ ecma_gc_free_property (ecma_object_t *object_p, /**< object */
#endif /* JERRY_BUILTIN_WEAKMAP || JERRY_BUILTIN_WEAKSET || JERRY_BUILTIN_WEAKREF */
default:
{
JERRY_ASSERT (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
JERRY_ASSERT (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER
|| name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES);
ecma_gc_free_native_pointer (property, value);
break;
}

View File

@ -304,7 +304,7 @@ typedef ecma_value_t (*ecma_native_handler_t) (const struct jerry_call_info_t *c
typedef struct
{
void *native_p; /**< points to the data of the object */
jerry_object_native_info_t *info_p; /**< native info */
jerry_object_native_info_t *native_info_p; /**< native info */
} ecma_native_pointer_t;
/**

View File

@ -36,10 +36,15 @@
bool
ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create property in */
void *native_p, /**< native pointer */
void *info_p) /**< native pointer's type info */
const jerry_object_native_info_t *native_info_p) /**< native type info */
{
ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
if (native_info_p != NULL && native_info_p->number_of_references > 0)
{
name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES);
}
if (ecma_op_object_is_fast_array (obj_p))
{
ecma_fast_array_convert_to_normal (obj_p);
@ -67,7 +72,7 @@ ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create
native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value_p->value);
if (native_pointer_p->info_p == info_p)
if (native_pointer_p->native_info_p == native_info_p)
{
native_pointer_p->native_p = native_p;
return false;
@ -107,7 +112,7 @@ ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create
while (true)
{
if (item_p->data.info_p == info_p)
if (item_p->data.native_info_p == native_info_p)
{
/* The native info already exists -> update the corresponding data */
item_p->data.native_p = native_p;
@ -134,7 +139,7 @@ ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create
}
native_pointer_p->native_p = native_p;
native_pointer_p->info_p = info_p;
native_pointer_p->native_info_p = (jerry_object_native_info_t *) native_info_p;
return is_new;
} /* ecma_create_native_pointer_property */
@ -151,7 +156,7 @@ ecma_create_native_pointer_property (ecma_object_t *obj_p, /**< object to create
*/
ecma_native_pointer_t *
ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property value from */
void *info_p) /**< native pointer's type info */
const jerry_object_native_info_t *native_info_p) /**< native type info */
{
if (ecma_op_object_is_fast_array (obj_p))
{
@ -160,6 +165,12 @@ ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property
}
ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
if (native_info_p != NULL && native_info_p->number_of_references > 0)
{
name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES);
}
ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
if (property_p == NULL)
@ -174,7 +185,7 @@ ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property
ecma_native_pointer_t *native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t,
value_p->value);
if (native_pointer_p->info_p == info_p)
if (native_pointer_p->native_info_p == native_info_p)
{
return native_pointer_p;
}
@ -195,7 +206,7 @@ ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property
do
{
if (item_p->data.info_p == info_p)
if (item_p->data.native_info_p == native_info_p)
{
return &item_p->data;
}
@ -219,7 +230,7 @@ ecma_get_native_pointer_value (ecma_object_t *obj_p, /**< object to get property
*/
bool
ecma_delete_native_pointer_property (ecma_object_t *obj_p, /**< object to delete property from */
void *info_p) /**< native pointer's type info */
const jerry_object_native_info_t *native_info_p) /**< native type info */
{
if (ecma_op_object_is_fast_array (obj_p))
{
@ -228,6 +239,12 @@ ecma_delete_native_pointer_property (ecma_object_t *obj_p, /**< object to delete
}
ecma_string_t *name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER);
if (native_info_p != NULL && native_info_p->number_of_references > 0)
{
name_p = ecma_get_internal_string (LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES);
}
ecma_property_t *property_p = ecma_find_named_property (obj_p, name_p);
if (property_p == NULL)
@ -242,7 +259,7 @@ ecma_delete_native_pointer_property (ecma_object_t *obj_p, /**< object to delete
ecma_native_pointer_t *native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t,
value_p->value);
if (native_pointer_p->info_p != info_p)
if (native_pointer_p->native_info_p != native_info_p)
{
return false;
}
@ -269,7 +286,7 @@ ecma_delete_native_pointer_property (ecma_object_t *obj_p, /**< object to delete
do
{
if (item_p->data.info_p == info_p)
if (item_p->data.native_info_p == native_info_p)
{
if (prev_p == NULL)
{

View File

@ -545,9 +545,11 @@ uintptr_t ecma_get_current_stack_usage (void);
#endif /* (JERRY_STACK_LIMIT != 0) */
/* ecma-helpers-external-pointers.c */
bool ecma_create_native_pointer_property (ecma_object_t *obj_p, void *native_p, void *info_p);
ecma_native_pointer_t *ecma_get_native_pointer_value (ecma_object_t *obj_p, void *info_p);
bool ecma_delete_native_pointer_property (ecma_object_t *obj_p, void *info_p);
bool ecma_create_native_pointer_property (ecma_object_t *obj_p, void *native_p,
const jerry_object_native_info_t *native_info_p);
ecma_native_pointer_t *ecma_get_native_pointer_value (ecma_object_t *obj_p,
const jerry_object_native_info_t *native_info_p);
bool ecma_delete_native_pointer_property (ecma_object_t *obj_p, const jerry_object_native_info_t *native_info_p);
/* ecma-helpers-conversion.c */
ecma_number_t ecma_utf8_string_to_number (const lit_utf8_byte_t *str_p, lit_utf8_size_t str_size,

View File

@ -247,6 +247,11 @@ void jerry_set_object_native_pointer (const jerry_value_t obj_val,
const jerry_object_native_info_t *native_info_p);
bool jerry_delete_object_native_pointer (const jerry_value_t obj_val,
const jerry_object_native_info_t *native_info_p);
void jerry_native_pointer_init_references (void *native_pointer_p,
const jerry_object_native_info_t *native_info_p);
void jerry_native_pointer_release_references (void *native_pointer_p,
const jerry_object_native_info_t *native_info_p);
void jerry_native_pointer_set_reference (jerry_value_t *reference_p, jerry_value_t value);
bool jerry_objects_foreach (jerry_objects_foreach_t foreach_p,
void *user_data);

View File

@ -387,6 +387,9 @@ typedef void *(*jerry_context_alloc_t) (size_t size, void *cb_data_p);
typedef struct jerry_object_native_info_t
{
jerry_object_native_free_callback_t free_cb; /**< the free callback of the native pointer */
uint16_t number_of_references; /**< the number of value references which are marked by the garbage collector */
uint16_t offset_of_references; /**< byte offset indicating the start offset of value
* references in the user allocated buffer */
} jerry_object_native_info_t;
/**

View File

@ -63,6 +63,8 @@ typedef enum
LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< native pointer info associated with an object */
LIT_INTERNAL_MAGIC_STRING_FIRST_DATA = LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER, /**< first index of special
* data properties */
LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES, /**< native pointer info associated with an object
* which contains references to other values */
LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD, /**< dynamic environment record needed by class constructors */
LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED, /**< computed class field name list */
LIT_INTERNAL_MAGIC_STRING_CONTAINER_WEAK_REFS, /**< Weak references to the current container object */

View File

@ -138,7 +138,9 @@ handler_construct_2_freecb (void *native_p, /**< native pointer */
#define JERRY_DEFINE_NATIVE_HANDLE_INFO(c_type, native_free_cb) \
static const jerry_object_native_info_t JERRY_NATIVE_HANDLE_INFO_FOR_CTYPE (c_type) = \
{ \
.free_cb = (jerry_object_native_free_callback_t) native_free_cb \
.free_cb = (jerry_object_native_free_callback_t) native_free_cb, \
.number_of_references = 0, \
.offset_of_references = 0, \
}
JERRY_DEFINE_NATIVE_HANDLE_INFO (bind1, handler_construct_1_freecb);

View File

@ -26,7 +26,9 @@ static void native_cb2 (void)
static const jerry_object_native_info_t native_info2 =
{
.free_cb = (jerry_object_native_free_callback_t) native_cb2
.free_cb = (jerry_object_native_free_callback_t) native_cb2,
.number_of_references = 0,
.offset_of_references = 0,
};
static void native_cb (void)
@ -40,7 +42,9 @@ static void native_cb (void)
static const jerry_object_native_info_t native_info =
{
.free_cb = (jerry_object_native_free_callback_t) native_cb
.free_cb = (jerry_object_native_free_callback_t) native_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static void *

View File

@ -33,18 +33,108 @@ native_free_callback (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info_1 =
{
.free_cb = native_free_callback,
.number_of_references = 0,
.offset_of_references = 0,
};
static const jerry_object_native_info_t native_info_2 =
{
.free_cb = NULL,
.number_of_references = 0,
.offset_of_references = 0,
};
static const jerry_object_native_info_t native_info_3 =
{
.free_cb = NULL,
.number_of_references = 0,
.offset_of_references = 0,
};
typedef struct
{
uint32_t check_before;
jerry_value_t a;
jerry_value_t b;
jerry_value_t c;
uint32_t check_after;
} test_references_t;
static test_references_t test_references1;
static test_references_t test_references2;
static test_references_t test_references3;
static test_references_t test_references4;
static int call_count = 0;
static void
native_references_free_callback (void *native_p, /**< native pointer */
jerry_object_native_info_t *info_p) /**< native info */
{
test_references_t *refs_p = (test_references_t *) native_p;
TEST_ASSERT ((refs_p == &test_references1 && test_references1.check_before == 0x12345678)
|| (refs_p == &test_references2 && test_references2.check_before == 0x87654321)
|| (refs_p == &test_references3 && test_references3.check_before == 0x12344321));
TEST_ASSERT (refs_p->check_before == refs_p->check_after);
uint32_t check = refs_p->check_before;
jerry_native_pointer_release_references (native_p, info_p);
TEST_ASSERT (jerry_value_is_undefined (refs_p->a));
TEST_ASSERT (jerry_value_is_undefined (refs_p->b));
TEST_ASSERT (jerry_value_is_undefined (refs_p->c));
TEST_ASSERT (refs_p->check_before == check);
TEST_ASSERT (refs_p->check_after == check);
call_count++;
} /* native_references_free_callback */
static const jerry_object_native_info_t native_info_4 =
{
.free_cb = native_references_free_callback,
.number_of_references = 3,
.offset_of_references = (uint16_t) offsetof (test_references_t, a),
};
static void
init_references (test_references_t *refs_p, /**< native pointer */
uint32_t check) /**< value for memory check */
{
/* Memory garbage */
refs_p->check_before = check;
refs_p->a = 1;
refs_p->b = 2;
refs_p->c = 3;
refs_p->check_after = check;
jerry_native_pointer_init_references ((void *) refs_p, &native_info_4);
TEST_ASSERT (jerry_value_is_undefined (refs_p->a));
TEST_ASSERT (jerry_value_is_undefined (refs_p->b));
TEST_ASSERT (jerry_value_is_undefined (refs_p->c));
TEST_ASSERT (refs_p->check_before == check);
TEST_ASSERT (refs_p->check_after == check);
} /* init_references */
static void
set_references (test_references_t *refs_p, /**< native pointer */
jerry_value_t value1, /**< first value to be set */
jerry_value_t value2, /**< second value to be set */
jerry_value_t value3) /**< third value to be set */
{
jerry_native_pointer_set_reference (&refs_p->a, value1);
jerry_native_pointer_set_reference (&refs_p->b, value2);
jerry_native_pointer_set_reference (&refs_p->c, value3);
TEST_ASSERT (jerry_value_is_object (value1) ? jerry_value_is_object (refs_p->a)
: jerry_value_is_string (refs_p->a));
TEST_ASSERT (jerry_value_is_object (value2) ? jerry_value_is_object (refs_p->b)
: jerry_value_is_string (refs_p->b));
TEST_ASSERT (jerry_value_is_object (value3) ? jerry_value_is_object (refs_p->c)
: jerry_value_is_string (refs_p->c));
} /* set_references */
static void
check_native_info (jerry_value_t object_value, /**< object value */
const jerry_object_native_info_t *native_info_p, /**< native info */
@ -149,10 +239,105 @@ main (void)
TEST_ASSERT (!jerry_get_object_native_pointer (object_value, NULL, &native_info_2));
TEST_ASSERT (!jerry_get_object_native_pointer (object_value, NULL, &native_info_3));
/* Test value references */
jerry_value_t string1_value = jerry_create_string ((const jerry_char_t *) "String1");
jerry_value_t string2_value = jerry_create_string ((const jerry_char_t *) "String2");
jerry_value_t object1_value = jerry_create_object ();
jerry_value_t object2_value = jerry_create_object ();
init_references (&test_references1, 0x12345678);
init_references (&test_references2, 0x87654321);
jerry_set_object_native_pointer (object1_value, (void *) &test_references1, &native_info_4);
jerry_set_object_native_pointer (object2_value, (void *) &test_references2, &native_info_4);
/* Assign values (cross reference between object1 and object2). */
set_references (&test_references1, string1_value, object2_value, string2_value);
set_references (&test_references2, string2_value, object1_value, string1_value);
jerry_gc (JERRY_GC_PRESSURE_HIGH);
/* Reassign values. */
set_references (&test_references1, object2_value, string2_value, string1_value);
set_references (&test_references2, object1_value, string1_value, string2_value);
jerry_gc (JERRY_GC_PRESSURE_HIGH);
jerry_release_value (object1_value);
jerry_release_value (object2_value);
object1_value = jerry_create_object ();
object2_value = jerry_create_object ();
init_references (&test_references3, 0x12344321);
/* Assign the same native pointer to multiple objects. */
jerry_set_object_native_pointer (object1_value, (void *) &test_references3, &native_info_4);
jerry_set_object_native_pointer (object2_value, (void *) &test_references3, &native_info_4);
set_references (&test_references3, object1_value, object2_value, string1_value);
jerry_gc (JERRY_GC_PRESSURE_HIGH);
init_references (&test_references4, 0x87655678);
/* Re-assign reference */
jerry_set_object_native_pointer (object1_value, (void *) &test_references4, &native_info_4);
set_references (&test_references4, string1_value, string2_value, string1_value);
jerry_set_object_native_pointer (object1_value, NULL, &native_info_4);
jerry_native_pointer_release_references ((void *) &test_references4, &native_info_4);
/* Calling jerry_native_pointer_init_references with test_references4 is optional here. */
jerry_set_object_native_pointer (object1_value, (void *) &test_references4, &native_info_4);
set_references (&test_references4, string2_value, string1_value, string2_value);
TEST_ASSERT (jerry_delete_object_native_pointer (object1_value, &native_info_4));
jerry_native_pointer_release_references ((void *) &test_references4, &native_info_4);
jerry_release_value (object1_value);
jerry_release_value (object2_value);
/* Delete references */
for (int i = 0; i < 3; i++)
{
object1_value = jerry_create_object ();
jerry_set_object_native_pointer (object1_value, global_p, NULL);
jerry_set_object_native_pointer (object1_value, (void *) &test_references4, &native_info_4);
jerry_set_object_native_pointer (object1_value, global_p, &native_info_2);
set_references (&test_references4, string1_value, string2_value, object1_value);
jerry_gc (JERRY_GC_PRESSURE_HIGH);
if (i == 1)
{
TEST_ASSERT (jerry_delete_object_native_pointer (object1_value, NULL));
}
else if (i == 2)
{
TEST_ASSERT (jerry_delete_object_native_pointer (object1_value, &native_info_2));
}
TEST_ASSERT (jerry_delete_object_native_pointer (object1_value, &native_info_4));
jerry_native_pointer_release_references ((void *) &test_references4, &native_info_4);
jerry_release_value (object1_value);
}
jerry_release_value (string1_value);
jerry_release_value (string2_value);
jerry_release_value (object_value);
jerry_cleanup ();
TEST_ASSERT (global_counter == 0);
TEST_ASSERT (call_count == 3);
return 0;
} /* main */

View File

@ -141,7 +141,9 @@ static void free_test_data (void *native_p, /**< native pointer */
static const jerry_object_native_info_t test_info =
{
.free_cb = free_test_data
.free_cb = free_test_data,
.number_of_references = 0,
.offset_of_references = 0,
};
static const jerry_char_t strict_equal_source[] = "var x = function(a, b) {return a === b;}; x";

View File

@ -162,6 +162,8 @@ proxy_native_freecb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t proxy_native_info =
{
.free_cb = proxy_native_freecb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -67,12 +67,16 @@ static const jerry_char_t test_source[] = TEST_STRING_LITERAL (
static const jerry_object_native_info_t thing_a_info =
{
.free_cb = NULL
.free_cb = NULL,
.number_of_references = 0,
.offset_of_references = 0,
};
static const jerry_object_native_info_t thing_b_info =
{
.free_cb = NULL
.free_cb = NULL,
.number_of_references = 0,
.offset_of_references = 0,
};
typedef struct

View File

@ -35,6 +35,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -35,6 +35,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -38,6 +38,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -38,6 +38,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -38,6 +38,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -35,6 +35,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -36,6 +36,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t

View File

@ -35,6 +35,8 @@ native_free_cb (void *native_p, /**< native pointer */
static const jerry_object_native_info_t native_info =
{
.free_cb = native_free_cb,
.number_of_references = 0,
.offset_of_references = 0,
};
static jerry_value_t