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:
committed by
László Langó
parent
c818930cdc
commit
b3f4aa6816
+185
-51
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user