Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 184 additions & 50 deletions docs/02.API-REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4756,97 +4756,197 @@ 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**

```c
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**

[doctest]: # ()

```c
typedef struct {
int foo;
bool bar;
} native_obj_t;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "jerryscript.h"

static void native_freecb (void *native_p)
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)
{
// 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 (has_p)
if (need_shape_info)
{
// The type_p pointer address itself is used to identify the type:
if (type_p == &native_obj_type_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)
{
// 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)
{
// 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.
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;
}
```

Expand Down Expand Up @@ -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

Expand Down
48 changes: 35 additions & 13 deletions jerry-core/api/jerry.c
Original file line number Diff line number Diff line change
Expand Up @@ -2591,9 +2591,8 @@ jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_i
{
if (!ecma_is_lexical_environment (iter_p))
{
native_pointer_p = ecma_get_native_pointer_value (iter_p);
native_pointer_p = ecma_get_native_pointer_value (iter_p, (void *) native_info_p);
if (native_pointer_p
&& ((const jerry_object_native_info_t *) native_pointer_p->info_p) == native_info_p
&& !foreach_p (ecma_make_object_value (iter_p), native_pointer_p->data_p, user_data_p))
{
return true;
Expand All @@ -2605,20 +2604,19 @@ jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_i
} /* jerry_objects_foreach_by_native_info */

/**
* Get native pointer and its type information, associated with specified object.
* Get native pointer and its type information, associated with the given native type info.
*
* Note:
* If native pointer is present, its type information is returned
* in out_native_pointer_p and out_native_info_p.
* If native pointer is present, its type information is returned in out_native_pointer_p
*
* @return true - if there is an associated pointer,
* false - otherwise
*/
bool
jerry_get_object_native_pointer (const jerry_value_t obj_val, /**< object to get native pointer from */
void **out_native_pointer_p, /**< [out] native pointer */
const jerry_object_native_info_t **out_native_info_p) /**< [out] the type info
* of the native pointer */
const jerry_object_native_info_t *native_info_p) /**< the type info
* of the native pointer */
{
jerry_assert_api_available ();

Expand All @@ -2628,7 +2626,7 @@ jerry_get_object_native_pointer (const jerry_value_t obj_val, /**< object to get
}

ecma_native_pointer_t *native_pointer_p;
native_pointer_p = ecma_get_native_pointer_value (ecma_get_object_from_value (obj_val));
native_pointer_p = ecma_get_native_pointer_value (ecma_get_object_from_value (obj_val), (void *) native_info_p);

if (native_pointer_p == NULL)
{
Expand All @@ -2640,11 +2638,6 @@ jerry_get_object_native_pointer (const jerry_value_t obj_val, /**< object to get
*out_native_pointer_p = native_pointer_p->data_p;
}

if (out_native_info_p != NULL)
{
*out_native_info_p = (const jerry_object_native_info_t *) native_pointer_p->info_p;
}

return true;
} /* jerry_get_object_native_pointer */

Expand Down Expand Up @@ -2676,6 +2669,35 @@ jerry_set_object_native_pointer (const jerry_value_t obj_val, /**< object to set
}
} /* jerry_set_object_native_pointer */

/**
* Delete the previously set native pointer by the native type info from the specified object.
*
* Note:
* If the specified object has no matching native pointer for the given native type info
* the function has no effect.
*
* Note:
* This operation cannot throw an exception.
*
* @return true - if the native pointer has been deleted succesfully
* false - otherwise
*/
bool
jerry_delete_object_native_pointer (const jerry_value_t obj_val, /**< object to delete native pointer from */
const jerry_object_native_info_t *native_info_p) /**< object's native type info */
{
jerry_assert_api_available ();

if (ecma_is_value_object (obj_val))
{
ecma_object_t *object_p = ecma_get_object_from_value (obj_val);

return ecma_delete_native_pointer_property (object_p, (void *) native_info_p);
}

return false;
} /* jerry_delete_object_native_pointer */

/**
* Applies the given function to the every property in the object.
*
Expand Down
Loading