Allow the JS objects to have more than one native pointer data (#2814)

Currently JS objects can only have one native pointer data which could be a limitation in special cases.
This patch allows to register multiple native infos, which can be accessed/associated with the corresponding `jerry_object_native_info_t`.

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik
2019-04-16 07:50:49 +02:00
committed by László Langó
parent c818930cdc
commit b3f4aa6816
14 changed files with 436 additions and 180 deletions
+185 -51
View File
@@ -4756,23 +4756,11 @@ jerry_set_prototype (const jerry_value_t obj_val,
**Summary**
Get native pointer and its type information.
Get native pointer by the given type information.
The pointer and the type information are previously associated with the object by jerry_set_object_native_pointer.
*Note*: It is recommended to ensure that the `out_native_info_p` value pointer
is equal to the native info pointer that is expected, before casting
and accessing the `out_native_pointer_p`.
An example of when this is important: external functions that expect
`this` to have a native pointer of a certain C type.
It is possible in JavaScript to change `this` at will using `call()`,
`apply()` or `bind()`. Therefore, it is possible that the native pointer
of `this` is not of the expected C type. To handle this safely and
securely, one must always add type checks to make sure that the
`out_native_pointer_p` is of the expected type, before casting
and dereferencing `out_native_pointer_p`.
*Note*: `out_native_pointer_p` and `out_native_info_p` can be NULL, and it means the
caller doesn't want to get the native_pointer or type information.
*Note*: `out_native_pointer_p` can be NULL, and it means the
caller doesn't want to get the native_pointer.
**Prototype**
@@ -4780,73 +4768,185 @@ The pointer and the type information are previously associated with the object b
bool
jerry_get_object_native_pointer (const jerry_value_t obj_val,
void **out_native_pointer_p,
const jerry_object_native_info_t **out_native_info_p)
const jerry_object_native_info_t *native_info_p)
```
- `obj_val` - object value to get native pointer from.
- `out_native_pointer_p` - native pointer (output parameter).
- `out_native_info_p` - native pointer's type information (output parameter).
- `native_info_p` - native pointer's type information.
- return value
- true, if there is native pointer associated with the object
- true, if there is native pointer associated of the specified object with the given native type info
- false, otherwise
**Example**
```c
typedef struct {
int foo;
bool bar;
} native_obj_t;
[doctest]: # ()
static void native_freecb (void *native_p)
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jerryscript.h"
typedef struct
{
... // free the native pointer
char *data_p;
unsigned int length;
} buffer_native_object_t;
typedef struct
{
int area;
int perimeter;
} shape_native_object_t;
#define SECRET_INFO ((void *) 42)
static void
buffer_native_freecb (void *native_p)
{
char *data_p = ((buffer_native_object_t*)native_p)->data_p;
if (data_p != NULL)
{
free (data_p);
}
free (native_p);
}
static void
shape_native_freecb (void *native_p)
{
free (native_p);
}
static void
destructor_freecb (void *native_p)
{
printf("Note: the object has been freed\n");
}
// 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 =
// C type `buffer_native_object_t *`.
static const jerry_object_native_info_t buffer_obj_type_info =
{
.free_cb = native_freecb
.free_cb = buffer_native_freecb
};
// Function creating JS object that is "backed" by a native_obj_t *:
// NOTE: The address (!) of type_info acts as a way to uniquely "identify" the
// C type `shape_native_object_t *`.
static const jerry_object_native_info_t shape_obj_type_info =
{
...
.free_cb = shape_native_freecb
};
// construct object and native_set value:
jerry_value_t object = ...;
native_obj_t *native_obj = malloc(sizeof(*native_obj));
jerry_set_object_native_pointer (object, native_obj, &native_obj_type_info);
// NOTE: The address (!) of type_info is the unique "identifier"
static const jerry_object_native_info_t destructor_obj_type_info =
{
.free_cb = destructor_freecb
};
...
static void
print_buffer (char *data_p,
unsigned int length)
{
for (unsigned int i = 0; i < length; ++i)
{
printf("%c", data_p[i]);
}
printf("\n");
}
// Native method, `this` is expected to be "backed" by a native_obj_t *:
static void
do_stuff (jerry_value_t object)
{
void *native_p;
const jerry_object_native_info_t *type_p;
bool has_p = jerry_get_object_native_pointer (this_val, &native_p, &type_p);
bool has_p = jerry_get_object_native_pointer (object, &native_p, &buffer_obj_type_info);
if (has_p)
if (!has_p)
{
// The type_p pointer address itself is used to identify the type:
if (type_p == &native_obj_type_info)
// Process the error
return;
}
// It is safe to cast to buffer_native_object_t * and dereference the pointer:
buffer_native_object_t *buffer_p = (buffer_native_object_t *) native_p;
print_buffer (buffer_p->data_p, buffer_p->length); // Usage of buffer_p
bool need_shape_info = true; // implementation dependent
if (need_shape_info)
{
has_p = jerry_get_object_native_pointer (object, &native_p, &shape_obj_type_info);
if (!has_p)
{
// The type of this's native pointer matches what is expected.
// Only now is it safe to cast to native_obj_t * and dereference the
// pointer:
native_obj_t *native_obj = native_p;
native_obj->bar = ...; // Safe to access now!
// Process the error
return;
}
else
// It is safe to cast to shape_native_object_t * and dereference the pointer:
shape_native_object_t *shape_p = (shape_native_object_t *) native_p;
printf("Area: %d\tPerimeter: %d\n", shape_p->area, shape_p->perimeter); // Usage of shape_p
}
bool need_secret_info = true; // implementation dependent
if (need_secret_info)
{
has_p = jerry_get_object_native_pointer (object, &native_p, NULL);
if (!has_p)
{
// The type of this's native pointer is NOT what we expected!
// We should not cast to native_obj_t * and dereference because it's unsafe.
// Handle the error here, for example throw an error.
// Process the error
return;
}
printf("Secret: %d\n", (int)((uintptr_t) native_p)); // Usage of native_p
bool deleted = jerry_delete_object_native_pointer (object, NULL);
if (deleted)
{
printf("The secret is no longer available\n");
}
}
...
}
int
main (void)
{
jerry_init (JERRY_INIT_EMPTY);
jerry_value_t object = jerry_create_object ();
buffer_native_object_t *buffer_p = (buffer_native_object_t *) malloc (sizeof (buffer_native_object_t));
buffer_p->length = 14;
buffer_p->data_p = (char *) malloc (buffer_p->length * sizeof (char));
memcpy (buffer_p->data_p, "My buffer data", buffer_p->length);
jerry_set_object_native_pointer (object, buffer_p, &buffer_obj_type_info);
shape_native_object_t *shape_p = (shape_native_object_t *) malloc (sizeof (shape_native_object_t));
shape_p->area = 6;
shape_p->perimeter = 12;
jerry_set_object_native_pointer (object, shape_p, &shape_obj_type_info);
// The native pointer can be NULL. This gives possibily to get notified via the native type info's
// free callback when the object has been freed by the GC.
jerry_set_object_native_pointer (object, NULL, &destructor_obj_type_info);
// The native type info can be NULL as well. In this case the registered property is simply freed
// when the object is freed by te GC.
jerry_set_object_native_pointer (object, SECRET_INFO, NULL);
do_stuff (object);
jerry_release_value (object);
jerry_cleanup ();
return 0;
}
```
@@ -4897,6 +4997,40 @@ best-practice example.
- [jerry_get_object_native_pointer](#jerry_get_object_native_pointer)
- [jerry_object_native_info_t](#jerry_object_native_info_t)
## jerry_delete_object_native_pointer
**Summary**
Delete the native pointer of the specified object associated with the given native type info.
You can get them by calling jerry_get_object_native_pointer later.
*Note*:
- If the specified object has no matching native pointer for the given native type info the operation has no effect.
- This operation cannot throw an exception.
**Prototype**
```c
bool
jerry_delete_object_native_pointer (const jerry_value_t obj_val,
const jerry_object_native_info_t *info_p)
```
- `obj_val` - object to delete native pointer from.
- `info_p` - native pointer's type information.
**Example**
See [jerry_get_object_native_pointer](#jerry_get_object_native_pointer) for a
best-practice example.
**See also**
- [jerry_create_object](#jerry_create_object)
- [jerry_get_object_native_pointer](#jerry_get_object_native_pointer)
- [jerry_get_object_native_pointer](#jerry_set_object_native_pointer)
- [jerry_object_native_info_t](#jerry_object_native_info_t)
## jerry_foreach_object_property