diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 01a958b97..94868d326 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -1457,6 +1457,86 @@ typedef struct - [jerry_source_info_enabled_fields_t](#jerry_source_info_enabled_fields_t) - [jerry_get_source_info](#jerry_get_source_info) +## jerry_arraybuffer_type_t + +**Summary** + +Enum that contains the JerryScript type of an array buffer: + + - JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER - the object is an array buffer object + - JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER - the object is a shared array buffer object + +*New in version [[NEXT_RELEASE]]*. + +**See also** + +- [jerry_arraybuffer_allocate_t](#jerry_arraybuffer_allocate_t) +- [jerry_arraybuffer_free_t](#jerry_arraybuffer_free_t) + +## jerry_arraybuffer_allocate_t + +**Summary** + +Callback for allocating the backing store of array buffer or shared array buffer objects. + +*Note*: +- The value referenced by `arraybuffer_user_p` is always NULL unless the buffer is created by + [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external) or + [jerry_create_shared_arraybuffer_external](#jerry_create_shared_arraybuffer_external). + The value referenced by `arraybuffer_user_p` can be changed, and the new value is passed to + [jerry_arraybuffer_free_t](#jerry_arraybuffer_free_t). + +**Prototype** + +```c +typedef uint8_t *(*jerry_arraybuffer_allocate_t) (jerry_arraybuffer_type_t buffer_type, uint32_t buffer_size, + void **arraybuffer_user_p, void *user_p); +``` + +- `buffer_type` - type of the array buffer object, see: [jerry_arraybuffer_type_t](#jerry_arraybuffer_type_t). +- `buffer_size` - size of the requested buffer. +- `arraybuffer_user_p` - [in/out] user pointer assigned to the array buffer or shared array buffer object. +- `user_p` - user pointer passed to [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) +- return value + - Pointer to the buffer, if the allocation is successful, NULL otherwise. + +*New in version [[NEXT_RELEASE]]*. + +**See also** + +- [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) + +## jerry_arraybuffer_free_t + +**Summary** + +Callback for freeing the backing store of array buffer or shared array buffer objects. + +*Note*: +- The value passed to `arraybuffer_user_p` is always NULL unless the buffer is created by + [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external) or + [jerry_create_shared_arraybuffer_external](#jerry_create_shared_arraybuffer_external), + or the value is modified by [jerry_arraybuffer_allocate_t](#jerry_arraybuffer_allocate_t). + +**Prototype** + +```c +typedef void (*jerry_arraybuffer_free_t) (jerry_arraybuffer_type_t buffer_type, uint8_t *buffer_p, + uint32_t buffer_size, void *arraybuffer_user_p, void *user_p); +``` + +- `buffer_type` - type of the array buffer object, see: [jerry_arraybuffer_type_t](#jerry_arraybuffer_type_t). +- `buffer_p` - pointer to the allocated buffer. +- `buffer_size` - size of the allocated buffer. +- `arraybuffer_user_p` - [in/out] user pointer assigned to the array buffer or shared array buffer object. +- `user_p` - user pointer passed to [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) + +*New in version [[NEXT_RELEASE]]*. + +**See also** + +- [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) + # General engine functions @@ -6893,9 +6973,11 @@ jerry_create_array (uint32_t size); Create a jerry_value_t representing an ArrayBuffer object. *Note*: - - This API depends on the es.next profile. - - Returned value must be freed with [jerry_release_value](#jerry_release_value) - when it is no longer needed. +- This API depends on a build option (`JERRY_BUILTIN_TYPEDARRAY`) and can be checked + in runtime with the `JERRY_FEATURE_TYPEDARRAY` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. **Prototype** @@ -6904,7 +6986,7 @@ jerry_value_t jerry_create_arraybuffer (jerry_length_t size); ``` - - `size` - size of the ArrayBuffer to create **in bytes** + - `size` - size of the backing store allocated for the array buffer **in bytes**. - return value - the new ArrayBuffer as a `jerry_value_t` *New in version 2.0*. @@ -6941,29 +7023,31 @@ After the object is not needed the GC will call the `free_cb` so the user can release the buffer which was provided. *Note*: - - This API depends on the es.next profile. - - Returned value must be freed with [jerry_release_value](#jerry_release_value) - when it is no longer needed. +- This API depends on a build option (`JERRY_BUILTIN_TYPEDARRAY`) and can be checked + in runtime with the `JERRY_FEATURE_TYPEDARRAY` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- If `buffer_p` is NULL, the buffer is allocated by the allocator callback passed to + [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) +- Returned value must be freed with [jerry_release_value](#jerry_release_value) + when it is no longer needed. **Prototype** ```c jerry_value_t jerry_create_arraybuffer_external (const jerry_length_t size - uint8_t *buffer_p, - jerry_value_free_callback_t free_cb); + uint8_t *buffer_p, void *arraybuffer_user_p); ``` -- `size` - size of the buffer to use **in bytes** (should not be 0) -- `buffer_p` - the buffer used for the Array Buffer object (should not be a null pointer) -- `free_cb` - the callback function called when the object is released +- `size` - size of the buffer **in bytes**. +- `buffer_p` - the backing store used by the array buffer object. +- `arraybuffer_user_p` - user pointer assigned to the array buffer object. - return value - - the new ArrayBuffer as a `jerry_value_t` - - if the `size` is zero or `buffer_p` is a null pointer this will return an empty ArrayBuffer. + - value of the newly construced array buffer object. *New in version 2.0*. -*Changed in version [[NEXT_RELEASE]]*: type of `free_cb` has been changed. +*Changed in version [[NEXT_RELEASE]]*: `free_cb` has been replaced by `arraybuffer_user_p`. **Example** @@ -6995,7 +7079,7 @@ jerry_create_arraybuffer_external (const jerry_length_t size Create a jerry_value_t representing a SharedArrayBuffer object. *Note*: -- This API depends on the es.next profile. +- This API depends on a build option (`JERRY_BUILTIN_SHAREDARRAYBUFFER`). - Returned value must be freed with [jerry_release_value](#jerry_release_value) when it is no longer needed. @@ -7006,7 +7090,7 @@ jerry_value_t jerry_create_shared_arraybuffer (jerry_length_t size); ``` -- `size` - size of the SharedArrayBuffer to create **in bytes** +- `size` - size of the backing store allocated for the shared array buffer **in bytes**. - return value - the new SharedArrayBuffer as a `jerry_value_t` *New in version [[NEXT_RELEASE]]*. @@ -7043,7 +7127,9 @@ After the object is not needed the GC will call the `free_cb` so the user can release the buffer which was provided. *Note*: -- This API depends on the es.next profile. +- This API depends on a build option (`JERRY_BUILTIN_SHAREDARRAYBUFFER`). +- If `buffer_p` is NULL, the buffer is allocated by the allocator callback passed to + [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) - Returned value must be freed with [jerry_release_value](#jerry_release_value) when it is no longer needed. @@ -7056,12 +7142,11 @@ jerry_create_shared_arraybuffer_external (const jerry_length_t size jerry_value_free_callback_t free_cb); ``` -- `size` - size of the buffer to use **in bytes** (should not be 0) -- `buffer_p` - the buffer used for the Shared Array Buffer object (should not be a null pointer) -- `free_cb` - the callback function called when the object is released +- `size` - size of the buffer **in bytes**. +- `buffer_p` - the backing store used by the shared array buffer object. +- `arraybuffer_user_p` - user pointer assigned to the shared array buffer object. - return value - - the new SharedArrayBuffer as a `jerry_value_t` - - if the `size` is zero or `buffer_p` is a null pointer this will return an empty SharedArrayBuffer. + - value of the newly construced shared array buffer object. *New in version [[NEXT_RELEASE]]*. @@ -12702,6 +12787,231 @@ jerry_detach_arraybuffer (const jerry_value_t value); - [jerry_is_arraybuffer_detachable](#jerry_is_arraybuffer_detachable) +## jerry_arraybuffer_has_buffer + +**Summary** + +Checks whether a buffer is currently allocated for an array buffer or typed array. + +*Notes*: +- This API depends on a build option (`JERRY_BUILTIN_TYPEDARRAY`) and can be checked + in runtime with the `JERRY_FEATURE_TYPEDARRAY` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). + +**Prototype** + +```c +bool +jerry_arraybuffer_has_buffer (const jerry_value_t value); +``` + +- `value` - array buffer or typed array value. +- return + - true, if a buffer is allocated for an array buffer or typed array + - false, otherwise + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_value_t array_buffer_value = jerry_create_arraybuffer (1024 * 1024); + + /* By default, the backing store of large array buffers + * is allocated when it is used the first time. */ + + if (!jerry_arraybuffer_has_buffer (array_buffer_value)) + { + /* Code enters here in this case. */ + } + + jerry_release_value (array_buffer_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external) +- [jerry_create_shared_arraybuffer_external](#jerry_create_shared_arraybuffer_external) +- [jerry_arraybuffer_set_compact_allocation_limit](#jerry_arraybuffer_set_compact_allocation_limit) +- [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) + +## jerry_arraybuffer_set_compact_allocation_limit + +**Summary** + +Array buffers which size is less or equal than the limit passed to this +function are allocated in a single memory block. The allocator callbacks set by +[jerry_arraybuffer_set_allocation_callbacks](#jerry_arraybuffer_set_allocation_callbacks) +are not called for these array buffers. + +*Notes*: +- This API depends on a build option (`JERRY_BUILTIN_TYPEDARRAY`) and can be checked + in runtime with the `JERRY_FEATURE_TYPEDARRAY` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- The default limit is 256 bytes. +- When an array buffer is allocated in a single memory block, its + backing store is not freed when the array buffer is detached. +- This limit does not affect shared array buffers, their backing store is always + allocated by the allocator callback. + +**Prototype** + +```c +void +jerry_arraybuffer_set_compact_allocation_limit (const jerry_length_t allocation_limit); +``` + +- `allocation_limit` - maximum size of compact allocation. + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include "jerryscript.h" + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_arraybuffer_set_compact_allocation_limit (1); + + jerry_value_t array_buffer_value = jerry_create_arraybuffer (1); + + if (jerry_arraybuffer_has_buffer (array_buffer_value)) + { + /* Code enters here because the backing store + * is allocated during buffer creation. */ + } + + jerry_release_value (array_buffer_value); + + array_buffer_value = jerry_create_arraybuffer (2); + + if (jerry_arraybuffer_has_buffer (array_buffer_value)) + { + /* Code does not enter here because the backing store + * is allocated when it is used the first time. */ + } + + jerry_release_value (array_buffer_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_arraybuffer_has_buffer](#jerry_arraybuffer_has_buffer) +- [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) + +## jerry_arraybuffer_set_allocator_callbacks + +**Summary** + +Set callbacks for allocating and freeing backing stores for array buffer objects. + +*Notes*: +- This API depends on a build option (`JERRY_BUILTIN_TYPEDARRAY`) and can be checked + in runtime with the `JERRY_FEATURE_TYPEDARRAY` feature enum value, + see: [jerry_is_feature_enabled](#jerry_is_feature_enabled). +- This function is recommended to be called after [jerry_init](#jerry_init) before + any array buffer is allocated. +- The callbacks can be NULL to use the default callbacks. The default `allocate_callback` + allocates memory using [jerry_heap_alloc](#jerry_heap_alloc) and the default + `free_callback` frees memory using [jerry_heap_free](#jerry_heap_free). + + +**Prototype** + +```c +void +jerry_arraybuffer_set_allocator_callbacks (jerry_arraybuffer_allocate_t allocate_callback, + jerry_arraybuffer_free_t free_callback, + void *user_p) +``` + +- `allocate_callback` - callback for allocating array buffer memory. +- `free_callback` - callback for freeing array buffer memory. +- `user_p` - user pointer passed to the callbacks. + +*New in version [[NEXT_RELEASE]]*. + +**Example** + +[doctest]: # (test="compile") + +```c +#include "jerryscript.h" + +static uint8_t global_buffer[64]; + +static void +array_buffer_free_cb (jerry_arraybuffer_type_t buffer_type, /**< type of the array buffer object */ + uint8_t *buffer_p, /**< pointer to the allocated buffer */ + uint32_t buffer_size, /**< size of the allocated buffer */ + void *arraybuffer_user_p, /**< user pointer assigned to the array buffer object */ + void *user_p) /**< user pointer passed to jerry_arraybuffer_set_allocation_callbacks */ +{ + (void) buffer_type; + (void) user_p; + + /* As for this example, only the free callback is redirected. This callback + * function does not free the memory if the arraybuffer_user_p is non-NULL. */ + + if (arraybuffer_user_p == NULL) + { + jerry_heap_free (buffer_p, buffer_size); + } +} /* array_buffer_free_cb */ + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + jerry_arraybuffer_set_allocator_callbacks (NULL, array_buffer_free_cb, NULL); + + /* The buffer of the array buffer object is allocated by the default + * allocator using jerry_heap_alloc and freed by array_buffer_free_cb. */ + + const jerry_char_t script[] = "var result = new uint32Array(1024); result[0] = 1; result"; + jerry_value_t array_buffer_value = jerry_eval (script, sizeof (script) - 1, JERRY_PARSE_NO_OPTS); + jerry_release_value (array_buffer_value); + + /* The buffer of the array buffer object has a non-NULL + * arraybuffer_user_p value, so it is not freed by array_buffer_free_cb. */ + + array_buffer_value = jerry_create_arraybuffer_external (sizeof (global_buffer), global_buffer, global_buffer); + jerry_release_value (array_buffer_value); + + jerry_cleanup (); + return 0; +} +``` + +**See also** + +- [jerry_arraybuffer_has_buffer](#jerry_arraybuffer_has_buffer) +- [jerry_arraybuffer_set_allocator_callbacks](#jerry_arraybuffer_set_allocator_callbacks) + ## jerry_get_dataview_buffer **Summary** diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index c8a8524df..737d9add0 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -5923,7 +5923,8 @@ jerry_value_is_arraybuffer (const jerry_value_t value) /**< value to check if it * @return value of the constructed ArrayBuffer object */ jerry_value_t -jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffer to create */ +jerry_create_arraybuffer (const jerry_length_t size) /**< size of the backing store allocated + * for the array buffer in bytes */ { jerry_assert_api_available (); @@ -5944,32 +5945,41 @@ jerry_create_arraybuffer (const jerry_length_t size) /**< size of the ArrayBuffe * * if the typed arrays are disabled this will return a TypeError. * * if the size is zero or buffer_p is a null pointer this will return an empty ArrayBuffer. * - * @return value of the construced ArrayBuffer object + * @return value of the newly construced array buffer object */ jerry_value_t -jerry_create_arraybuffer_external (const jerry_length_t size, /**< size of the buffer to used */ - uint8_t *buffer_p, /**< buffer to use as the ArrayBuffer's backing */ - jerry_value_free_callback_t free_cb) /**< buffer free callback */ +jerry_create_arraybuffer_external (const jerry_length_t size, /**< size of the buffer in bytes */ + uint8_t *buffer_p, /**< the backing store used by the array buffer object */ + void *arraybuffer_user_p) /**< user pointer assigned to the array buffer object */ { jerry_assert_api_available (); #if JERRY_BUILTIN_TYPEDARRAY - ecma_object_t *arraybuffer; + ecma_object_t *arraybuffer_p; - if (JERRY_UNLIKELY (size == 0 || buffer_p == NULL)) + if (JERRY_UNLIKELY (size == 0)) { - arraybuffer = ecma_arraybuffer_new_object (0); + arraybuffer_p = ecma_arraybuffer_new_object (0); } else { - arraybuffer = ecma_arraybuffer_new_object_external (size, buffer_p, free_cb); + arraybuffer_p = ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_ARRAY_BUFFER, size); + + ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) arraybuffer_p; + arraybuffer_pointer_p->arraybuffer_user_p = arraybuffer_user_p; + + if (buffer_p != NULL) + { + arraybuffer_pointer_p->extended_object.u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED; + arraybuffer_pointer_p->buffer_p = buffer_p; + } } - return jerry_return (ecma_make_object_value (arraybuffer)); + return jerry_return (ecma_make_object_value (arraybuffer_p)); #else /* !JERRY_BUILTIN_TYPEDARRAY */ JERRY_UNUSED (size); JERRY_UNUSED (buffer_p); - JERRY_UNUSED (free_cb); + JERRY_UNUSED (arraybuffer_user_p); return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_typed_array_not_supported_p))); #endif /* JERRY_BUILTIN_TYPEDARRAY */ } /* jerry_create_arraybuffer_external */ @@ -5999,7 +6009,8 @@ jerry_value_is_shared_arraybuffer (const jerry_value_t value) /**< value to chec * @return value of the constructed SharedArrayBuffer object */ jerry_value_t -jerry_create_shared_arraybuffer (const jerry_length_t size) /**< size of the SharedArrayBuffer to create */ +jerry_create_shared_arraybuffer (const jerry_length_t size) /**< size of the backing store allocated + * for the shared array buffer in bytes */ { jerry_assert_api_available (); @@ -6020,32 +6031,43 @@ jerry_create_shared_arraybuffer (const jerry_length_t size) /**< size of the Sha * * if the typed arrays are disabled this will return a TypeError. * * if the size is zero or buffer_p is a null pointer this will return an empty SharedArrayBuffer. * - * @return value of the construced SharedArrayBuffer object + * @return value of the newly construced shared array buffer object */ jerry_value_t -jerry_create_shared_arraybuffer_external (const jerry_length_t size, /**< size of the buffer to used */ - uint8_t *buffer_p, /**< buffer to use as the SharedArrayBuffer's backing */ - jerry_value_free_callback_t free_cb) /**< buffer free callback */ +jerry_create_shared_arraybuffer_external (const jerry_length_t size, /**< size of the buffer in bytes */ + uint8_t *buffer_p, /**< the backing store used by the + * shared array buffer object */ + void *arraybuffer_user_p) /**< user pointer assigned to the + * shared array buffer object */ { jerry_assert_api_available (); #if JERRY_BUILTIN_SHAREDARRAYBUFFER - ecma_object_t *shared_arraybuffer; + ecma_object_t *shared_arraybuffer_p; - if (JERRY_UNLIKELY (size == 0 || buffer_p == NULL)) + if (JERRY_UNLIKELY (size == 0)) { - shared_arraybuffer = ecma_shared_arraybuffer_new_object (0); + shared_arraybuffer_p = ecma_shared_arraybuffer_new_object (0); } else { - shared_arraybuffer = ecma_shared_arraybuffer_new_object_external (size, buffer_p, free_cb); + shared_arraybuffer_p = ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, size); + + ecma_arraybuffer_pointer_t *shared_arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) shared_arraybuffer_p; + shared_arraybuffer_pointer_p->arraybuffer_user_p = arraybuffer_user_p; + + if (buffer_p != NULL) + { + shared_arraybuffer_pointer_p->extended_object.u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED; + shared_arraybuffer_pointer_p->buffer_p = buffer_p; + } } - return jerry_return (ecma_make_object_value (shared_arraybuffer)); + return jerry_return (ecma_make_object_value (shared_arraybuffer_p)); #else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */ JERRY_UNUSED (size); JERRY_UNUSED (buffer_p); - JERRY_UNUSED (free_cb); + JERRY_UNUSED (arraybuffer_user_p); return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_shared_arraybuffer_not_supported_p))); #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ } /* jerry_create_shared_arraybuffer_external */ @@ -6073,6 +6095,13 @@ jerry_arraybuffer_write (const jerry_value_t value, /**< target ArrayBuffer or S } ecma_object_t *buffer_p = ecma_get_object_from_value (value); + + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (buffer_p)) + { + jerry_release_value (jcontext_take_exception ()); + return 0; + } + jerry_length_t length = ecma_arraybuffer_get_length (buffer_p); if (offset >= length) @@ -6122,6 +6151,13 @@ jerry_arraybuffer_read (const jerry_value_t value, /**< ArrayBuffer or SharedArr } ecma_object_t *buffer_p = ecma_get_object_from_value (value); + + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (buffer_p)) + { + jerry_release_value (jcontext_take_exception ()); + return 0; + } + jerry_length_t length = ecma_arraybuffer_get_length (buffer_p); if (offset >= length) @@ -6191,15 +6227,19 @@ jerry_get_arraybuffer_pointer (const jerry_value_t array_buffer) /**< Array Buff jerry_assert_api_available (); #if JERRY_BUILTIN_TYPEDARRAY - if (ecma_is_value_error_reference (array_buffer) - || !(ecma_is_arraybuffer (array_buffer) || ecma_is_shared_arraybuffer (array_buffer))) + if (!(ecma_is_arraybuffer (array_buffer) || ecma_is_shared_arraybuffer (array_buffer))) { return NULL; } ecma_object_t *buffer_p = ecma_get_object_from_value (array_buffer); - lit_utf8_byte_t *mem_buffer_p = ecma_arraybuffer_get_buffer (buffer_p); - return (uint8_t *const) mem_buffer_p; + + if (!(ECMA_ARRAYBUFFER_GET_FLAGS (buffer_p) & ECMA_ARRAYBUFFER_ALLOCATED)) + { + return NULL; + } + + return (uint8_t *) ecma_arraybuffer_get_buffer (buffer_p); #else /* !JERRY_BUILTIN_TYPEDARRAY */ JERRY_UNUSED (array_buffer); #endif /* JERRY_BUILTIN_TYPEDARRAY */ @@ -6222,7 +6262,7 @@ jerry_is_arraybuffer_detachable (const jerry_value_t value) /**< ArrayBuffer */ if (ecma_is_arraybuffer (value)) { ecma_object_t *buffer_p = ecma_get_object_from_value (value); - return ecma_arraybuffer_is_detached (buffer_p) ? ECMA_VALUE_FALSE : ECMA_VALUE_TRUE; + return ecma_make_boolean_value (!ecma_arraybuffer_is_detached (buffer_p)); } #else /* !JERRY_BUILTIN_TYPEDARRAY */ JERRY_UNUSED (value); @@ -6233,8 +6273,8 @@ jerry_is_arraybuffer_detachable (const jerry_value_t value) /**< ArrayBuffer */ /** * Detach the underlying data block from ArrayBuffer and set its bytelength to 0. * - * Note: If the ArrayBuffer has been created with `jerry_create_arraybuffer_external` - * the optional free callback is called on a successful detach operation + * Note: if the ArrayBuffer has a separate data buffer, the free callback set by + * jerry_arraybuffer_set_allocation_callbacks is called for this buffer * * @return null value - if success * value marked with error flag - otherwise @@ -6260,6 +6300,83 @@ jerry_detach_arraybuffer (const jerry_value_t value) /**< ArrayBuffer */ return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Expected an ArrayBuffer"))); } /* jerry_detach_arraybuffer */ +/** + * Checks whether a buffer is currently allocated for an array buffer or typed array. + * + * @return true, if a buffer is allocated for an array buffer or typed array + * false, otherwise + */ +bool +jerry_arraybuffer_has_buffer (const jerry_value_t value) /**< array buffer or typed array value */ +{ + jerry_assert_api_available (); + +#if JERRY_BUILTIN_TYPEDARRAY + if (!ecma_is_value_object (value)) + { + return false; + } + + ecma_object_t *object_p = ecma_get_object_from_value (value); + + if (ecma_object_is_typedarray (object_p)) + { + object_p = ecma_typedarray_get_arraybuffer (object_p); + } + else if (!(ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) + || ecma_object_is_shared_arraybuffer (object_p))) + { + return false; + } + + return (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_ALLOCATED) != 0; +#else /* !JERRY_BUILTIN_TYPEDARRAY */ + JERRY_UNUSED (value); + return false; +#endif /* JERRY_BUILTIN_TYPEDARRAY */ +} /* jerry_arraybuffer_has_buffer */ + +/** + * Array buffers which size is less or equal than the limit passed to this function are allocated in + * a single memory block. The allocator callbacks set by jerry_arraybuffer_set_allocation_callbacks + * are not called for these array buffers. The default limit is 256 bytes. + */ +void +jerry_arraybuffer_set_compact_allocation_limit (const jerry_length_t allocation_limit) /**< maximum size of + * compact allocation */ +{ + jerry_assert_api_available (); + +#if JERRY_BUILTIN_TYPEDARRAY + JERRY_CONTEXT (arraybuffer_compact_allocation_limit) = allocation_limit; +#else /* !JERRY_BUILTIN_TYPEDARRAY */ + JERRY_UNUSED (allocation_limit); +#endif /* JERRY_BUILTIN_TYPEDARRAY */ +} /* jerry_arraybuffer_set_compact_allocation_limit */ + +/** + * Set callbacks for allocating and freeing backing stores for array buffer objects. + */ +void +jerry_arraybuffer_set_allocator_callbacks (jerry_arraybuffer_allocate_t allocate_callback, /**< callback for allocating + * array buffer memory */ + jerry_arraybuffer_free_t free_callback, /**< callback for freeing + * array buffer memory */ + void *user_p) /**< user pointer passed to the callbacks */ +{ + jerry_assert_api_available (); + +#if JERRY_BUILTIN_TYPEDARRAY + JERRY_CONTEXT (arraybuffer_allocate_callback) = allocate_callback; + JERRY_CONTEXT (arraybuffer_free_callback) = free_callback; + JERRY_CONTEXT (arraybuffer_allocate_callback_user_p) = user_p; +#else /* !JERRY_BUILTIN_TYPEDARRAY */ + JERRY_UNUSED (allocate_callback); + JERRY_UNUSED (free_callback); + JERRY_UNUSED (user_p); +#endif /* JERRY_BUILTIN_TYPEDARRAY */ +} /* jerry_arraybuffer_set_allocator_callbacks */ + /** * DataView related functions */ diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index f3e3c0dd8..2bd2e7fca 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -19,6 +19,7 @@ #include "ecma-alloc.h" #include "ecma-array-object.h" +#include "ecma-arraybuffer-object.h" #include "ecma-builtin-handlers.h" #include "ecma-container-object.h" #include "ecma-function-object.h" @@ -1854,25 +1855,18 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ case ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER: #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ { - uint32_t arraybuffer_length = ext_object_p->u.cls.u3.length; - - if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p)) + if (!(ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) { - ext_object_size = sizeof (ecma_arraybuffer_external_info); - - /* Call external free callback if any. */ - ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p; - - if (array_p->free_cb != NULL) - { - array_p->free_cb (array_p->buffer_p); - } - } - else - { - ext_object_size += arraybuffer_length; + ext_object_size += ext_object_p->u.cls.u3.length; + break; } + ext_object_size = sizeof (ecma_arraybuffer_pointer_t); + + if (ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_ALLOCATED) + { + ecma_arraybuffer_release_buffer (object_p); + } break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ diff --git a/jerry-core/ecma/base/ecma-globals.h b/jerry-core/ecma/base/ecma-globals.h index b39c2adbe..7ce5a8758 100644 --- a/jerry-core/ecma/base/ecma-globals.h +++ b/jerry-core/ecma/base/ecma-globals.h @@ -2084,35 +2084,24 @@ typedef enum } ecma_typedarray_flag_t; /** - * ArrayBuffers flags. + * Array buffer flags. */ typedef enum { - ECMA_ARRAYBUFFER_INTERNAL_MEMORY = 0u, /* ArrayBuffer memory is handled internally. */ - ECMA_ARRAYBUFFER_EXTERNAL_MEMORY = (1u << 0), /* ArrayBuffer created via jerry_create_arraybuffer_external. */ - ECMA_ARRAYBUFFER_DETACHED = (1u << 1), /* ArrayBuffer has been detached */ + ECMA_ARRAYBUFFER_HAS_POINTER = (1u << 0), /* ArrayBuffer has a buffer pointer. */ + ECMA_ARRAYBUFFER_ALLOCATED = (1u << 1), /* ArrayBuffer memory is allocated */ + ECMA_ARRAYBUFFER_DETACHED = (1u << 2), /* ArrayBuffer has been detached */ } ecma_arraybuffer_flag_t; /** - * Check whether the ArrayBuffer has external underlying buffer - */ -#define ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY(object_p) \ - ((((ecma_extended_object_t *) object_p)->u.cls.u1.array_buffer_flags & ECMA_ARRAYBUFFER_EXTERNAL_MEMORY) != 0) - -/** - * Struct to store information for ArrayBuffers with external memory. - * - * The following elements are stored in Jerry memory. - * - * buffer_p - pointer to the external memory. - * free_cb - pointer to a callback function which is called when the ArrayBuffer is freed. + * Structure for array buffers with a backing store pointer. */ typedef struct { ecma_extended_object_t extended_object; /**< extended object part */ - void *buffer_p; /**< external buffer pointer */ - jerry_value_free_callback_t free_cb; /**< the free callback for the above buffer pointer */ -} ecma_arraybuffer_external_info; + void *buffer_p; /**< pointer to the backing store of the array buffer object */ + void *arraybuffer_user_p; /**< user pointer passed to the free callback */ +} ecma_arraybuffer_pointer_t; /** * Some internal properties of TypedArray object. @@ -2132,10 +2121,6 @@ typedef struct typedef struct { ecma_object_t *array_buffer_p; /**< pointer to the typedArray's [[ViewedArrayBuffer]] internal slot */ - lit_utf8_byte_t *buffer_p; /**< pointer to the underlying raw data buffer. - * Note: - * - This address is increased by the [ByteOffset]] internal property. - * - This address must be used during indexed read/write operation. */ ecma_typedarray_type_t id; /**< [[TypedArrayName]] internal slot */ uint32_t length; /**< [[ByteLength]] internal slot */ uint32_t offset; /**< [[ByteOffset]] internal slot. */ diff --git a/jerry-core/ecma/base/ecma-init-finalize.c b/jerry-core/ecma/base/ecma-init-finalize.c index 67d637094..5ae409bfb 100644 --- a/jerry-core/ecma/base/ecma-init-finalize.c +++ b/jerry-core/ecma/base/ecma-init-finalize.c @@ -63,6 +63,10 @@ ecma_init (void) #if JERRY_ESNEXT JERRY_CONTEXT (current_new_target_p) = NULL; #endif /* JERRY_ESNEXT */ + +#if JERRY_BUILTIN_TYPEDARRAY + JERRY_CONTEXT (arraybuffer_compact_allocation_limit) = 256; +#endif /* JERRY_BUILTIN_TYPEDARRAY */ } /* ecma_init */ /** diff --git a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c index 17ab6b10b..b2e6d40dc 100644 --- a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c +++ b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray-prototype.c @@ -130,15 +130,15 @@ ecma_builtin_typedarray_prototype_exec_routine (ecma_value_t this_arg, /**< this JERRY_ASSERT (mode < TYPEDARRAY_ROUTINE__COUNT); ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); - ecma_object_t *func_object_p = ecma_get_object_from_value (cb_func_val); + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint32_t byte_pos = 0; ecma_value_t ret_value = ECMA_VALUE_EMPTY; for (uint32_t index = 0; index < info_p->length && ecma_is_value_empty (ret_value); index++) { ecma_value_t current_index = ecma_make_uint32_value (index); - ecma_value_t element = typedarray_getter_cb (info_p->buffer_p + byte_pos); + ecma_value_t element = typedarray_getter_cb (buffer_p + byte_pos); ecma_value_t call_args[] = { element, current_index, this_arg }; @@ -221,17 +221,31 @@ ecma_builtin_typedarray_prototype_map (ecma_value_t this_arg, /**< this object * } ecma_object_t *target_obj_p = ecma_get_object_from_value (new_typedarray); + uint8_t *src_buffer_p = ecma_typedarray_get_buffer (src_info_p); + + if (JERRY_UNLIKELY (src_buffer_p == NULL)) + { + ecma_deref_object (target_obj_p); + return ECMA_VALUE_ERROR; + } + ecma_typedarray_info_t target_info = ecma_typedarray_get_info (target_obj_p); + uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); + + if (JERRY_UNLIKELY (target_buffer_p == NULL)) + { + ecma_deref_object (target_obj_p); + return ECMA_VALUE_ERROR; + } ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (src_info_p->id); ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); - uint32_t src_byte_pos = 0; - for (uint32_t index = 0; index < src_info_p->length; index++) { ecma_value_t current_index = ecma_make_uint32_value (index); - ecma_value_t element = src_typedarray_getter_cb (src_info_p->buffer_p + src_byte_pos); + ecma_value_t element = src_typedarray_getter_cb (src_buffer_p); + src_buffer_p += src_info_p->element_size; ecma_value_t call_args[] = { element, current_index, this_arg }; ecma_value_t mapped_value = ecma_op_function_call (func_object_p, cb_this_arg, call_args, 3); @@ -252,8 +266,8 @@ ecma_builtin_typedarray_prototype_map (ecma_value_t this_arg, /**< this object * return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); } - uint32_t target_byte_pos = index << target_info.shift; - ecma_value_t set_element = target_typedarray_setter_cb (target_info.buffer_p + target_byte_pos, mapped_value); + ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, mapped_value); + target_buffer_p += target_info.element_size; ecma_free_value (mapped_value); if (ECMA_IS_VALUE_ERROR (set_element)) @@ -261,8 +275,6 @@ ecma_builtin_typedarray_prototype_map (ecma_value_t this_arg, /**< this object * ecma_free_value (new_typedarray); return set_element; } - - src_byte_pos += src_info_p->element_size; } return new_typedarray; @@ -299,11 +311,12 @@ ecma_builtin_typedarray_prototype_reduce_with_direction (ecma_value_t this_arg, ecma_value_t accumulator = ECMA_VALUE_UNDEFINED; uint32_t index = is_right ? (info_p->length - 1) : 0; + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; if (ecma_is_value_undefined (arguments_list_p[1])) { byte_pos = index << info_p->shift; - accumulator = getter_cb (info_p->buffer_p + byte_pos); + accumulator = getter_cb (buffer_p + byte_pos); if (is_right) { @@ -335,7 +348,7 @@ ecma_builtin_typedarray_prototype_reduce_with_direction (ecma_value_t this_arg, { ecma_value_t current_index = ecma_make_uint32_value (index); byte_pos = index << info_p->shift; - ecma_value_t get_value = getter_cb (info_p->buffer_p + byte_pos); + ecma_value_t get_value = getter_cb (buffer_p + byte_pos); ecma_value_t call_args[] = { accumulator, get_value, current_index, this_arg }; @@ -413,12 +426,12 @@ ecma_builtin_typedarray_prototype_filter (ecma_value_t this_arg, /**< this objec } ecma_collection_t *collected_p = ecma_new_collection (); - uint32_t byte_pos = 0; + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; for (uint32_t index = 0; index < info_p->length; index++) { ecma_value_t current_index = ecma_make_uint32_value (index); - ecma_value_t get_value = getter_cb (info_p->buffer_p + byte_pos); + ecma_value_t get_value = getter_cb (buffer_p); JERRY_ASSERT (ecma_is_value_number (get_value) || ecma_is_value_bigint (get_value)); @@ -451,7 +464,7 @@ ecma_builtin_typedarray_prototype_filter (ecma_value_t this_arg, /**< this objec ecma_fast_free_value (get_value); } - byte_pos += info_p->element_size; + buffer_p += info_p->element_size; ecma_fast_free_value (call_value); } @@ -461,24 +474,35 @@ ecma_builtin_typedarray_prototype_filter (ecma_value_t this_arg, /**< this objec if (!ECMA_IS_VALUE_ERROR (ret_value)) { - ecma_object_t *obj_p = ecma_get_object_from_value (ret_value); - ecma_typedarray_info_t target_info = ecma_typedarray_get_info (obj_p); + ecma_object_t *new_typedarray_p = ecma_get_object_from_value (ret_value); + ecma_typedarray_info_t target_info = ecma_typedarray_get_info (new_typedarray_p); JERRY_ASSERT (target_info.offset == 0); + uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); + + if (JERRY_UNLIKELY (target_buffer_p == NULL)) + { + ecma_deref_object (new_typedarray_p); + ret_value = ECMA_VALUE_ERROR; + goto cleanup; + } + ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); - uint32_t target_byte_index = 0; + for (uint32_t idx = 0; idx < collected_p->item_count; idx++) { - ecma_value_t set_element = target_typedarray_setter_cb (target_info.buffer_p + target_byte_index, + ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, collected_p->buffer_p[idx]); if (ECMA_IS_VALUE_ERROR (set_element)) { + ecma_deref_object (new_typedarray_p); + ret_value = ECMA_VALUE_ERROR; goto cleanup; } - target_byte_index += target_info.element_size; + target_buffer_p += target_info.element_size; } } @@ -501,16 +525,17 @@ static ecma_value_t ecma_builtin_typedarray_prototype_reverse (ecma_value_t this_arg, /**< this argument */ ecma_typedarray_info_t *info_p) /**< object info */ { + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; uint32_t middle = (info_p->length / 2) << info_p->shift; uint32_t buffer_last = (info_p->length << info_p->shift) - info_p->element_size; for (uint32_t lower = 0; lower < middle; lower += info_p->element_size) { uint32_t upper = buffer_last - lower; - lit_utf8_byte_t *lower_p = info_p->buffer_p + lower; - lit_utf8_byte_t *upper_p = info_p->buffer_p + upper; + uint8_t *lower_p = buffer_p + lower; + uint8_t *upper_p = buffer_p + upper; - lit_utf8_byte_t tmp[8]; + uint8_t tmp[8]; memcpy (&tmp[0], lower_p, info_p->element_size); memcpy (lower_p, upper_p, info_p->element_size); memcpy (upper_p, &tmp[0], info_p->element_size); @@ -546,22 +571,22 @@ ecma_op_typedarray_set_with_typedarray (ecma_value_t this_arg, /**< this argumen } ecma_object_t *target_typedarray_p = ecma_get_object_from_value (this_arg); - ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (target_typedarray_p); - if (ecma_arraybuffer_is_detached (arraybuffer_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); - } - ecma_typedarray_info_t target_info = ecma_typedarray_get_info (target_typedarray_p); + uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); + + if (JERRY_UNLIKELY (target_buffer_p == NULL)) + { + return ECMA_VALUE_ERROR; + } ecma_object_t *src_typedarray_p = ecma_get_object_from_value (arr_val); - ecma_object_t *src_arraybuffer_p = ecma_typedarray_get_arraybuffer (src_typedarray_p); - if (ecma_arraybuffer_is_detached (src_arraybuffer_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); - } - ecma_typedarray_info_t src_info = ecma_typedarray_get_info (src_typedarray_p); + uint8_t *src_buffer_p = ecma_typedarray_get_buffer (&src_info); + + if (JERRY_UNLIKELY (src_buffer_p == NULL)) + { + return ECMA_VALUE_ERROR; + } uint32_t target_offset_uint32 = ecma_number_to_uint32 (target_offset_num); @@ -577,26 +602,25 @@ ecma_op_typedarray_set_with_typedarray (ecma_value_t this_arg, /**< this argumen } /* 26. targetByteIndex */ - uint32_t target_byte_index = target_offset_uint32 * target_info.element_size; + target_buffer_p += target_offset_uint32 << target_info.shift; /* 27. limit */ - uint32_t limit = target_byte_index + target_info.element_size * src_info.length; + uint32_t limit = src_info.length << target_info.shift; if (src_info.id == target_info.id) { - memmove (target_info.buffer_p + target_byte_index, src_info.buffer_p, - target_info.element_size * src_info.length); + memmove (target_buffer_p, src_buffer_p, limit); } else { + uint8_t *target_limit_p = target_buffer_p + limit; ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (src_info.id); ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); - uint32_t src_byte_index = 0; - while (target_byte_index < limit) + while (target_buffer_p < target_limit_p) { - ecma_value_t element = src_typedarray_getter_cb (src_info.buffer_p + src_byte_index); - ecma_value_t set_element = target_typedarray_setter_cb (target_info.buffer_p + target_byte_index, element); + ecma_value_t element = src_typedarray_getter_cb (src_buffer_p); + ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, element); ecma_free_value (element); if (ECMA_IS_VALUE_ERROR (set_element)) @@ -604,8 +628,8 @@ ecma_op_typedarray_set_with_typedarray (ecma_value_t this_arg, /**< this argumen return set_element; } - src_byte_index += src_info.element_size; - target_byte_index += target_info.element_size; + src_buffer_p += src_info.element_size; + target_buffer_p += target_info.element_size; } } @@ -649,13 +673,13 @@ ecma_builtin_typedarray_prototype_set (ecma_value_t this_arg, /**< this argument /* 11. ~ 15. */ ecma_object_t *typedarray_p = ecma_get_object_from_value (this_arg); - ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); - if (ecma_arraybuffer_is_detached (arraybuffer_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); - } - ecma_typedarray_info_t target_info = ecma_typedarray_get_info (typedarray_p); + uint8_t *target_buffer_p = ecma_typedarray_get_buffer (&target_info); + + if (JERRY_UNLIKELY (target_buffer_p == NULL)) + { + return ECMA_VALUE_ERROR; + } /* 16.~ 17. */ ecma_value_t source_obj = ecma_op_to_object (arr_val); @@ -686,10 +710,10 @@ ecma_builtin_typedarray_prototype_set (ecma_value_t this_arg, /**< this argument uint32_t source_length_uint32 = (uint32_t) source_length; /* 21.~ 25. */ - uint32_t target_byte_index = target_offset_uint32 * target_info.element_size; - uint32_t k = 0; + target_buffer_p += target_offset_uint32 << target_info.shift; ecma_typedarray_setter_fn_t target_typedarray_setter_cb = ecma_get_typedarray_setter_fn (target_info.id); + uint32_t k = 0; while (k < source_length_uint32) { @@ -731,14 +755,14 @@ ecma_builtin_typedarray_prototype_set (ecma_value_t this_arg, /**< this argument ecma_free_value (elem); - if (ecma_arraybuffer_is_detached (arraybuffer_p)) + if (ecma_arraybuffer_is_detached (target_info.array_buffer_p)) { ecma_deref_object (source_obj_p); ecma_free_value (value_to_set); return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); } - ecma_value_t set_element = target_typedarray_setter_cb (target_info.buffer_p + target_byte_index, value_to_set); + ecma_value_t set_element = target_typedarray_setter_cb (target_buffer_p, value_to_set); ecma_free_value (value_to_set); @@ -749,7 +773,7 @@ ecma_builtin_typedarray_prototype_set (ecma_value_t this_arg, /**< this argument } k++; - target_byte_index += target_info.element_size; + target_buffer_p += target_info.element_size; } ecma_deref_object (source_obj_p); @@ -929,7 +953,7 @@ ecma_builtin_typedarray_prototype_subarray (ecma_value_t this_arg, /**< this obj } /* 17. beginByteOffset */ - uint32_t begin_byte_offset = info_p->offset + begin_index_uint32 * info_p->element_size; + uint32_t begin_byte_offset = info_p->offset + (begin_index_uint32 << info_p->shift); ecma_value_t arguments_p[3] = { @@ -1019,18 +1043,21 @@ ecma_builtin_typedarray_prototype_fill (ecma_value_t this_arg, /**< this object subarray_length = end_index_uint32 - begin_index_uint32; } - ecma_typedarray_setter_fn_t typedarray_setter_cb = ecma_get_typedarray_setter_fn (info_p->id); - uint32_t byte_index = begin_index_uint32 * info_p->element_size; - uint32_t limit = byte_index + subarray_length * info_p->element_size; + uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); - if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) + if (JERRY_UNLIKELY (buffer_p == NULL)) { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } - while (byte_index < limit) + buffer_p += begin_index_uint32 << info_p->shift; + + uint8_t *limit_p = buffer_p + (subarray_length << info_p->shift); + ecma_typedarray_setter_fn_t typedarray_setter_cb = ecma_get_typedarray_setter_fn (info_p->id); + + while (buffer_p < limit_p) { - ecma_value_t set_element = typedarray_setter_cb (info_p->buffer_p + byte_index, value_to_set); + ecma_value_t set_element = typedarray_setter_cb (buffer_p, value_to_set); if (ECMA_IS_VALUE_ERROR (set_element)) { @@ -1038,7 +1065,7 @@ ecma_builtin_typedarray_prototype_fill (ecma_value_t this_arg, /**< this object return set_element; } - byte_index += info_p->element_size; + buffer_p += info_p->element_size; } ecma_free_value (value_to_set); @@ -1171,17 +1198,19 @@ ecma_builtin_typedarray_prototype_sort (ecma_value_t this_arg, /**< this argumen ecma_value_t ret_value = ECMA_VALUE_EMPTY; JMEM_DEFINE_LOCAL_ARRAY (values_buffer, info_p->length, ecma_value_t); - uint32_t byte_index = 0, buffer_index = 0; - uint32_t limit = info_p->length * info_p->element_size; + uint32_t buffer_index = 0; ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; + uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); + /* Copy unsorted array into a native c array. */ - while (byte_index < limit) + while (buffer_p < limit_p) { JERRY_ASSERT (buffer_index < info_p->length); - ecma_value_t element_value = typedarray_getter_cb (info_p->buffer_p + byte_index); + ecma_value_t element_value = typedarray_getter_cb (buffer_p); values_buffer[buffer_index++] = element_value; - byte_index += info_p->element_size; + buffer_p += info_p->element_size; } JERRY_ASSERT (buffer_index == info_p->length); @@ -1209,15 +1238,15 @@ ecma_builtin_typedarray_prototype_sort (ecma_value_t this_arg, /**< this argumen ecma_typedarray_setter_fn_t typedarray_setter_cb = ecma_get_typedarray_setter_fn (info_p->id); - byte_index = 0; + buffer_p = limit_p - (info_p->length << info_p->shift); buffer_index = 0; - limit = info_p->length * info_p->element_size; + /* Put sorted values from the native array back into the typedarray buffer. */ - while (byte_index < limit) + while (buffer_p < limit_p) { JERRY_ASSERT (buffer_index < info_p->length); ecma_value_t element_value = values_buffer[buffer_index++]; - ecma_value_t set_element = typedarray_setter_cb (info_p->buffer_p + byte_index, element_value); + ecma_value_t set_element = typedarray_setter_cb (buffer_p, element_value); if (ECMA_IS_VALUE_ERROR (set_element)) { @@ -1225,7 +1254,7 @@ ecma_builtin_typedarray_prototype_sort (ecma_value_t this_arg, /**< this argumen goto free_values; } - byte_index += info_p->element_size; + buffer_p += info_p->element_size; } JERRY_ASSERT (buffer_index == info_p->length); @@ -1261,16 +1290,16 @@ ecma_builtin_typedarray_prototype_find_helper (ecma_value_t this_arg, /**< this { JERRY_ASSERT (ecma_is_value_object (predicate)); ecma_object_t *func_object_p = ecma_get_object_from_value (predicate); - - uint32_t buffer_index = 0; - uint32_t limit = info_p->length * info_p->element_size; - + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; + uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); + uint32_t buffer_index = 0; - for (uint32_t byte_index = 0; byte_index < limit; byte_index += info_p->element_size) + while (buffer_p < limit_p) { JERRY_ASSERT (buffer_index < info_p->length); - ecma_value_t element_value = typedarray_getter_cb (info_p->buffer_p + byte_index); + ecma_value_t element_value = typedarray_getter_cb (buffer_p); + buffer_p += info_p->element_size; ecma_value_t call_args[] = { element_value, ecma_make_uint32_value (buffer_index), this_arg }; ecma_value_t call_value = ecma_op_function_call (func_object_p, predicate_this_arg, call_args, 3); @@ -1355,7 +1384,6 @@ ecma_builtin_typedarray_prototype_index_of (ecma_typedarray_info_t *info_p, /**< bool is_bigint = false; #endif /* JERRY_BUILTIN_BIGINT */ - uint32_t limit = info_p->length * info_p->element_size; uint32_t from_index; /* 5. */ @@ -1377,25 +1405,34 @@ ecma_builtin_typedarray_prototype_index_of (ecma_typedarray_info_t *info_p, /**< { return ECMA_VALUE_ERROR; } - } + uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); + + if (JERRY_UNLIKELY (buffer_p == NULL)) + { + return ECMA_VALUE_ERROR; + } + + uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); + buffer_p += from_index << info_p->shift; + /* 11. */ - for (uint32_t position = from_index * info_p->element_size; - position < limit; - position += info_p->element_size) + while (buffer_p < limit_p) { - ecma_value_t element = getter_cb (info_p->buffer_p + position); + ecma_value_t element = getter_cb (buffer_p); if (ecma_op_same_value_zero (args[0], element, true)) { ecma_free_value (element); - return ecma_make_number_value ((ecma_number_t) position / info_p->element_size); + return ecma_make_number_value (from_index); } ecma_free_value (element); + buffer_p += info_p->element_size; + from_index++; } /* 12. */ @@ -1461,17 +1498,18 @@ ecma_builtin_typedarray_prototype_last_index_of (ecma_typedarray_info_t *info_p, } ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); - uint8_t *current_element_p = info_p->buffer_p + from_index * info_p->element_size; + uint8_t *buffer_p = ecma_arraybuffer_get_buffer (info_p->array_buffer_p) + info_p->offset; + uint8_t *current_element_p = buffer_p + (from_index << info_p->shift); /* 10. */ - while (current_element_p >= info_p->buffer_p) + while (current_element_p >= buffer_p) { - ecma_value_t element = getter_cb (info_p->buffer_p + from_index * info_p->element_size); + ecma_value_t element = getter_cb (current_element_p); if (ecma_op_same_value_zero (args[0], element, true)) { ecma_free_value (element); - return ecma_make_number_value ((ecma_number_t) from_index * info_p->element_size / info_p->element_size); + return ecma_make_number_value ((ecma_number_t) from_index); } ecma_free_value (element); @@ -1532,26 +1570,26 @@ ecma_builtin_typedarray_prototype_copy_within (ecma_value_t this_arg, /**< this } } - if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); - } - if (relative_target >= info_p->length || relative_start >= relative_end || relative_end == 0) { return ecma_copy_value (this_arg); } - else - { - uint32_t distance = relative_end - relative_start; - uint32_t offset = info_p->length - relative_target; - uint32_t count = JERRY_MIN (distance, offset); - memmove (info_p->buffer_p + (relative_target * info_p->element_size), - info_p->buffer_p + (relative_start * info_p->element_size), - (size_t) (count * info_p->element_size)); + uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); + + if (JERRY_UNLIKELY (buffer_p == NULL)) + { + return ECMA_VALUE_ERROR; } + uint32_t distance = relative_end - relative_start; + uint32_t offset = info_p->length - relative_target; + uint32_t count = JERRY_MIN (distance, offset); + + memmove (buffer_p + (relative_target << info_p->shift), + buffer_p + (relative_start << info_p->shift), + (size_t) (count << info_p->shift)); + return ecma_copy_value (this_arg); } /* ecma_builtin_typedarray_prototype_copy_within */ @@ -1606,23 +1644,31 @@ ecma_builtin_typedarray_prototype_slice (ecma_value_t this_arg, /**< this argume } ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); + uint8_t *src_buffer_p = ecma_typedarray_get_buffer (info_p); - if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) + if (JERRY_UNLIKELY (src_buffer_p == NULL)) { ecma_deref_object (new_typedarray_p); - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } ecma_typedarray_info_t new_typedarray_info = ecma_typedarray_get_info (new_typedarray_p); + uint8_t *dst_buffer_p = ecma_typedarray_get_buffer (&new_typedarray_info); + + if (JERRY_UNLIKELY (dst_buffer_p == NULL)) + { + ecma_deref_object (new_typedarray_p); + return ECMA_VALUE_ERROR; + } + + JERRY_ASSERT (new_typedarray_info.offset == 0); + + src_buffer_p += relative_start << info_p->shift; if (info_p->id == new_typedarray_info.id) { // 22.2.3.23. Step 22. h-i. - uint32_t src_byte_index = (relative_start * info_p->element_size); - - memcpy (new_typedarray_info.buffer_p, - info_p->buffer_p + src_byte_index, - count * info_p->element_size); + memcpy (dst_buffer_p, src_buffer_p, count << info_p->shift); } else { @@ -1630,13 +1676,10 @@ ecma_builtin_typedarray_prototype_slice (ecma_value_t this_arg, /**< this argume ecma_typedarray_getter_fn_t src_typedarray_getter_cb = ecma_get_typedarray_getter_fn (info_p->id); ecma_typedarray_setter_fn_t new_typedarray_setter_cb = ecma_get_typedarray_setter_fn (new_typedarray_info.id); - uint32_t src_byte_index = (relative_start * info_p->element_size); - uint32_t dst_byte_index = 0; - for (uint32_t idx = 0; idx < count; idx++) { - ecma_value_t element = src_typedarray_getter_cb (info_p->buffer_p + src_byte_index); - ecma_value_t set_element = new_typedarray_setter_cb (new_typedarray_info.buffer_p + dst_byte_index, element); + ecma_value_t element = src_typedarray_getter_cb (src_buffer_p); + ecma_value_t set_element = new_typedarray_setter_cb (dst_buffer_p, element); ecma_free_value (element); if (ECMA_IS_VALUE_ERROR (set_element)) @@ -1645,8 +1688,8 @@ ecma_builtin_typedarray_prototype_slice (ecma_value_t this_arg, /**< this argume return set_element; } - src_byte_index += info_p->element_size; - dst_byte_index += new_typedarray_info.element_size; + src_buffer_p += info_p->element_size; + dst_buffer_p += new_typedarray_info.element_size; } } @@ -1747,8 +1790,6 @@ ecma_builtin_typedarray_prototype_includes (ecma_typedarray_info_t *info_p, /**< const ecma_value_t args[], /**< arguments list */ uint32_t args_number) /**< number of arguments */ { - uint32_t limit = info_p->length * info_p->element_size; - #if JERRY_BUILTIN_BIGINT bool is_bigint = ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info_p->id); #else /* !JERRRY_BUILTIN_BIGINT */ @@ -1772,18 +1813,21 @@ ecma_builtin_typedarray_prototype_includes (ecma_typedarray_info_t *info_p, /**< } } - ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); + uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); - uint32_t search_pos = (uint32_t) from_index * info_p->element_size; - - if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) + if (JERRY_UNLIKELY (buffer_p == NULL)) { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } - while (search_pos < limit) + ecma_typedarray_getter_fn_t getter_cb = ecma_get_typedarray_getter_fn (info_p->id); + uint8_t *limit_p = buffer_p + (info_p->length << info_p->shift); + + buffer_p += from_index << info_p->shift; + + while (buffer_p < limit_p) { - ecma_value_t element = getter_cb (info_p->buffer_p + search_pos); + ecma_value_t element = getter_cb (buffer_p); if (ecma_op_same_value_zero (args[0], element, false)) { @@ -1792,7 +1836,7 @@ ecma_builtin_typedarray_prototype_includes (ecma_typedarray_info_t *info_p, /**< } ecma_free_value (element); - search_pos += info_p->element_size; + buffer_p += info_p->element_size; } return ECMA_VALUE_FALSE; @@ -1828,10 +1872,10 @@ ecma_builtin_typedarray_prototype_dispatch_routine (uint8_t builtin_routine_id, { info = ecma_typedarray_get_info (typedarray_p); - if (ecma_arraybuffer_is_detached (info.array_buffer_p) - && builtin_routine_id != ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SUBARRAY) + if (builtin_routine_id != ECMA_TYPEDARRAY_PROTOTYPE_ROUTINE_SUBARRAY + && ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (info.array_buffer_p)) { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } } diff --git a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray.c b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray.c index c961618e9..2631f4dbf 100644 --- a/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray.c +++ b/jerry-core/ecma/builtin-objects/typedarray/ecma-builtin-typedarray.c @@ -131,10 +131,17 @@ ecma_builtin_typedarray_of (ecma_value_t this_arg, /**< 'this' argument */ ecma_object_t *ret_obj_p = ecma_get_object_from_value (ret_val); ecma_typedarray_info_t info = ecma_typedarray_get_info (ret_obj_p); ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); + lit_utf8_byte_t *buffer_p = ecma_typedarray_get_buffer (&info); + + if (JERRY_UNLIKELY (buffer_p == NULL)) + { + ecma_deref_object (ret_obj_p); + return ECMA_VALUE_ERROR; + } while (k < arguments_list_len) { - ecma_value_t set_element = setter_cb (info.buffer_p, arguments_list_p[k]); + ecma_value_t set_element = setter_cb (buffer_p, arguments_list_p[k]); if (ECMA_IS_VALUE_ERROR (set_element)) { @@ -143,7 +150,7 @@ ecma_builtin_typedarray_of (ecma_value_t this_arg, /**< 'this' argument */ } k++; - info.buffer_p += info.element_size; + buffer_p += info.element_size; } return ret_val; diff --git a/jerry-core/ecma/operations/ecma-arraybuffer-object.c b/jerry-core/ecma/operations/ecma-arraybuffer-object.c index cf8ae006b..99749921e 100644 --- a/jerry-core/ecma/operations/ecma-arraybuffer-object.c +++ b/jerry-core/ecma/operations/ecma-arraybuffer-object.c @@ -13,19 +13,18 @@ * limitations under the License. */ -#include "ecma-builtin-helpers.h" #include "ecma-arraybuffer-object.h" -#include "ecma-shared-arraybuffer-object.h" -#include "ecma-typedarray-object.h" -#include "ecma-objects.h" #include "ecma-builtins.h" +#include "ecma-builtin-helpers.h" #include "ecma-exceptions.h" +#include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" -#include "jmem.h" +#include "ecma-objects.h" +#include "ecma-shared-arraybuffer-object.h" +#include "ecma-typedarray-object.h" #include "jcontext.h" -#include "ecma-function-object.h" #if JERRY_BUILTIN_TYPEDARRAY @@ -37,64 +36,187 @@ */ /** - * Helper function: create arraybuffer object based on the array length + * Creating ArrayBuffer objects with a buffer after the arraybuffer header * - * The struct of arraybuffer object: - * ecma_object_t - * extend_part - * data buffer - * - * @return ecma_object_t * + * @return new ArrayBuffer object */ ecma_object_t * -ecma_arraybuffer_new_object (uint32_t length) /**< length of the arraybuffer */ +ecma_arraybuffer_create_object (uint8_t type, /**< type of the arraybuffer */ + uint32_t length) /**< length of the arraybuffer */ { - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE); - ecma_object_t *object_p = ecma_create_object (prototype_obj_p, + ecma_builtin_id_t prototype_id; + +#if JERRY_BUILTIN_SHAREDARRAYBUFFER + JERRY_ASSERT (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER + || type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER); + + prototype_id = (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER ? ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE + : ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE); +#else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */ + JERRY_ASSERT (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER); + + prototype_id = ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE; +#endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ + + ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (prototype_id), sizeof (ecma_extended_object_t) + length, ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_ARRAY_BUFFER; - ext_object_p->u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_INTERNAL_MEMORY; + ext_object_p->u.cls.type = type; + ext_object_p->u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_ALLOCATED; ext_object_p->u.cls.u3.length = length; - lit_utf8_byte_t *buf = (lit_utf8_byte_t *) (ext_object_p + 1); - memset (buf, 0, length); + memset ((uint8_t *) (ext_object_p + 1), 0, length); + return object_p; +} /* ecma_arraybuffer_create_object */ + +/** + * Creating ArrayBuffer objects with a pointer to its buffer + * + * @return new ArrayBuffer object + */ +ecma_object_t * +ecma_arraybuffer_create_object_with_buffer (uint8_t type, /**< type of the arraybuffer */ + uint32_t length) +{ + ecma_builtin_id_t prototype_id; + +#if JERRY_BUILTIN_SHAREDARRAYBUFFER + JERRY_ASSERT (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER + || type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER); + + prototype_id = (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER ? ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE + : ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE); +#else /* !JERRY_BUILTIN_SHAREDARRAYBUFFER */ + JERRY_ASSERT (type == ECMA_OBJECT_CLASS_ARRAY_BUFFER); + + prototype_id = ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE; +#endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ + + ecma_object_t *object_p = ecma_create_object (ecma_builtin_get (prototype_id), + sizeof (ecma_arraybuffer_pointer_t), + ECMA_OBJECT_TYPE_CLASS); + + ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) object_p; + arraybuffer_pointer_p->extended_object.u.cls.type = type; + arraybuffer_pointer_p->extended_object.u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_HAS_POINTER; + arraybuffer_pointer_p->extended_object.u.cls.u3.length = length; + + arraybuffer_pointer_p->buffer_p = NULL; + arraybuffer_pointer_p->arraybuffer_user_p = NULL; return object_p; +} /* ecma_arraybuffer_create_object_with_buffer */ + +/** + * Creating ArrayBuffer objects based on the array length + * + * @return new ArrayBuffer object + */ +ecma_object_t * +ecma_arraybuffer_new_object (uint32_t length) /**< length of the arraybuffer */ +{ + if (length > JERRY_CONTEXT (arraybuffer_compact_allocation_limit)) + { + return ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_ARRAY_BUFFER, length); + } + + return ecma_arraybuffer_create_object (ECMA_OBJECT_CLASS_ARRAY_BUFFER, length); } /* ecma_arraybuffer_new_object */ /** - * Helper function: create arraybuffer object with external buffer backing. + * Allocate a backing store for an array buffer. * - * The struct of external arraybuffer object: - * ecma_object_t - * extend_part - * arraybuffer external info part - * - * @return ecma_object_t *, pointer to the created ArrayBuffer object + * @return ECMA_VALUE_UNDEFINED on success, + * ECMA_VALUE_ERROR otherwise */ -ecma_object_t * -ecma_arraybuffer_new_object_external (uint32_t length, /**< length of the buffer_p to use */ - void *buffer_p, /**< pointer for ArrayBuffer's buffer backing */ - jerry_value_free_callback_t free_cb) /**< buffer free callback */ +ecma_value_t +ecma_arraybuffer_allocate_buffer (ecma_object_t *object_p) /**< ArrayBuffer object */ { - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_ARRAYBUFFER_PROTOTYPE); - ecma_object_t *object_p = ecma_create_object (prototype_obj_p, - sizeof (ecma_arraybuffer_external_info), - ECMA_OBJECT_TYPE_CLASS); + JERRY_ASSERT (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_ALLOCATED)); - ecma_arraybuffer_external_info *array_object_p = (ecma_arraybuffer_external_info *) object_p; - array_object_p->extended_object.u.cls.type = ECMA_OBJECT_CLASS_ARRAY_BUFFER; - array_object_p->extended_object.u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_EXTERNAL_MEMORY; - array_object_p->extended_object.u.cls.u3.length = length; + if (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_DETACHED) + { + return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + } - array_object_p->buffer_p = buffer_p; - array_object_p->free_cb = free_cb; + JERRY_ASSERT (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_HAS_POINTER); - return object_p; -} /* ecma_arraybuffer_new_object_external */ + ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) object_p; + uint32_t arraybuffer_length = extended_object_p->u.cls.u3.length; + ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) object_p; + jerry_arraybuffer_allocate_t arraybuffer_allocate_callback = JERRY_CONTEXT (arraybuffer_allocate_callback); + void *buffer_p; + + if (arraybuffer_allocate_callback != NULL) + { + jerry_arraybuffer_type_t type = JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER; + +#if JERRY_BUILTIN_SHAREDARRAYBUFFER + if (extended_object_p->u.cls.type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER) + { + type = JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER; + } +#endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ + + buffer_p = arraybuffer_allocate_callback (type, + arraybuffer_length, + &arraybuffer_pointer_p->arraybuffer_user_p, + JERRY_CONTEXT (arraybuffer_allocate_callback_user_p)); + } + else + { + buffer_p = jmem_heap_alloc_block_null_on_error (arraybuffer_length); + } + + if (buffer_p == NULL) + { + extended_object_p->u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_DETACHED; + return ecma_raise_range_error (ECMA_ERR_MSG ("Cannot allocate memory for ArrayBuffer")); + } + + arraybuffer_pointer_p->buffer_p = buffer_p; + extended_object_p->u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_ALLOCATED; + + memset (buffer_p, 0, arraybuffer_length); + return ECMA_VALUE_UNDEFINED; +} /* ecma_arraybuffer_allocate_buffer */ + +/** + * Release the backing store allocated by an array buffer. + */ +void +ecma_arraybuffer_release_buffer (ecma_object_t *object_p) /**< ArrayBuffer object */ +{ + JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) + || ecma_object_is_shared_arraybuffer (object_p)); + + jerry_arraybuffer_free_t free_callback = JERRY_CONTEXT (arraybuffer_free_callback); + ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) object_p; + uint32_t arraybuffer_length = arraybuffer_pointer_p->extended_object.u.cls.u3.length; + + if (free_callback == NULL) + { + jmem_heap_free_block (arraybuffer_pointer_p->buffer_p, arraybuffer_length); + return; + } + + jerry_arraybuffer_type_t type = JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER; + +#if JERRY_BUILTIN_SHAREDARRAYBUFFER + if (arraybuffer_pointer_p->extended_object.u.cls.type == ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER) + { + type = JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER; + } +#endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ + + free_callback (type, + arraybuffer_pointer_p->buffer_p, + arraybuffer_length, + arraybuffer_pointer_p->arraybuffer_user_p, + JERRY_CONTEXT (arraybuffer_allocate_callback_user_p)); +} /* ecma_arraybuffer_release_buffer */ /** * ArrayBuffer object creation operation. @@ -198,26 +320,21 @@ ecma_arraybuffer_get_length (ecma_object_t *object_p) /**< pointer to the ArrayB * * @return pointer to the data buffer */ -extern inline lit_utf8_byte_t * JERRY_ATTR_PURE JERRY_ATTR_ALWAYS_INLINE +extern inline uint8_t * JERRY_ATTR_PURE JERRY_ATTR_ALWAYS_INLINE ecma_arraybuffer_get_buffer (ecma_object_t *object_p) /**< pointer to the ArrayBuffer object */ { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (object_p)); - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + JERRY_ASSERT (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_ALLOCATED); - if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p)) + if (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) { - ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p; - JERRY_ASSERT (!ecma_arraybuffer_is_detached (object_p) || array_p->buffer_p == NULL); - return (lit_utf8_byte_t *) array_p->buffer_p; - } - else if (ext_object_p->u.cls.u1.array_buffer_flags & ECMA_ARRAYBUFFER_DETACHED) - { - return NULL; + return (uint8_t *) object_p + sizeof (ecma_extended_object_t); } - return (lit_utf8_byte_t *) (ext_object_p + 1); + ecma_arraybuffer_pointer_t *arraybuffer_pointer_p = (ecma_arraybuffer_pointer_t *) object_p; + return (uint8_t *) arraybuffer_pointer_p->buffer_p; } /* ecma_arraybuffer_get_buffer */ /** @@ -232,7 +349,7 @@ ecma_arraybuffer_is_detached (ecma_object_t *object_p) /**< pointer to the Array JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) || ecma_object_is_shared_arraybuffer (object_p)); - return (((ecma_extended_object_t *) object_p)->u.cls.u1.array_buffer_flags & ECMA_ARRAYBUFFER_DETACHED) != 0; + return (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_DETACHED) != 0; } /* ecma_arraybuffer_is_detached */ /** @@ -240,7 +357,7 @@ ecma_arraybuffer_is_detached (ecma_object_t *object_p) /**< pointer to the Array * * See also: ES2015 24.1.1.3 * - * @return true - if detach op succeeded + * @return true - if detach operation is succeeded * false - otherwise */ extern inline bool JERRY_ATTR_ALWAYS_INLINE @@ -248,7 +365,7 @@ ecma_arraybuffer_detach (ecma_object_t *object_p) /**< pointer to the ArrayBuffe { JERRY_ASSERT (ecma_object_class_is (object_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER)); - if (ecma_arraybuffer_is_detached (object_p)) + if (ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_DETACHED) { return false; } @@ -256,23 +373,31 @@ ecma_arraybuffer_detach (ecma_object_t *object_p) /**< pointer to the ArrayBuffe ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; ext_object_p->u.cls.u1.array_buffer_flags |= ECMA_ARRAYBUFFER_DETACHED; - if (ECMA_ARRAYBUFFER_HAS_EXTERNAL_MEMORY (ext_object_p)) + if (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_ALLOCATED)) { - ecma_arraybuffer_external_info *array_p = (ecma_arraybuffer_external_info *) ext_object_p; - - if (array_p->free_cb != NULL) - { - array_p->free_cb (array_p->buffer_p); - array_p->free_cb = NULL; - } - - ext_object_p->u.cls.u3.length = 0; - array_p->buffer_p = NULL; + return true; } + ext_object_p->u.cls.u1.array_buffer_flags &= (uint8_t) ~ECMA_ARRAYBUFFER_ALLOCATED; + + if (!(ECMA_ARRAYBUFFER_GET_FLAGS (object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) + { + return true; + } + + ecma_arraybuffer_release_buffer (object_p); return true; } /* ecma_arraybuffer_detach */ +/** + * ArrayBuffer slice operation + * + * See also: + * ECMA-262 v11, 24.1.4.3 + * + * @return ecma value + * Returned value must be freed with ecma_free_value. + */ ecma_value_t ecma_builtin_arraybuffer_slice (ecma_value_t this_arg, const ecma_value_t *argument_list_p, @@ -280,10 +405,10 @@ ecma_builtin_arraybuffer_slice (ecma_value_t this_arg, { ecma_object_t *object_p = ecma_get_object_from_value (this_arg); - /* 4. */ - if (ecma_arraybuffer_is_detached (object_p)) + /* 3-4. */ + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (object_p)) { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } /* 5. */ @@ -358,7 +483,7 @@ ecma_builtin_arraybuffer_slice (ecma_value_t this_arg, } /* 14-15. */ - if (ecma_arraybuffer_is_detached (new_arraybuffer_p)) + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (new_arraybuffer_p)) { ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Returned ArrayBuffer has been detached")); goto free_new_arraybuffer; @@ -381,7 +506,7 @@ ecma_builtin_arraybuffer_slice (ecma_value_t this_arg, /* 19. */ if (ecma_arraybuffer_is_detached (object_p)) { - ret_value = ecma_raise_type_error (ECMA_ERR_MSG ("Original ArrayBuffer has been detached")); + ret_value = ECMA_VALUE_ERROR; goto free_new_arraybuffer; } diff --git a/jerry-core/ecma/operations/ecma-arraybuffer-object.h b/jerry-core/ecma/operations/ecma-arraybuffer-object.h index 48cbe078d..be53a944f 100644 --- a/jerry-core/ecma/operations/ecma-arraybuffer-object.h +++ b/jerry-core/ecma/operations/ecma-arraybuffer-object.h @@ -27,6 +27,19 @@ * @{ */ +/** + * Get array buffer flags. + */ +#define ECMA_ARRAYBUFFER_GET_FLAGS(arraybuffer_p) \ + (((ecma_extended_object_t *) (arraybuffer_p))->u.cls.u1.array_buffer_flags) + +/** + * Check whether the backing store is allocated for an array buffer. + */ +#define ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR(arraybuffer_p) \ + (JERRY_UNLIKELY (!(ECMA_ARRAYBUFFER_GET_FLAGS (arraybuffer_p) & ECMA_ARRAYBUFFER_ALLOCATED)) \ + && ecma_arraybuffer_allocate_buffer (arraybuffer_p) == ECMA_VALUE_ERROR) + ecma_value_t ecma_op_create_arraybuffer_object (const ecma_value_t *, uint32_t); @@ -34,12 +47,16 @@ ecma_op_create_arraybuffer_object (const ecma_value_t *, uint32_t); * Helper functions for arraybuffer. */ ecma_object_t * -ecma_arraybuffer_new_object (uint32_t lengh); +ecma_arraybuffer_create_object (uint8_t type, uint32_t length); ecma_object_t * -ecma_arraybuffer_new_object_external (uint32_t length, - void *buffer_p, - jerry_value_free_callback_t free_cb); -lit_utf8_byte_t * JERRY_ATTR_PURE +ecma_arraybuffer_create_object_with_buffer (uint8_t type, uint32_t length); +ecma_object_t * +ecma_arraybuffer_new_object (uint32_t length); +ecma_value_t +ecma_arraybuffer_allocate_buffer (ecma_object_t *object_p); +void +ecma_arraybuffer_release_buffer (ecma_object_t *object_p); +uint8_t * JERRY_ATTR_PURE ecma_arraybuffer_get_buffer (ecma_object_t *obj_p); uint32_t JERRY_ATTR_PURE ecma_arraybuffer_get_length (ecma_object_t *obj_p); diff --git a/jerry-core/ecma/operations/ecma-dataview-object.c b/jerry-core/ecma/operations/ecma-dataview-object.c index 75b9bc035..8ef71c1fe 100644 --- a/jerry-core/ecma/operations/ecma-dataview-object.c +++ b/jerry-core/ecma/operations/ecma-dataview-object.c @@ -298,10 +298,9 @@ ecma_op_dataview_get_set_view_value (ecma_value_t view, /**< the operation's 'vi /* GetViewValue 4., SetViewValue 6. */ bool is_little_endian = ecma_op_to_boolean (is_little_endian_value); - if (ecma_arraybuffer_is_detached (buffer_p)) + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (buffer_p)) { - ecma_free_value (value_to_set); - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } /* GetViewValue 7., SetViewValue 9. */ @@ -320,10 +319,12 @@ ecma_op_dataview_get_set_view_value (ecma_value_t view, /**< the operation's 'vi return ecma_raise_range_error (ECMA_ERR_MSG ("Start offset is outside the bounds of the buffer")); } - /* GetViewValue 11., SetViewValue 13. */ - uint32_t buffer_index = (uint32_t) get_index + view_offset; - lit_utf8_byte_t *block_p = ecma_arraybuffer_get_buffer (buffer_p) + buffer_index; + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (buffer_p)) + { + return ECMA_VALUE_ERROR; + } + /* GetViewValue 11., SetViewValue 13. */ bool system_is_little_endian = ecma_dataview_check_little_endian (); ecma_typedarray_info_t info; @@ -335,21 +336,27 @@ ecma_op_dataview_get_set_view_value (ecma_value_t view, /**< the operation's 'vi info.array_buffer_p = buffer_p; /* GetViewValue 12. */ + uint8_t *block_p = ecma_arraybuffer_get_buffer (buffer_p) + (uint32_t) get_index + view_offset; + if (ecma_is_value_empty (value_to_set)) { JERRY_VLA (lit_utf8_byte_t, swap_block_p, element_size); memcpy (swap_block_p, block_p, element_size * sizeof (lit_utf8_byte_t)); ecma_dataview_swap_order (system_is_little_endian, is_little_endian, element_size, swap_block_p); - info.buffer_p = swap_block_p; - return ecma_get_typedarray_element (&info, 0); + + ecma_typedarray_getter_fn_t typedarray_getter_cb = ecma_get_typedarray_getter_fn (info.id); + return typedarray_getter_cb (swap_block_p); } + if (!ecma_number_is_nan (get_index) && get_index <= 0) { get_index = 0; } + /* SetViewValue 14. */ - info.buffer_p = block_p; - ecma_value_t set_element = ecma_set_typedarray_element (&info, value_to_set, 0); + ecma_typedarray_setter_fn_t typedarray_setter_cb = ecma_get_typedarray_setter_fn (info.id); + ecma_value_t set_element = typedarray_setter_cb (block_p, value_to_set); + ecma_free_value (value_to_set); if (ECMA_IS_VALUE_ERROR (set_element)) diff --git a/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.c b/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.c index 22ddc3be8..2ac0c6916 100644 --- a/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.c +++ b/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.c @@ -13,17 +13,17 @@ * limitations under the License. */ -#include "ecma-shared-arraybuffer-object.h" -#include "ecma-typedarray-object.h" -#include "ecma-objects.h" +#include "ecma-arraybuffer-object.h" #include "ecma-builtins.h" #include "ecma-exceptions.h" +#include "ecma-function-object.h" #include "ecma-gc.h" #include "ecma-globals.h" #include "ecma-helpers.h" -#include "jmem.h" +#include "ecma-objects.h" +#include "ecma-shared-arraybuffer-object.h" +#include "ecma-typedarray-object.h" #include "jcontext.h" -#include "ecma-function-object.h" /** \addtogroup ecma ECMA * @{ @@ -35,65 +35,21 @@ #if JERRY_BUILTIN_SHAREDARRAYBUFFER /** - * Helper function: create SharedArrayBuffer object based on the array length + * Creating SharedArrayBuffer objects based on the array length * - * The struct of arraybuffer object: - * ecma_object_t - * extend_part - * data buffer - * - * @return ecma_object_t * + * @return new SharedArrayBuffer object */ ecma_object_t * ecma_shared_arraybuffer_new_object (uint32_t length) /**< length of the SharedArrayBuffer */ { - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE); - ecma_object_t *object_p = ecma_create_object (prototype_obj_p, - sizeof (ecma_extended_object_t) + length, - ECMA_OBJECT_TYPE_CLASS); + if (length > 0) + { + return ecma_arraybuffer_create_object_with_buffer (ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, length); + } - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER; - ext_object_p->u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_INTERNAL_MEMORY; - ext_object_p->u.cls.u3.length = length; - - lit_utf8_byte_t *buf = (lit_utf8_byte_t *) (ext_object_p + 1); - memset (buf, 0, length); - - return object_p; + return ecma_arraybuffer_create_object (ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER, length); } /* ecma_shared_arraybuffer_new_object */ -/** - * Helper function: create SharedArrayBuffer object with external buffer backing. - * - * The struct of external arraybuffer object: - * ecma_object_t - * extend_part - * SharedArrayBuffer external info part - * - * @return ecma_object_t *, pointer to the created SharedArrayBuffer object - */ -ecma_object_t * -ecma_shared_arraybuffer_new_object_external (uint32_t length, /**< length of the buffer_p to use */ - void *buffer_p, /**< pointer for SharedArrayBuffer's buffer backing */ - jerry_value_free_callback_t free_cb) /**< buffer free callback */ -{ - ecma_object_t *prototype_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_SHARED_ARRAYBUFFER_PROTOTYPE); - ecma_object_t *object_p = ecma_create_object (prototype_obj_p, - sizeof (ecma_arraybuffer_external_info), - ECMA_OBJECT_TYPE_CLASS); - - ecma_arraybuffer_external_info *array_object_p = (ecma_arraybuffer_external_info *) object_p; - array_object_p->extended_object.u.cls.type = ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER; - array_object_p->extended_object.u.cls.u1.array_buffer_flags = ECMA_ARRAYBUFFER_EXTERNAL_MEMORY; - array_object_p->extended_object.u.cls.u3.length = length; - - array_object_p->buffer_p = buffer_p; - array_object_p->free_cb = free_cb; - - return object_p; -} /* ecma_shared_arraybuffer_new_object_external */ - /** * SharedArrayBuffer object creation operation. * diff --git a/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.h b/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.h index 0bcfd0186..f0bc2eded 100644 --- a/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.h +++ b/jerry-core/ecma/operations/ecma-shared-arraybuffer-object.h @@ -35,10 +35,6 @@ ecma_op_create_shared_arraybuffer_object (const ecma_value_t *, uint32_t); */ ecma_object_t * ecma_shared_arraybuffer_new_object (uint32_t lengh); -ecma_object_t * -ecma_shared_arraybuffer_new_object_external (uint32_t length, - void *buffer_p, - jerry_value_free_callback_t free_cb); #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ bool ecma_is_shared_arraybuffer (ecma_value_t val); diff --git a/jerry-core/ecma/operations/ecma-typedarray-object.c b/jerry-core/ecma/operations/ecma-typedarray-object.c index cb2b978e2..397969697 100644 --- a/jerry-core/ecma/operations/ecma-typedarray-object.c +++ b/jerry-core/ecma/operations/ecma-typedarray-object.c @@ -622,34 +622,35 @@ ecma_get_typedarray_magic_string_id (ecma_typedarray_type_t typedarray_id) * @return ecma_typedarray_getter_fn_t: the getter function for the given builtin TypedArray id */ extern inline ecma_typedarray_getter_fn_t JERRY_ATTR_ALWAYS_INLINE -ecma_get_typedarray_getter_fn (ecma_typedarray_type_t typedarray_id) +ecma_get_typedarray_getter_fn (ecma_typedarray_type_t typedarray_id) /**< typedarray id */ { return ecma_typedarray_getters[typedarray_id]; } /* ecma_get_typedarray_getter_fn */ /** - * get typedarray's element value + * Get element from a TypedArray * - * @return ecma_number_t: the value of the element + * @return the value of the element */ extern inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE -ecma_get_typedarray_element (ecma_typedarray_info_t *info_p, - ecma_number_t num) +ecma_get_typedarray_element (ecma_typedarray_info_t *info_p, /**< typedarray info */ + ecma_number_t num) /**< element index */ { - if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) + uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); + + if (JERRY_UNLIKELY (buffer_p == NULL)) { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } - if (!ecma_op_is_integer (num) + if (ecma_number_is_negative (num) || num >= info_p->length - || num < 0 - || (ecma_number_is_negative (num) && ecma_number_is_zero (num))) + || ((ecma_number_t) (uint32_t) num) != num) { return ECMA_VALUE_UNDEFINED; } - uint32_t byte_pos = (uint32_t) num << info_p->shift; - return ecma_typedarray_getters[info_p->id](info_p->buffer_p + byte_pos); + + return ecma_typedarray_getters[info_p->id](buffer_p + ((uint32_t) num << info_p->shift)); } /* ecma_get_typedarray_element */ /** @@ -658,7 +659,7 @@ ecma_get_typedarray_element (ecma_typedarray_info_t *info_p, * @return ecma_typedarray_setter_fn_t: the setter function for the given builtin TypedArray id */ extern inline ecma_typedarray_setter_fn_t JERRY_ATTR_ALWAYS_INLINE -ecma_get_typedarray_setter_fn (ecma_typedarray_type_t typedarray_id) +ecma_get_typedarray_setter_fn (ecma_typedarray_type_t typedarray_id) /**< typedarray id */ { return ecma_typedarray_setters[typedarray_id]; } /* ecma_get_typedarray_setter_fn */ @@ -667,9 +668,9 @@ ecma_get_typedarray_setter_fn (ecma_typedarray_type_t typedarray_id) * set typedarray's element value */ extern inline ecma_value_t JERRY_ATTR_ALWAYS_INLINE -ecma_set_typedarray_element (ecma_typedarray_info_t *info_p, - ecma_value_t value, - ecma_number_t num) +ecma_set_typedarray_element (ecma_typedarray_info_t *info_p, /**< typedarray info */ + ecma_value_t value, /**< value to be set */ + ecma_number_t num) /**< element index */ { ecma_value_t to_num; if (ECMA_TYPEDARRAY_IS_BIGINT_TYPE (info_p->id)) @@ -692,23 +693,25 @@ ecma_set_typedarray_element (ecma_typedarray_info_t *info_p, } } - if (ecma_arraybuffer_is_detached (info_p->array_buffer_p)) + uint8_t *buffer_p = ecma_typedarray_get_buffer (info_p); + + if (JERRY_UNLIKELY (buffer_p == NULL)) { ecma_free_value (to_num); - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + return ECMA_VALUE_ERROR; } - if (!ecma_op_is_integer (num) + if (ecma_number_is_negative (num) || num >= info_p->length - || num < 0 - || (ecma_number_is_negative (num) && ecma_number_is_zero (num))) + || ((ecma_number_t) (uint32_t) num) != num) { ecma_free_value (to_num); return ECMA_VALUE_FALSE; } + ecma_free_value (to_num); - lit_utf8_byte_t *src_buffer = info_p->buffer_p + ((uint32_t) num << info_p->shift); - return ecma_typedarray_setters[info_p->id](src_buffer, value); + + return ecma_typedarray_setters[info_p->id](buffer_p + ((uint32_t) num << info_p->shift), value); } /* ecma_set_typedarray_element */ /** @@ -830,6 +833,7 @@ ecma_typedarray_create_object_with_length (uint32_t array_length, /**< length of else { ecma_value_t ctor_proto = ecma_op_species_constructor (src_buffer_p, ECMA_BUILTIN_ID_ARRAYBUFFER); + if (ECMA_IS_VALUE_ERROR (ctor_proto)) { return ctor_proto; @@ -856,7 +860,7 @@ ecma_typedarray_create_object_with_length (uint32_t array_length, /**< length of if (ecma_arraybuffer_is_detached (src_buffer_p)) { ecma_deref_object (new_arraybuffer_p); - return ecma_raise_type_error (ECMA_ERR_MSG ("Maximum TypedArray size is reached")); + return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); } } @@ -875,53 +879,6 @@ ecma_typedarray_create_object_with_length (uint32_t array_length, /**< length of return ecma_make_object_value (object_p); } /* ecma_typedarray_create_object_with_length */ -/** - * Create a TypedArray object by given buffer, offset, and array_length - * - * See also: ES2015 22.2.1.5 - * - * @return ecma value of the new typedarray object - * Returned value must be freed with ecma_free_value - */ -static ecma_value_t -ecma_typedarray_create_object_with_buffer (ecma_object_t *arraybuffer_p, /**< the arraybuffer inside */ - uint32_t byte_offset, /**< the byte offset of the arraybuffer */ - uint32_t array_length, /**< length of the typedarray */ - ecma_object_t *proto_p, /**< prototype object */ - uint8_t element_size_shift, /**< the size shift of the element length */ - ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ -{ - if (ecma_arraybuffer_is_detached (arraybuffer_p)) - { - return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); - } - uint32_t expected_length = (ecma_arraybuffer_get_length (arraybuffer_p) >> element_size_shift); - - bool needs_ext_typedarray_obj = (byte_offset != 0 || array_length != expected_length); - - size_t object_size = (needs_ext_typedarray_obj ? sizeof (ecma_extended_typedarray_object_t) - : sizeof (ecma_extended_object_t)); - - ecma_object_t *object_p = ecma_create_object (proto_p, object_size, ECMA_OBJECT_TYPE_CLASS); - - ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; - ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_TYPEDARRAY; - ext_object_p->u.cls.u1.typedarray_type = (uint8_t) typedarray_id; - ext_object_p->u.cls.u2.typedarray_flags = 0; - ext_object_p->u.cls.u3.arraybuffer = ecma_make_object_value (arraybuffer_p); - - if (needs_ext_typedarray_obj) - { - ext_object_p->u.cls.u2.typedarray_flags |= ECMA_TYPEDARRAY_IS_EXTENDED; - - ecma_extended_typedarray_object_t *typedarray_info_p = (ecma_extended_typedarray_object_t *) object_p; - typedarray_info_p->array_length = array_length; - typedarray_info_p->byte_offset = byte_offset; - } - - return ecma_make_object_value (object_p); -} /* ecma_typedarray_create_object_with_buffer */ - /** * Create a TypedArray object by given another TypedArray object * @@ -938,9 +895,10 @@ ecma_typedarray_create_object_with_typedarray (ecma_object_t *typedarray_p, /**< { uint32_t array_length = ecma_typedarray_get_length (typedarray_p); ecma_object_t *src_arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); - if (ecma_arraybuffer_is_detached (src_arraybuffer_p)) + + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (src_arraybuffer_p)) { - return ecma_raise_type_error (ECMA_ERR_MSG ("Invalid detached ArrayBuffer")); + return ECMA_VALUE_ERROR; } ecma_value_t new_typedarray = ecma_typedarray_create_object_with_length (array_length, @@ -955,10 +913,14 @@ ecma_typedarray_create_object_with_typedarray (ecma_object_t *typedarray_p, /**< } ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); + ecma_object_t *dst_arraybuffer_p = ecma_typedarray_get_arraybuffer (new_typedarray_p); + + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (dst_arraybuffer_p)) + { + return ECMA_VALUE_ERROR; + } lit_utf8_byte_t *src_buf_p = ecma_arraybuffer_get_buffer (src_arraybuffer_p); - - ecma_object_t *dst_arraybuffer_p = ecma_typedarray_get_arraybuffer (new_typedarray_p); lit_utf8_byte_t *dst_buf_p = ecma_arraybuffer_get_buffer (dst_arraybuffer_p); src_buf_p += ecma_typedarray_get_offset (typedarray_p); @@ -1017,15 +979,10 @@ ecma_op_typedarray_from_helper (ecma_value_t this_val, /**< this_arg for the abo ecma_value_t current_value, /**< given value to set */ uint32_t index, /**< currrent index */ ecma_object_t *func_object_p, /**< map function object */ - ecma_typedarray_info_t *info_p, /**< typedarray info */ + uint8_t *buffer_p, /**< target buffer */ ecma_typedarray_setter_fn_t setter_cb) /**< setter callback function */ { - ecma_value_t mapped_value; - - if (!ecma_is_value_found (current_value)) - { - current_value = ECMA_VALUE_UNDEFINED; - } + ecma_value_t mapped_value = current_value; if (func_object_p != NULL) { @@ -1045,17 +1002,8 @@ ecma_op_typedarray_from_helper (ecma_value_t this_val, /**< this_arg for the abo mapped_value = cb_value; } - else - { - mapped_value = current_value; - } - if (index >= info_p->length) - { - return ecma_raise_type_error (ECMA_ERR_MSG ("Invalid argument type")); - } - - ecma_value_t set_element = setter_cb (info_p->buffer_p + (index << info_p->shift), mapped_value); + ecma_value_t set_element = setter_cb (buffer_p, mapped_value); ecma_free_value (mapped_value); if (ECMA_IS_VALUE_ERROR (set_element)) @@ -1080,7 +1028,6 @@ ecma_typedarray_create_object_with_object (ecma_value_t items_val, /**< the sour uint8_t element_size_shift, /**< the size shift of the element length */ ecma_typedarray_type_t typedarray_id) /**< id of the typedarray */ { - /* 5 */ ecma_value_t using_iterator = ecma_op_get_method_by_symbol_id (items_val, LIT_GLOBAL_SYMBOL_ITERATOR); @@ -1156,39 +1103,48 @@ ecma_typedarray_create_object_with_object (ecma_value_t items_val, /**< the sour ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t info = ecma_typedarray_get_info (new_typedarray_p); - ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); + ecma_value_t *next_value_p = values_p->buffer_p; + uint8_t *buffer_p = ecma_typedarray_get_buffer (&info); - ret_value = ecma_make_object_value (new_typedarray_p); + ret_value = ECMA_VALUE_ERROR; - /* 8.e */ - for (uint32_t index = 0; index < values_p->item_count; index++) + if (buffer_p != NULL) { - ecma_value_t set_value = ecma_op_typedarray_from_helper (ECMA_VALUE_UNDEFINED, - values_p->buffer_p[index], - index, - NULL, - &info, - setter_cb); + ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); + uint8_t *limit_p = buffer_p + (values_p->item_count << info.shift); - if (ECMA_IS_VALUE_ERROR (set_value)) + ret_value = ecma_make_object_value (new_typedarray_p); + + /* 8.e */ + while (buffer_p < limit_p) { - for (uint32_t j = index + 1; j < values_p->item_count; j++) + ecma_value_t value = *next_value_p++; + ecma_value_t set_value = setter_cb (buffer_p, value); + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (set_value)) { - ecma_free_value (values_p->buffer_p[j]); + ret_value = set_value; + break; } - ret_value = set_value; - break; + buffer_p += info.element_size; } } - ecma_collection_destroy (values_p); - if (ECMA_IS_VALUE_ERROR (ret_value)) { + ecma_value_t *last_value_p = values_p->buffer_p + values_p->item_count; + + while (next_value_p < last_value_p) + { + ecma_free_value (*next_value_p++); + } + ecma_deref_object (new_typedarray_p); } + ecma_collection_destroy (values_p); return ret_value; } @@ -1235,31 +1191,41 @@ ecma_typedarray_create_object_with_object (ecma_value_t items_val, /**< the sour ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t info = ecma_typedarray_get_info (new_typedarray_p); - ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); - ecma_value_t ret_value = ecma_make_object_value (new_typedarray_p); + uint8_t *buffer_p = ecma_typedarray_get_buffer (&info); + ecma_value_t ret_value = ECMA_VALUE_ERROR; - /* 12 */ - for (uint32_t index = 0; index < len; index++) + if (buffer_p != NULL) { - ecma_value_t current_value = ecma_op_object_find_by_index (arraylike_object_p, index); + ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); - if (ECMA_IS_VALUE_ERROR (current_value)) + ret_value = ecma_make_object_value (new_typedarray_p); + + /* 12 */ + for (uint32_t index = 0; index < len; index++) { - ret_value = current_value; - break; - } + ecma_value_t value = ecma_op_object_find_by_index (arraylike_object_p, index); - ecma_value_t set_value = ecma_op_typedarray_from_helper (ECMA_VALUE_UNDEFINED, - current_value, - index, - NULL, - &info, - setter_cb); + if (ECMA_IS_VALUE_ERROR (value)) + { + ret_value = value; + break; + } - if (ECMA_IS_VALUE_ERROR (set_value)) - { - ret_value = set_value; - break; + if (!ecma_is_value_found (value)) + { + value = ECMA_VALUE_UNDEFINED; + } + + ecma_value_t set_value = setter_cb (buffer_p, value); + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (set_value)) + { + ret_value = set_value; + break; + } + + buffer_p += info.element_size; } } @@ -1377,38 +1343,48 @@ ecma_op_typedarray_from (ecma_value_t this_val, /**< this value */ ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t info = ecma_typedarray_get_info (new_typedarray_p); ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); + ecma_value_t *next_value_p = values_p->buffer_p; + uint8_t *buffer_p = ecma_typedarray_get_buffer (&info); - ret_value = ecma_make_object_value (new_typedarray_p); + ret_value = ECMA_VALUE_ERROR; - /* 6.e */ - for (uint32_t index = 0; index < values_p->item_count; index++) + if (buffer_p != NULL) { - ecma_value_t set_value = ecma_op_typedarray_from_helper (this_arg, - values_p->buffer_p[index], - index, - func_object_p, - &info, - setter_cb); + ret_value = ecma_make_object_value (new_typedarray_p); - if (ECMA_IS_VALUE_ERROR (set_value)) + /* 6.e */ + for (uint32_t index = 0; index < values_p->item_count; index++) { - for (uint32_t j = index + 1; j < values_p->item_count; j++) + ecma_value_t set_value = ecma_op_typedarray_from_helper (this_arg, + *next_value_p++, + index, + func_object_p, + buffer_p, + setter_cb); + + if (ECMA_IS_VALUE_ERROR (set_value)) { - ecma_free_value (values_p->buffer_p[j]); + ret_value = set_value; + break; } - ret_value = set_value; - break; + buffer_p += info.element_size; } } - ecma_collection_destroy (values_p); - if (ECMA_IS_VALUE_ERROR (ret_value)) { + ecma_value_t *last_value_p = values_p->buffer_p + values_p->item_count; + + while (next_value_p < last_value_p) + { + ecma_free_value (*next_value_p++); + } + ecma_deref_object (new_typedarray_p); } + ecma_collection_destroy (values_p); return ret_value; } @@ -1454,31 +1430,45 @@ ecma_op_typedarray_from (ecma_value_t this_val, /**< this value */ ecma_object_t *new_typedarray_p = ecma_get_object_from_value (new_typedarray); ecma_typedarray_info_t info = ecma_typedarray_get_info (new_typedarray_p); - ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); - ecma_value_t ret_value = ecma_make_object_value (new_typedarray_p); + uint8_t *buffer_p = ecma_typedarray_get_buffer (&info); + ecma_value_t ret_value = ECMA_VALUE_ERROR; - /* 12 */ - for (uint32_t index = 0; index < len; index++) + if (buffer_p != NULL) { - ecma_value_t current_value = ecma_op_object_find_by_index (arraylike_object_p, index); + ecma_typedarray_setter_fn_t setter_cb = ecma_get_typedarray_setter_fn (info.id); - if (ECMA_IS_VALUE_ERROR (current_value)) + ret_value = ecma_make_object_value (new_typedarray_p); + + /* 12 */ + for (uint32_t index = 0; index < len; index++) { - ret_value = current_value; - break; - } + ecma_value_t value = ecma_op_object_find_by_index (arraylike_object_p, index); - ecma_value_t set_value = ecma_op_typedarray_from_helper (this_arg, - current_value, - index, - func_object_p, - &info, - setter_cb); + if (ECMA_IS_VALUE_ERROR (value)) + { + ret_value = value; + break; + } - if (ECMA_IS_VALUE_ERROR (set_value)) - { - ret_value = set_value; - break; + if (!ecma_is_value_found (value)) + { + value = ECMA_VALUE_UNDEFINED; + } + + ecma_value_t set_value = ecma_op_typedarray_from_helper (this_arg, + value, + index, + func_object_p, + buffer_p, + setter_cb); + + if (ECMA_IS_VALUE_ERROR (set_value)) + { + ret_value = set_value; + break; + } + + buffer_p += info.element_size; } } @@ -1543,6 +1533,7 @@ ecma_typedarray_get_length (ecma_object_t *typedarray_p) /**< the pointer to the } ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); + if (ecma_arraybuffer_is_detached (arraybuffer_p)) { return 0; @@ -1571,6 +1562,7 @@ ecma_typedarray_get_offset (ecma_object_t *typedarray_p) /**< the pointer to the } ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); + if (ecma_arraybuffer_is_detached (arraybuffer_p)) { return 0; @@ -1582,16 +1574,22 @@ ecma_typedarray_get_offset (ecma_object_t *typedarray_p) /**< the pointer to the } /* ecma_typedarray_get_offset */ /** - * Utility function: return the pointer of the data buffer referenced by the typed array + * Utility function: return the pointer of the data buffer referenced by the typedarray info * - * @return pointer to the data buffer + * @return pointer to the data buffer if successfull, + * NULL otherwise */ -lit_utf8_byte_t * -ecma_typedarray_get_buffer (ecma_object_t *typedarray_p) /**< the pointer to the typed array object */ +uint8_t * +ecma_typedarray_get_buffer (ecma_typedarray_info_t *info_p) /**< typedarray info */ { - ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); + ecma_object_t *array_buffer_p = info_p->array_buffer_p; - return ecma_arraybuffer_get_buffer (arraybuffer_p) + ecma_typedarray_get_offset (typedarray_p); + if (ECMA_ARRAYBUFFER_CHECK_BUFFER_ERROR (array_buffer_p)) + { + return NULL; + } + + return ecma_arraybuffer_get_buffer (array_buffer_p) + info_p->offset; } /* ecma_typedarray_get_buffer */ /** @@ -1614,14 +1612,13 @@ ecma_op_create_typedarray (const ecma_value_t *arguments_list_p, /**< the arg li { JERRY_ASSERT (arguments_list_len == 0 || arguments_list_p != NULL); - ecma_value_t ret = ECMA_VALUE_EMPTY; - if (arguments_list_len == 0) { /* 22.2.1.1 */ - ret = ecma_typedarray_create_object_with_length (0, NULL, proto_p, element_size_shift, typedarray_id); + return ecma_typedarray_create_object_with_length (0, NULL, proto_p, element_size_shift, typedarray_id); } - else if (!ecma_is_value_object (arguments_list_p[0])) + + if (!ecma_is_value_object (arguments_list_p[0])) { ecma_number_t num = 0; @@ -1636,127 +1633,144 @@ ecma_op_create_typedarray (const ecma_value_t *arguments_list_p, /**< the arg li if (num > UINT32_MAX) { #if JERRY_ERROR_MESSAGES - ret = ecma_raise_standard_error_with_format (JERRY_ERROR_RANGE, - "Invalid typed array length: %", - arguments_list_p[0]); + return ecma_raise_standard_error_with_format (JERRY_ERROR_RANGE, + "Invalid typed array length: %", + arguments_list_p[0]); #else /* !JERRY_ERROR_MESSAGES */ - ret = ecma_raise_range_error (NULL); + return ecma_raise_range_error (NULL); #endif /* JERRY_ERROR_MESSAGES */ } - else - { - ret = ecma_typedarray_create_object_with_length ((uint32_t) num, - NULL, - proto_p, - element_size_shift, - typedarray_id); - } + + return ecma_typedarray_create_object_with_length ((uint32_t) num, + NULL, + proto_p, + element_size_shift, + typedarray_id); } - else if (ecma_is_value_object (arguments_list_p[0])) + + ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list_p[0]); + + if (ecma_object_is_typedarray (obj_p)) { - ecma_object_t *obj_p = ecma_get_object_from_value (arguments_list_p[0]); - if (ecma_object_is_typedarray (obj_p)) + /* 22.2.1.3 */ + ecma_object_t *typedarray_p = obj_p; + return ecma_typedarray_create_object_with_typedarray (typedarray_p, + proto_p, + element_size_shift, + typedarray_id); + } + + if (!ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) + && !ecma_object_is_shared_arraybuffer (obj_p)) + { + /* 22.2.1.4 */ + return ecma_typedarray_create_object_with_object (arguments_list_p[0], + proto_p, + element_size_shift, + typedarray_id); + } + + /* 22.2.1.5 */ + ecma_object_t *arraybuffer_p = obj_p; + ecma_value_t byte_offset_value = ((arguments_list_len > 1) ? arguments_list_p[1] + : ECMA_VALUE_UNDEFINED); + + ecma_value_t length_value = ((arguments_list_len > 2) ? arguments_list_p[2] + : ECMA_VALUE_UNDEFINED); + + ecma_number_t offset; + + if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (byte_offset_value, &offset))) + { + return ECMA_VALUE_ERROR; + } + + if (ecma_number_is_negative (offset) || fmod (offset, (1 << element_size_shift)) != 0) + { + /* ES2015 22.2.1.5: 9 - 10. */ + if (ecma_number_is_zero (offset)) { - /* 22.2.1.3 */ - ecma_object_t *typedarray_p = obj_p; - ret = ecma_typedarray_create_object_with_typedarray (typedarray_p, - proto_p, - element_size_shift, - typedarray_id); - } - else if (ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_ARRAY_BUFFER) - || ecma_object_is_shared_arraybuffer (obj_p)) - { - /* 22.2.1.5 */ - ecma_object_t *arraybuffer_p = obj_p; - ecma_value_t arg2 = ((arguments_list_len > 1) ? arguments_list_p[1] - : ECMA_VALUE_UNDEFINED); - - ecma_value_t arg3 = ((arguments_list_len > 2) ? arguments_list_p[2] - : ECMA_VALUE_UNDEFINED); - - ecma_number_t offset; - if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (arg2, &offset))) - { - return ECMA_VALUE_ERROR; - } - - if (ecma_number_is_negative (offset) && ecma_number_is_zero (offset)) - { - offset = 0; - } - if (ecma_number_is_negative (offset) || fmod (offset, (1 << element_size_shift)) != 0) - { - /* ES2015 22.2.1.5: 9 - 10. */ - ret = ecma_raise_range_error (ECMA_ERR_MSG ("Invalid offset")); - } - else if (ecma_arraybuffer_is_detached (arraybuffer_p)) - { - ret = ecma_raise_type_error (ECMA_ERR_MSG ("Invalid detached ArrayBuffer")); - } - else - { - uint32_t buf_byte_length = ecma_arraybuffer_get_length (arraybuffer_p); - uint32_t new_byte_length = 0; - - if (ecma_is_value_undefined (arg3)) - { - if ((buf_byte_length % (uint32_t) (1 << element_size_shift) != 0) - || (buf_byte_length < offset)) - { - ret = ecma_raise_range_error (ECMA_ERR_MSG ("Invalid length")); - } - else - { - new_byte_length = (uint32_t) (buf_byte_length - offset); - } - } - else - { - ecma_number_t new_length; - if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (arg3, &new_length))) - { - return ECMA_VALUE_ERROR; - } - - if (new_length > (UINT32_MAX >> element_size_shift)) - { - ret = ecma_raise_range_error (ECMA_ERR_MSG ("Maximum TypedArray size is reached")); - } - else - { - new_byte_length = (uint32_t) new_length << element_size_shift; - - if (((ecma_number_t) new_byte_length + offset) > buf_byte_length) - { - ret = ecma_raise_range_error (ECMA_ERR_MSG ("Invalid length")); - } - } - } - - if (ecma_is_value_empty (ret)) - { - uint32_t array_length = new_byte_length >> element_size_shift; - ret = ecma_typedarray_create_object_with_buffer (arraybuffer_p, - (uint32_t) offset, - array_length, - proto_p, - element_size_shift, - typedarray_id); - } - } + offset = 0; } else { - /* 22.2.1.4 */ - ret = ecma_typedarray_create_object_with_object (arguments_list_p[0], - proto_p, - element_size_shift, - typedarray_id); + return ecma_raise_range_error (ECMA_ERR_MSG ("Invalid offset")); } } - return ret; + ecma_number_t new_length = 0; + + if (ECMA_IS_VALUE_ERROR (ecma_op_to_index (length_value, &new_length))) + { + return ECMA_VALUE_ERROR; + } + + if (ecma_arraybuffer_is_detached (arraybuffer_p)) + { + return ecma_raise_type_error (ECMA_ERR_MSG (ecma_error_arraybuffer_is_detached)); + } + + const char *invalid_length_p = ECMA_ERR_MSG ("Invalid length"); + + if (offset > UINT32_MAX) + { + return ecma_raise_range_error (invalid_length_p); + } + + uint32_t byte_offset = (uint32_t) offset; + + uint32_t buf_byte_length = ecma_arraybuffer_get_length (arraybuffer_p); + uint32_t new_byte_length = 0; + + if (ecma_is_value_undefined (length_value)) + { + if ((buf_byte_length % (uint32_t) (1 << element_size_shift) != 0) + || (buf_byte_length < byte_offset)) + { + return ecma_raise_range_error (invalid_length_p); + } + + new_byte_length = (uint32_t) (buf_byte_length - byte_offset); + } + else + { + if (new_length > (UINT32_MAX >> element_size_shift)) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Maximum TypedArray size is reached")); + } + + new_byte_length = (uint32_t) new_length << element_size_shift; + + if (byte_offset > buf_byte_length + || new_byte_length > (buf_byte_length - byte_offset)) + { + return ecma_raise_range_error (invalid_length_p); + } + } + + bool needs_ext_typedarray_obj = (byte_offset != 0 || new_byte_length != buf_byte_length); + + size_t object_size = (needs_ext_typedarray_obj ? sizeof (ecma_extended_typedarray_object_t) + : sizeof (ecma_extended_object_t)); + + ecma_object_t *object_p = ecma_create_object (proto_p, object_size, ECMA_OBJECT_TYPE_CLASS); + + ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; + ext_object_p->u.cls.type = ECMA_OBJECT_CLASS_TYPEDARRAY; + ext_object_p->u.cls.u1.typedarray_type = (uint8_t) typedarray_id; + ext_object_p->u.cls.u2.typedarray_flags = 0; + ext_object_p->u.cls.u3.arraybuffer = ecma_make_object_value (arraybuffer_p); + + if (needs_ext_typedarray_obj) + { + ext_object_p->u.cls.u2.typedarray_flags |= ECMA_TYPEDARRAY_IS_EXTENDED; + + ecma_extended_typedarray_object_t *typedarray_info_p = (ecma_extended_typedarray_object_t *) object_p; + typedarray_info_p->array_length = new_byte_length >> element_size_shift; + typedarray_info_p->byte_offset = byte_offset; + } + + return ecma_make_object_value (object_p); } /* ecma_op_create_typedarray */ /** @@ -1955,6 +1969,7 @@ ecma_typedarray_create (ecma_object_t *constructor_p, /**< constructor function ecma_object_t *typedarray_p = ecma_get_object_from_value (ret_val); ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (ecma_get_object_from_value (ret_val)); + if (ecma_arraybuffer_is_detached (arraybuffer_p)) { ecma_deref_object (typedarray_p); @@ -2065,7 +2080,6 @@ ecma_typedarray_get_info (ecma_object_t *typedarray_p) info.element_size = (uint8_t) (1 << info.shift); info.offset = ecma_typedarray_get_offset (typedarray_p); info.array_buffer_p = ecma_typedarray_get_arraybuffer (typedarray_p); - info.buffer_p = ecma_arraybuffer_get_buffer (info.array_buffer_p) + info.offset; return info; } /* ecma_typedarray_get_info */ diff --git a/jerry-core/ecma/operations/ecma-typedarray-object.h b/jerry-core/ecma/operations/ecma-typedarray-object.h index 4cbc1965f..70c90bbd3 100644 --- a/jerry-core/ecma/operations/ecma-typedarray-object.h +++ b/jerry-core/ecma/operations/ecma-typedarray-object.h @@ -49,7 +49,7 @@ ecma_value_t ecma_op_typedarray_from (ecma_value_t this_val, ecma_value_t this_arg); uint32_t ecma_typedarray_get_length (ecma_object_t *typedarray_p); uint32_t ecma_typedarray_get_offset (ecma_object_t *typedarray_p); -lit_utf8_byte_t *ecma_typedarray_get_buffer (ecma_object_t *typedarray_p); +uint8_t *ecma_typedarray_get_buffer (ecma_typedarray_info_t *info_p); uint8_t ecma_typedarray_get_element_size_shift (ecma_object_t *typedarray_p); ecma_object_t *ecma_typedarray_get_arraybuffer (ecma_object_t *typedarray_p); ecma_value_t ecma_op_create_typedarray (const ecma_value_t *arguments_list_p, diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index f1dbdfe66..6ee4532bc 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -374,8 +374,7 @@ void jerry_free_source_info (jerry_source_info_t *source_info_p); bool jerry_value_is_arraybuffer (const jerry_value_t value); jerry_value_t jerry_create_arraybuffer (const jerry_length_t size); jerry_value_t jerry_create_arraybuffer_external (const jerry_length_t size, - uint8_t *buffer_p, - jerry_value_free_callback_t free_cb); + uint8_t *buffer_p, void *buffer_user_p); jerry_length_t jerry_arraybuffer_write (const jerry_value_t value, jerry_length_t offset, const uint8_t *buf_p, @@ -388,6 +387,11 @@ jerry_length_t jerry_get_arraybuffer_byte_length (const jerry_value_t value); uint8_t *jerry_get_arraybuffer_pointer (const jerry_value_t value); jerry_value_t jerry_is_arraybuffer_detachable (const jerry_value_t value); jerry_value_t jerry_detach_arraybuffer (const jerry_value_t value); +bool jerry_arraybuffer_has_buffer (const jerry_value_t value); +void jerry_arraybuffer_set_compact_allocation_limit (const jerry_length_t allocation_limit); +void jerry_arraybuffer_set_allocator_callbacks (jerry_arraybuffer_allocate_t allocate_callback, + jerry_arraybuffer_free_t free_callback, + void *user_p); /** * SharedArrayBuffer components. @@ -396,8 +400,7 @@ jerry_value_t jerry_detach_arraybuffer (const jerry_value_t value); bool jerry_value_is_shared_arraybuffer (const jerry_value_t value); jerry_value_t jerry_create_shared_arraybuffer (const jerry_length_t size); jerry_value_t jerry_create_shared_arraybuffer_external (const jerry_length_t size, - uint8_t *buffer_p, - jerry_value_free_callback_t free_cb); + uint8_t *buffer_p, void *buffer_user_p); /** * DataView functions. diff --git a/jerry-core/include/jerryscript-types.h b/jerry-core/include/jerryscript-types.h index 872287112..c17876b41 100644 --- a/jerry-core/include/jerryscript-types.h +++ b/jerry-core/include/jerryscript-types.h @@ -818,6 +818,31 @@ typedef struct uint32_t source_range_length; /**< source length of the function in the source code */ } jerry_source_info_t; +/** + * Array buffer types. + */ + +/** + * Type of an array buffer. + */ +typedef enum +{ + JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER, /**< the object is an array buffer object */ + JERRY_ARRAYBUFFER_TYPE_SHARED_ARRAYBUFFER, /**< the object is a shared array buffer object */ +} jerry_arraybuffer_type_t; + +/** + * Callback for allocating the backing store of array buffer or shared array buffer objects. + */ +typedef uint8_t *(*jerry_arraybuffer_allocate_t) (jerry_arraybuffer_type_t buffer_type, uint32_t buffer_size, + void **arraybuffer_user_p, void *user_p); + +/** + * Callback for freeing the backing store of array buffer or shared array buffer objects. + */ +typedef void (*jerry_arraybuffer_free_t) (jerry_arraybuffer_type_t buffer_type, uint8_t *buffer_p, + uint32_t buffer_size, void *arraybuffer_user_p, void *user_p); + /** * @} */ diff --git a/jerry-core/jcontext/jcontext.h b/jerry-core/jcontext/jcontext.h index 87084e7c6..627988e76 100644 --- a/jerry-core/jcontext/jcontext.h +++ b/jerry-core/jcontext/jcontext.h @@ -197,6 +197,15 @@ struct jerry_context_t #endif /* JERRY_PROMISE_CALLBACK */ #endif /* JERRY_ESNEXT */ +#if JERRY_BUILTIN_TYPEDARRAY + uint32_t arraybuffer_compact_allocation_limit; /**< maximum size of compact allocation */ + jerry_arraybuffer_allocate_t arraybuffer_allocate_callback; /**< callback for allocating + * arraybuffer memory */ + jerry_arraybuffer_free_t arraybuffer_free_callback; /**< callback for freeing arraybuffer memory */ + void *arraybuffer_allocate_callback_user_p; /**< user pointer passed to arraybuffer_allocate_callback + * and arraybuffer_free_callback functions */ +#endif /* JERRY_BUILTIN_TYPEDARRAY */ + #if JERRY_VM_EXEC_STOP uint32_t vm_exec_stop_frequency; /**< reset value for vm_exec_stop_counter */ uint32_t vm_exec_stop_counter; /**< down counter for reducing the calls of vm_exec_stop_cb */ diff --git a/tests/unit-core/test-arraybuffer.c b/tests/unit-core/test-arraybuffer.c index a10c44be8..687625f47 100644 --- a/tests/unit-core/test-arraybuffer.c +++ b/tests/unit-core/test-arraybuffer.c @@ -151,21 +151,61 @@ static void test_write_with_offset (uint8_t offset) /**< offset for buffer write jerry_release_value (arraybuffer); } /* test_write_with_offset */ -static bool callback_called = false; -static bool detach_free_callback_called = false; +static int allocate_mode = 0; +static int allocate_count = 0; +static int free_count = 0; -static void test_free_cb (void *buffer) /**< buffer to free (if needed) */ +static uint8_t * +test_allocate_cb (jerry_arraybuffer_type_t buffer_type, /**< type of the array buffer object */ + uint32_t buffer_size, /**< size of the requested buffer */ + void **buffer_user_p, /**< [in/out] user pointer assigned to the array buffer object */ + void *user_p) /**< user pointer passed to jerry_arraybuffer_set_allocation_callbacks */ { - (void) buffer; - callback_called = true; + TEST_ASSERT (buffer_type == JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER); + TEST_ASSERT (user_p == (void *) &allocate_mode); + + if (*buffer_user_p != NULL) + { + TEST_ASSERT (*buffer_user_p == (void *) &allocate_count); + TEST_ASSERT (buffer_size == 20); + allocate_count++; + *buffer_user_p = (void *) &free_count; + } + else + { + *buffer_user_p = (void *) &allocate_mode; + } + return (uint8_t *) malloc (buffer_size); +} /* test_allocate_cb */ + +static void +test_free_cb (jerry_arraybuffer_type_t buffer_type, /**< type of the array buffer object */ + uint8_t *buffer_p, /**< pointer to the allocated buffer */ + uint32_t buffer_size, /**< size of the allocated buffer */ + void *buffer_user_p, /**< user pointer assigned to the array buffer object */ + void *user_p) /**< user pointer passed to jerry_arraybuffer_set_allocation_callbacks */ +{ + TEST_ASSERT (buffer_type == JERRY_ARRAYBUFFER_TYPE_ARRAYBUFFER); + TEST_ASSERT (user_p == (void *) &allocate_mode); + + if (buffer_user_p == NULL) + { + TEST_ASSERT (buffer_size == 15); + free_count++; + } + else if (buffer_user_p == (void *) &free_count) + { + TEST_ASSERT (buffer_size == 20); + free_count++; + } + else + { + TEST_ASSERT (buffer_user_p == (void *) &allocate_mode); + } + + free (buffer_p); } /* test_free_cb */ -static void test_detach_free_cb (void *buffer) /**< buffer to free */ -{ - free (buffer); - detach_free_callback_called = true; -} /* test_detach_free_cb */ - int main (void) { @@ -178,6 +218,9 @@ main (void) return 0; } + jerry_arraybuffer_set_compact_allocation_limit (4); + jerry_arraybuffer_set_allocator_callbacks (test_allocate_cb, test_free_cb, (void *) &allocate_mode); + jerry_value_t function_val = jerry_create_external_function (assert_handler); register_js_value ("assert", function_val); jerry_release_value (function_val); @@ -278,10 +321,10 @@ main (void) const uint32_t buffer_size = 15; const uint8_t base_value = 51; - JERRY_VLA (uint8_t, buffer_p, buffer_size); + uint8_t *buffer_p = (uint8_t *) malloc (buffer_size); memset (buffer_p, base_value, buffer_size); - jerry_value_t arrayb = jerry_create_arraybuffer_external (buffer_size, buffer_p, test_free_cb); + jerry_value_t arrayb = jerry_create_arraybuffer_external (buffer_size, buffer_p, NULL); uint8_t new_value = 123; jerry_length_t copied = jerry_arraybuffer_write (arrayb, 0, &new_value, 1); TEST_ASSERT (copied == 1); @@ -310,9 +353,8 @@ main (void) /* Test ArrayBuffer external memory map/unmap */ { const uint32_t buffer_size = 20; - JERRY_VLA (uint8_t, buffer_p, buffer_size); - jerry_value_t input_buffer = jerry_create_arraybuffer_external (buffer_size, buffer_p, NULL); + jerry_value_t input_buffer = jerry_create_arraybuffer_external (buffer_size, NULL, (void *) &allocate_count); register_js_value ("input_buffer", input_buffer); jerry_release_value (input_buffer); @@ -370,35 +412,43 @@ main (void) /* Test internal ArrayBuffer detach */ { - const uint32_t length = 1; + const uint32_t length = 4; jerry_value_t arraybuffer = jerry_create_arraybuffer (length); + TEST_ASSERT (jerry_arraybuffer_has_buffer (arraybuffer)); TEST_ASSERT (!jerry_value_is_error (arraybuffer)); TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer)); TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length); jerry_value_t is_detachable = jerry_is_arraybuffer_detachable (arraybuffer); - TEST_ASSERT (!jerry_value_is_error (is_detachable)); TEST_ASSERT (jerry_value_is_true (is_detachable)); - TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length); jerry_release_value (is_detachable); jerry_value_t res = jerry_detach_arraybuffer (arraybuffer); + TEST_ASSERT (!jerry_arraybuffer_has_buffer (arraybuffer)); TEST_ASSERT (!jerry_value_is_error (res)); TEST_ASSERT (jerry_get_arraybuffer_pointer (arraybuffer) == NULL); TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == 0); + is_detachable = jerry_is_arraybuffer_detachable (arraybuffer); + TEST_ASSERT (jerry_value_is_false (is_detachable)); + jerry_release_value (is_detachable); + jerry_release_value (res); jerry_release_value (arraybuffer); } /* Test external ArrayBuffer detach */ { - uint8_t buf[1]; - const uint32_t length = 1; - jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, buf, NULL); + const uint32_t length = 64; + jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, NULL, NULL); TEST_ASSERT (!jerry_value_is_error (arraybuffer)); TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer)); TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length); + TEST_ASSERT (!jerry_arraybuffer_has_buffer (arraybuffer)); + + uint8_t buf[1] = { 1 }; + TEST_ASSERT (jerry_arraybuffer_write (arraybuffer, 0, buf, 1) == 1); + TEST_ASSERT (jerry_arraybuffer_has_buffer (arraybuffer)); jerry_value_t is_detachable = jerry_is_arraybuffer_detachable (arraybuffer); TEST_ASSERT (!jerry_value_is_error (is_detachable)); @@ -414,45 +464,32 @@ main (void) is_detachable = jerry_is_arraybuffer_detachable (arraybuffer); TEST_ASSERT (!jerry_value_is_error (is_detachable)); TEST_ASSERT (!jerry_value_is_true (is_detachable)); + TEST_ASSERT (!jerry_arraybuffer_has_buffer (arraybuffer)); jerry_release_value (is_detachable); jerry_release_value (res); jerry_release_value (arraybuffer); } - /* Test external ArrayBuffer with callback detach */ + /* Test ArrayBuffer created in ECMAScript */ { - const uint32_t length = 8; - uint8_t *buf = (uint8_t *) malloc (length); - jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, buf, test_detach_free_cb); + const jerry_char_t source[] = TEST_STRING_LITERAL ("new ArrayBuffer(64)"); + jerry_value_t arraybuffer = jerry_eval (source, sizeof (source) - 1, JERRY_PARSE_NO_OPTS); TEST_ASSERT (!jerry_value_is_error (arraybuffer)); TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer)); - TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length); + TEST_ASSERT (!jerry_arraybuffer_has_buffer (arraybuffer)); - jerry_value_t is_detachable = jerry_is_arraybuffer_detachable (arraybuffer); - TEST_ASSERT (!jerry_value_is_error (is_detachable)); - TEST_ASSERT (jerry_value_is_true (is_detachable)); - TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length); - jerry_release_value (is_detachable); + uint8_t buf[2] = { 2, 3 }; + TEST_ASSERT (jerry_arraybuffer_write (arraybuffer, 63, buf, 2) == 1); + TEST_ASSERT (jerry_arraybuffer_has_buffer (arraybuffer)); - jerry_value_t res = jerry_detach_arraybuffer (arraybuffer); - TEST_ASSERT (!jerry_value_is_error (res)); - TEST_ASSERT (jerry_get_arraybuffer_pointer (arraybuffer) == NULL); - TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == 0); - TEST_ASSERT (detach_free_callback_called); - - is_detachable = jerry_is_arraybuffer_detachable (arraybuffer); - TEST_ASSERT (!jerry_value_is_error (is_detachable)); - TEST_ASSERT (!jerry_value_is_true (is_detachable)); - jerry_release_value (is_detachable); - - jerry_release_value (res); jerry_release_value (arraybuffer); } jerry_cleanup (); - TEST_ASSERT (callback_called == true); + TEST_ASSERT (allocate_count == 1); + TEST_ASSERT (free_count == 2); return 0; } /* main */ diff --git a/tests/unit-core/test-typedarray.c b/tests/unit-core/test-typedarray.c index 76035e587..623d31a51 100644 --- a/tests/unit-core/test-typedarray.c +++ b/tests/unit-core/test-typedarray.c @@ -36,7 +36,7 @@ typedef struct */ static void register_js_value (const char *name_p, /**< name of the function */ - jerry_value_t value) /**< function callback */ + jerry_value_t value) /**< function callback */ { jerry_value_t global_obj_val = jerry_get_global_object (); @@ -204,14 +204,12 @@ test_typedarray_complex_creation (test_entry_t test_entries[], /**< test cases * { const uint32_t arraybuffer_size = 256; - JERRY_VLA (uint8_t, buffer_ext, arraybuffer_size); - memset (buffer_ext, 0, arraybuffer_size); - for (uint32_t i = 0; test_entries[i].constructor_name != NULL; i++) { const uint32_t offset = 8; uint32_t element_count = test_entries[i].element_count; uint32_t bytes_per_element = test_entries[i].bytes_per_element; + uint8_t *buffer_p = NULL; /* new %TypedArray% (buffer, offset, length); */ jerry_value_t typedarray; @@ -220,7 +218,8 @@ test_typedarray_complex_creation (test_entry_t test_entries[], /**< test cases * if (use_external_buffer) { - arraybuffer = jerry_create_arraybuffer_external (arraybuffer_size, buffer_ext, NULL); + buffer_p = (uint8_t *) jerry_heap_alloc (arraybuffer_size); + arraybuffer = jerry_create_arraybuffer_external (arraybuffer_size, buffer_p, NULL); } else { @@ -290,8 +289,8 @@ test_typedarray_complex_creation (test_entry_t test_entries[], /**< test cases * if (use_external_buffer) { - test_buffer_value (0x11223344, buffer_ext, offset, type, bytes_per_element); - TEST_ASSERT (memcmp (buffer_ext, test_buffer, offset + byte_length) == 0); + test_buffer_value (0x11223344, buffer_p, offset, type, bytes_per_element); + TEST_ASSERT (memcmp (buffer_p, test_buffer, offset + byte_length) == 0); } jerry_release_value (buffer); @@ -504,9 +503,9 @@ test_detached_arraybuffer (void) /* Creating an TypedArray for a detached array buffer with a given length/offset is invalid */ { - uint8_t buf[1]; const uint32_t length = 1; - jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, buf, NULL); + uint8_t *buffer_p = (uint8_t *) jerry_heap_alloc (length); + jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, buffer_p, NULL); TEST_ASSERT (!jerry_value_is_error (arraybuffer)); TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer)); TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length); @@ -533,9 +532,9 @@ test_detached_arraybuffer (void) /* Creating an TypedArray for a detached array buffer without length/offset is valid */ { - uint8_t buf[1]; const uint32_t length = 1; - jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, buf, NULL); + uint8_t *buffer_p = (uint8_t *) jerry_heap_alloc (length); + jerry_value_t arraybuffer = jerry_create_arraybuffer_external (length, buffer_p, NULL); TEST_ASSERT (!jerry_value_is_error (arraybuffer)); TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer)); TEST_ASSERT (jerry_get_arraybuffer_byte_length (arraybuffer) == length);