diff --git a/docs/02.API-REFERENCE.md b/docs/02.API-REFERENCE.md index 37cb05499a..f41a456007 100644 --- a/docs/02.API-REFERENCE.md +++ b/docs/02.API-REFERENCE.md @@ -296,6 +296,26 @@ typedef jerry_value_t (*jerry_vm_exec_stop_callback_t) (void *user_p); - [jerry_set_vm_exec_stop_callback](#jerry_set_vm_exec_stop_callback) +## jerry_typedarray_type_t + +Enum which describes the TypedArray types. +Possible values: + + - JERRY_TYPEDARRAY_UINT8 - represents the Uint8Array TypedArray + - JERRY_TYPEDARRAY_UINT8CLAMPED - represents the Uint8ClampedArray TypedArray + - JERRY_TYPEDARRAY_INT8 - represents the Int8Array TypedArray + - JERRY_TYPEDARRAY_UINT16 - represents the Uint16Array TypedArray + - JERRY_TYPEDARRAY_INT16 - represents the Int16Array TypedArray + - JERRY_TYPEDARRAY_UINT32 - represents the Uint32Array TypedArray + - JERRY_TYPEDARRAY_INT32 - represents the Int32Array TypedArray + - JERRY_TYPEDARRAY_FLOAT32 - represents the Float32Array TypedArray + - JERRY_TYPEDARRAY_FLOAT64 - represents the Float64Array TypedArray + - JERRY_TYPEDARRAY_INVALID - represents an invalid TypedArray + +API functions can return the `JERRY_TYPEDARRAY_INVALID` value if the +TypedArray support is not in the engine. + + # General engine functions ## jerry_init @@ -1347,6 +1367,45 @@ jerry_value_is_string (const jerry_value_t value) - [jerry_release_value](#jerry_release_value) +## jerry_value_is_typedarray + +**Summary** + +Checks whether the given `jerry_value_t` is a TypedArray object or not. + +**Prototype** + +```c +bool +jerry_value_is_typedarray (const jerry_value_t value) +``` + +- `value` - object to check +- return value + - true, if the given `jerry_value_t` is a TypedArray object. + - false, otherwise + +**Example** + +```c +{ + jerry_value_t value; + ... // create or acquire value + + if (jerry_value_is_typedarray (value)) + { + ... + } + + jerry_release_value (value); +} +``` + +**See also** + +- [jerry_create_typedarray](#jerry_create_typedarray) + + ## jerry_value_is_undefined **Summary** @@ -3129,6 +3188,151 @@ jerry_create_string_sz (const jerry_char_t *str_p, - [jerry_create_string_from_utf8](#jerry_create_string_from_utf8) +## jerry_create_typedarray + +**Summary** + +Create a jerry_value_t representing an TypedArray object. + +For the new object the type of the TypedArray (see: [jerry_typedarray_type_t](#jerry_typedarray_type_t)) +and element count can be specified. + +**Prototype** + +```c +jerry_value_t +jerry_create_typedarray (jerry_typedarray_type_t type_name, jerry_length_t item_count); +``` + +- `type_name` - type of TypedArray to create +- `item_count` - number of items in the new TypedArray +- return value - the new TypedArray as a `jerry_value_t` + +**Example** + +```c +{ + jerry_value_t array = jerry_create_typedarray (JERRY_TYPEDARRAY_UINT16, 15); + + ... // use the TypedArray + + jerry_release_value (array); +} +``` + +**See also** + +- [jerry_typedarray_type_t](#jerry_typedarray_type_t) +- [jerry_value_is_typedarray](#jerry_value_is_typedarray) +- [jerry_release_value](#jerry_release_value) + + +## jerry_create_typedarray_for_arraybuffer + +**Summary** + +Create a jerry_value_t representing an TypedArray object using +an already existing ArrayBuffer object. + +For the new object the type of the TypedArray (see: [jerry_typedarray_type_t](#jerry_typedarray_type_t)) +and element count can be specified. + +The developer must ensure that the ArrayBuffer has the correct length for the given +type of TypedArray otherwise an error is generated. + +The JavaScript equivalent of this function is: `new %TypedArray%(arraybuffer)` where `%TypedArray%` is +one of the allowed TypedArray functions. + +**Prototype** + +```c +jerry_value_t +jerry_create_typedarray_for_arraybuffer (jerry_typedarray_type_t type_name, + const jerry_value_t arraybuffer); +``` + +- `type_name` - type of TypedArray to create +- `arraybuffer` - the ArrayBuffer to use for the new TypedArray +- return value + - the new TypedArray as a `jerry_value_t` + - Error if the ArrayBuffer does not have enough space for the given type of TypedArray + +**Example** + +```c +{ + jerry_value_t buffer = jerry_create_array_buffer (12 * 2); + jerry_value_t array = jerry_create_typedarray_for_arraybuffer (JERRY_TYPEDARRAY_UINT16, buffer); + jerry_release_value (buffer); + + ... // use the TypedArray + + jerry_release_value (array); +} +``` + +**See also** + +- [jerry_typedarray_type_t](#jerry_typedarray_type_t) +- [jerry_value_is_typedarray](#jerry_value_is_typedarray) +- [jerry_release_value](#jerry_release_value) + + +## jerry_create_typedarray_for_arraybuffer_sz + +**Summary** + +Create a jerry_value_t representing an TypedArray object using +an already existing ArrayBuffer object and by specifying the byteOffset, and length properties. + +For the new object the type of the TypedArray (see: [jerry_typedarray_type_t](#jerry_typedarray_type_t)) +and element count can be specified. + +The developer must ensure that the ArrayBuffer has the correct length for the given +type of TypedArray otherwise an error is generated. + +The JavaScript equivalent of this function is: `new %TypedArray%(arraybuffer, byteOffset, length)` where `%TypedArray%` is +one of the allowed TypedArray functions. + +**Prototype** + +```c +jerry_value_t +jerry_create_typedarray_for_arraybuffer_sz (jerry_typedarray_type_t type_name, + const jerry_value_t arraybuffer, + jerry_length_t byte_offset, + jerry_length_t length); +``` + +- `type_name` - type of TypedArray to create +- `arraybuffer` - the ArrayBuffer to use for the new TypedArray +- `byte_offset` - start offset to use for the ArrayBuffer +- `length` - number of elements to used from the ArrayBuffer (this is not the same as the byteLength) +- return value + - the new TypedArray as a `jerry_value_t` + - Error if the ArrayBuffer does not have enough space for the given type of TypedArray + +**Example** + +```c +{ + jerry_value_t buffer = jerry_create_array_buffer (12 * 2); + jerry_value_t array = jerry_create_typedarray_for_arraybuffer_sz (JERRY_TYPEDARRAY_UINT16, buffer, 4, 10); + jerry_release_value (buffer); + + ... // use the TypedArray + + jerry_release_value (array); +} +``` + +**See also** + +- [jerry_typedarray_type_t](#jerry_typedarray_type_t) +- [jerry_value_is_typedarray](#jerry_value_is_typedarray) +- [jerry_release_value](#jerry_release_value) + + ## jerry_create_undefined **Summary** @@ -5123,3 +5327,134 @@ jerry_get_arraybuffer_pointer (const jerry_value_t value); **See also** - [jerry_create_arraybuffer_external](#jerry_create_arraybuffer_external) + + +## jerry_get_typedarray_type + +**Summary** + +Get the type of the TypedArray. + +The returned type is one of the [jerry_typedarray_type_t](#jerry_typedarray_type_t) +enum value. + +**Prototype** + +```c +jerry_typedarray_type_t +jerry_get_typedarray_type (jerry_value_t value); +``` + +- `value` - TypedArray object to query for type. +- return + - the type of the TypedArray + - JERRY_TYPEDARRAY_INVALID if the object was not a TypedArray + +**Example** + +```c +{ + jerry_typedarray_type_t expected_type = JERRY_TYPEDARRAY_UINT32; + jerry_value_t typedarray = jerry_create_typedarray (expected_klass, 25); + + jerry_typedarray_type_t type = jerry_get_typedarray_type (typedarray); + + // 'type' is now JERRY_TYPEDARRAY_UINT32 + + jerry_release_value (typedarray); +} +``` + +**See also** + +- [jerry_create_typedarray](#jerry_create_typedarray) +- [jerry_typedarray_type_t](#jerry_typedarray_type_t) + + +## jerry_get_typedarray_length + +**Summary** + +Get the element count of the TypedArray as specified during creation. + +This is not the same as the byteLength property of a TypedArray object. + +**Prototype** + +``` +jerry_length_t +jerry_get_typedarray_length (jerry_value_t value); +``` + +- `value` - TypedArray object to query +- return + - length (element count) of the TypedArray object + - 0 if the object is not a TypedArray + +**Example** + +```c +{ + jerry_value_t array = jerry_create_typedarray (JERRY_TYPEDARRAY_INT32, 21); + + jerry_length_t element_count = jerry_get_typedarray_length (array); + + // element_count is now 21. + + jerry_release_value (array); +} +``` + +**See also** + +- [jerry_create_typedarray](#jerry_create_typedarray) + + +## jerry_get_typedarray_buffer + +**Summary** + +Get the ArrayBuffer object used by a TypedArray object. +Additionally returns the byteLength and byteOffset properties +of the TypedArray object. + +For the returned ArrayBuffer the [jerry_release_value](#jerry_release_value) +must be called. + +**Prototype** + +```c +jerry_value_t jerry_get_typedarray_buffer (jerry_value_t value, + jerry_length_t *byteOffset, + jerry_length_t *byteLength); +``` + +- `value` - TypedArray to get the ArrayBuffer from +- `byteOffset` - (Optional) returns the start offset of the ArrayBuffer for the TypedArray +- `byteLength` - (Optional) returns the number of bytes used from the ArrayBuffer for the TypedArray +- return + - TypedArray object's underlying ArrayBuffer object + - TypeError if the `value` is not a TypedArray object + +**Example** + +```c +{ + jerry_value_t array = jerry_create_typedarray (JERRY_TYPEDARRAY_INT16, 11); + + jerry_length_t byteLength = 0; + jerry_length_t byteOffset = 0; + jerry_value_t buffer = jerry_get_typedarray_buffer (array, &byteOffset, &byteLength); + + // buffer is an ArrayBuffer object and ArrayBuffer operations can be performed on it + // byteLength is 11 * 2 (2 as the TypedArray stores Int16 that is 2 byte elements) + // byteOffset is 0 + + jerry_release_value (buffer); + jerry_release_value (array); +} +``` + +**See also** + +- [jerry_create_typedarray](#jerry_create_typedarray) diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index ae67584b90..0430c5ef9a 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -32,6 +32,7 @@ #include "ecma-objects.h" #include "ecma-objects-general.h" #include "ecma-promise-object.h" +#include "ecma-typedarray-object.h" #include "jcontext.h" #include "jerryscript.h" #include "jmem.h" @@ -2936,6 +2937,321 @@ jerry_get_arraybuffer_pointer (const jerry_value_t value) /**< Array Buffer to u return NULL; } /* jerry_get_arraybuffer_pointer */ + +/** + * TypedArray related functions + */ + +/** + * Check if the given value is a TypedArray object. + * + * @return true - if it is a TypedArray object + * false - otherwise + */ +bool +jerry_value_is_typedarray (jerry_value_t value) /**< value to check if it is a TypedArray */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN + jerry_value_t array = jerry_get_arg_value (value); + return ecma_is_typedarray (array); +#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + JERRY_UNUSED (value); + return false; +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ +} /* jerry_value_is_typedarray */ + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN +typedef struct +{ + jerry_typedarray_type_t api_type; + ecma_builtin_id_t prototype_id; + lit_magic_string_id_t lit_id; + uint8_t element_size_shift; +} jerry_typedarray_mapping_t; + +static jerry_typedarray_mapping_t jerry_typedarray_mappings[] = +{ +#define TYPEDARRAY_ENTRY(NAME, LIT_NAME, SIZE_SHIFT) \ + { JERRY_TYPEDARRAY_ ## NAME, ECMA_BUILTIN_ID_ ## NAME ## ARRAY_PROTOTYPE, \ + LIT_MAGIC_STRING_ ## LIT_NAME ## _ARRAY_UL, SIZE_SHIFT } + + TYPEDARRAY_ENTRY (UINT8, UINT8, 0), + TYPEDARRAY_ENTRY (UINT8CLAMPED, UINT8_CLAMPED, 0), + TYPEDARRAY_ENTRY (INT8, INT8, 0), + TYPEDARRAY_ENTRY (UINT16, UINT16, 1), + TYPEDARRAY_ENTRY (INT16, INT16, 1), + TYPEDARRAY_ENTRY (UINT32, UINT32, 2), + TYPEDARRAY_ENTRY (INT32, INT32, 2), + TYPEDARRAY_ENTRY (FLOAT32, FLOAT32, 2), +#if CONFIG_ECMA_NUMBER_TYPE == CONFIG_ECMA_NUMBER_FLOAT64 + TYPEDARRAY_ENTRY (FLOAT64, FLOAT64, 3), +#endif + +#undef TYPEDARRAY_ENTRY +}; + +/** + * Helper function to get the TypedArray prototype, literal id, and element size shift + * information. + * + * @return true - if the TypedArray information was found + * false - if there is no such TypedArray type + */ +static bool +jerry_typedarray_find_by_type (jerry_typedarray_type_t type_name, /**< type of the TypedArray */ + ecma_builtin_id_t *prototype_id, /**< [out] found prototype object id */ + lit_magic_string_id_t *lit_id, /**< [out] found literal id */ + uint8_t *element_size_shift) /**< [out] found element size shift value */ +{ + JERRY_ASSERT (prototype_id != NULL); + JERRY_ASSERT (lit_id != NULL); + JERRY_ASSERT (element_size_shift != NULL); + + for (uint32_t i = 0; i < sizeof (jerry_typedarray_mappings) / sizeof (jerry_typedarray_mappings[0]); i++) + { + if (type_name == jerry_typedarray_mappings[i].api_type) + { + *prototype_id = jerry_typedarray_mappings[i].prototype_id; + *lit_id = jerry_typedarray_mappings[i].lit_id; + *element_size_shift = jerry_typedarray_mappings[i].element_size_shift; + return true; + } + } + + return false; +} /* jerry_typedarray_find_by_type */ + +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + +/** + * Create a TypedArray object with a given type and length. + * + * Notes: + * * returns TypeError if an incorrect type (type_name) is specified. + * * byteOffset property will be set to 0. + * * byteLength property will be a multiple of the length parameter (based on the type). + * + * @return - new TypedArray object + */ +jerry_value_t +jerry_create_typedarray (jerry_typedarray_type_t type_name, /**< type of TypedArray to create */ + jerry_length_t length) /**< element count of the new TypedArray */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN + ecma_builtin_id_t prototype_id = 0; + lit_magic_string_id_t lit_id = 0; + uint8_t element_size_shift = 0; + + if (!jerry_typedarray_find_by_type (type_name, &prototype_id, &lit_id, &element_size_shift)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("incorrect type for TypedArray."))); + } + + ecma_object_t *prototype_obj_p = ecma_builtin_get (prototype_id); + + ecma_value_t array_value = ecma_typedarray_create_object_with_length (length, + prototype_obj_p, + element_size_shift, + lit_id); + ecma_deref_object (prototype_obj_p); + + JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (array_value)); + + return array_value; +#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + JERRY_UNUSED (type_name); + JERRY_UNUSED (length); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("TypedArray not supported."))); +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ +} /* jerry_create_typedarray */ + +/** + * Create a TypedArray object using the given arraybuffer and size information. + * + * Notes: + * * returns TypeError if an incorrect type (type_name) is specified. + * * this is the 'new %TypedArray%(arraybuffer, byteOffset, length)' equivalent call. + * + * @return - new TypedArray object + */ +jerry_value_t +jerry_create_typedarray_for_arraybuffer_sz (jerry_typedarray_type_t type_name, /**< type of TypedArray to create */ + const jerry_value_t arraybuffer, /**< ArrayBuffer to use */ + jerry_length_t byte_offset, /**< offset for the ArrayBuffer */ + jerry_length_t length) /**< number of elements to use from ArrayBuffer */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN + ecma_builtin_id_t prototype_id = 0; + lit_magic_string_id_t lit_id = 0; + uint8_t element_size_shift = 0; + + if (!jerry_typedarray_find_by_type (type_name, &prototype_id, &lit_id, &element_size_shift)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("incorrect type for TypedArray."))); + } + + jerry_value_t buffer = jerry_get_arg_value (arraybuffer); + if (!ecma_is_arraybuffer (buffer)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not an ArrayBuffer"))); + } + + ecma_object_t *prototype_obj_p = ecma_builtin_get (prototype_id); + ecma_value_t arguments_p[3] = + { + arraybuffer, + ecma_make_uint32_value (byte_offset), + ecma_make_uint32_value (length) + }; + + ecma_value_t array_value = ecma_op_create_typedarray (arguments_p, 3, prototype_obj_p, element_size_shift, lit_id); + ecma_free_value (arguments_p[1]); + ecma_free_value (arguments_p[2]); + ecma_deref_object (prototype_obj_p); + + return jerry_return (array_value); +#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + JERRY_UNUSED (type_name); + JERRY_UNUSED (arraybuffer); + JERRY_UNUSED (byte_offset); + JERRY_UNUSED (length); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("TypedArray not supported."))); +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ +} /* jerry_create_typedarray_for_arraybuffer_sz */ + +/** + * Create a TypedArray object using the given arraybuffer and size information. + * + * Notes: + * * returns TypeError if an incorrect type (type_name) is specified. + * * this is the 'new %TypedArray%(arraybuffer)' equivalent call. + * + * @return - new TypedArray object + */ +jerry_value_t +jerry_create_typedarray_for_arraybuffer (jerry_typedarray_type_t type_name, /**< type of TypedArray to create */ + const jerry_value_t arraybuffer) /**< ArrayBuffer to use */ +{ + jerry_assert_api_available (); + jerry_length_t byteLength = jerry_get_arraybuffer_byte_length (arraybuffer); + return jerry_create_typedarray_for_arraybuffer_sz (type_name, arraybuffer, 0, byteLength); +} /* jerry_create_typedarray_for_arraybuffer */ + +/** + * Get the type of the TypedArray. + * + * @return - type of the TypedArray + * - JERRY_TYPEDARRAY_INVALID if the argument is not a TypedArray + */ +jerry_typedarray_type_t +jerry_get_typedarray_type (jerry_value_t value) /**< object to get the TypedArray type */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN + jerry_value_t array = jerry_get_arg_value (value); + if (!ecma_is_typedarray (array)) + { + return JERRY_TYPEDARRAY_INVALID; + } + + ecma_object_t *array_p = ecma_get_object_from_value (array); + + lit_magic_string_id_t class_name_id = ecma_object_get_class_name (array_p); + for (uint32_t i = 0; i < sizeof (jerry_typedarray_mappings) / sizeof (jerry_typedarray_mappings[0]); i++) + { + if (class_name_id == jerry_typedarray_mappings[i].lit_id) + { + return jerry_typedarray_mappings[i].api_type; + } + } +#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + JERRY_UNUSED (value); +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + + return JERRY_TYPEDARRAY_INVALID; +} /* jerry_get_typedarray_type */ + +/** + * Get the element count of the TypedArray. + * + * @return length of the TypedArray. + */ +jerry_length_t +jerry_get_typedarray_length (jerry_value_t value) /**< TypedArray to query */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN + jerry_value_t array = jerry_get_arg_value (value); + if (ecma_is_typedarray (array)) + { + ecma_object_t *array_p = ecma_get_object_from_value (array); + return ecma_typedarray_get_length (array_p); + } +#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + JERRY_UNUSED (value); +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + + return 0; +} /* jerry_get_typedarray_length */ + +/** + * Get the underlying ArrayBuffer from a TypedArray. + * + * Additionally the byteLength and byteOffset properties are also returned + * which were specified when the TypedArray was created. + * + * Note: + * the returned value must be freed with a jerry_release_value call + * + * @return ArrayBuffer of a TypedArray + * TypeError if the object is not a TypedArray. + */ +jerry_value_t +jerry_get_typedarray_buffer (jerry_value_t value, /**< TypedArray to get the arraybuffer from */ + jerry_length_t *byte_offset, /**< [out] byteOffset property */ + jerry_length_t *byte_length) /**< [out] byteLength property */ +{ + jerry_assert_api_available (); + +#ifndef CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN + jerry_value_t array = jerry_get_arg_value (value); + if (!ecma_is_typedarray (array)) + { + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("Object is not a TypedArray."))); + } + + ecma_object_t *array_p = ecma_get_object_from_value (array); + uint8_t shift = ecma_typedarray_get_element_size_shift (array_p); + + if (byte_length != NULL) + { + *byte_length = (jerry_length_t) (ecma_typedarray_get_length (array_p) << shift); + } + + if (byte_offset != NULL) + { + *byte_offset = (jerry_length_t) ecma_typedarray_get_offset (array_p); + } + + ecma_object_t *arraybuffer_p = ecma_typedarray_get_arraybuffer (array_p); + ecma_ref_object (arraybuffer_p); + return jerry_return (ecma_make_object_value (arraybuffer_p)); +#else /* CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ + JERRY_UNUSED (value); + JERRY_UNUSED (byte_length); + JERRY_UNUSED (byte_offset); + return jerry_throw (ecma_raise_type_error (ECMA_ERR_MSG ("TypedArray is not supported."))); +#endif /* !CONFIG_DISABLE_ES2015_TYPEDARRAY_BUILTIN */ +} /* jerry_get_typedarray_buffer */ + /** * @} */ diff --git a/jerry-core/ecma/operations/ecma-typedarray-object.c b/jerry-core/ecma/operations/ecma-typedarray-object.c index 29ca06ce1a..f3f3921a18 100644 --- a/jerry-core/ecma/operations/ecma-typedarray-object.c +++ b/jerry-core/ecma/operations/ecma-typedarray-object.c @@ -226,7 +226,7 @@ ecma_set_typedarray_element (lit_utf8_byte_t *dst_p, /**< the location in the in * @return ecma value of the new typedarray object * Returned value must be freed with ecma_free_value */ -static ecma_value_t +ecma_value_t ecma_typedarray_create_object_with_length (ecma_length_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 */ diff --git a/jerry-core/ecma/operations/ecma-typedarray-object.h b/jerry-core/ecma/operations/ecma-typedarray-object.h index 078b5f939e..68e75dffbd 100644 --- a/jerry-core/ecma/operations/ecma-typedarray-object.h +++ b/jerry-core/ecma/operations/ecma-typedarray-object.h @@ -57,6 +57,10 @@ bool ecma_op_typedarray_define_index_prop (ecma_object_t *obj_p, bool ecma_op_typedarray_set_index_prop (ecma_object_t *obj_p, uint32_t index, ecma_value_t value); ecma_value_t ecma_op_create_typedarray_with_type_and_length (ecma_object_t *obj_p, ecma_length_t array_length); +ecma_value_t ecma_typedarray_create_object_with_length (ecma_length_t array_length, + ecma_object_t *proto_p, + uint8_t element_size_shift, + lit_magic_string_id_t class_id); /** * @} diff --git a/jerry-core/include/jerryscript-core.h b/jerry-core/include/jerryscript-core.h index 4ee692cbbc..e3d15d7475 100644 --- a/jerry-core/include/jerryscript-core.h +++ b/jerry-core/include/jerryscript-core.h @@ -462,6 +462,43 @@ jerry_length_t jerry_arraybuffer_read (const jerry_value_t value, jerry_length_t jerry_get_arraybuffer_byte_length (const jerry_value_t value); uint8_t *jerry_get_arraybuffer_pointer (const jerry_value_t value); + +/** + * TypedArray functions. + */ + +/** + * TypedArray types. + */ +typedef enum +{ + JERRY_TYPEDARRAY_INVALID = 0, + JERRY_TYPEDARRAY_UINT8, + JERRY_TYPEDARRAY_UINT8CLAMPED, + JERRY_TYPEDARRAY_INT8, + JERRY_TYPEDARRAY_UINT16, + JERRY_TYPEDARRAY_INT16, + JERRY_TYPEDARRAY_UINT32, + JERRY_TYPEDARRAY_INT32, + JERRY_TYPEDARRAY_FLOAT32, + JERRY_TYPEDARRAY_FLOAT64, +} jerry_typedarray_type_t; + + +bool jerry_value_is_typedarray (jerry_value_t value); +jerry_value_t jerry_create_typedarray (jerry_typedarray_type_t type_name, jerry_length_t length); +jerry_value_t jerry_create_typedarray_for_arraybuffer_sz (jerry_typedarray_type_t type_name, + const jerry_value_t arraybuffer, + jerry_length_t byte_offset, + jerry_length_t length); +jerry_value_t jerry_create_typedarray_for_arraybuffer (jerry_typedarray_type_t type_name, + const jerry_value_t arraybuffer); +jerry_typedarray_type_t jerry_get_typedarray_type (jerry_value_t value); +jerry_length_t jerry_get_typedarray_length (jerry_value_t value); +jerry_value_t jerry_get_typedarray_buffer (jerry_value_t value, + jerry_length_t *byte_offset, + jerry_length_t *byte_length); + /** * @} */ diff --git a/tests/unit-core/test-typedarray.c b/tests/unit-core/test-typedarray.c new file mode 100644 index 0000000000..2337b0bbd9 --- /dev/null +++ b/tests/unit-core/test-typedarray.c @@ -0,0 +1,475 @@ +/* Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "jerryscript.h" +#include "jerryscript-port.h" +#include "jerryscript-port-default.h" +#include "test-common.h" + +#include + +/** + * Type to describe test cases. + */ +typedef struct +{ + jerry_typedarray_type_t typedarray_type; /**< what kind of TypedArray */ + char *constructor_name; /**< JS constructor name for TypedArray */ + uint32_t element_count; /**< number of elements for the TypedArray */ + uint32_t bytes_per_element; /**< bytes per elment of the given typedarray_type */ +} test_entry_t; + +/** + * Register a JavaScript value in the global object. + */ +static void +register_js_value (const char *name_p, /**< name of the function */ + jerry_value_t value) /**< function callback */ +{ + jerry_value_t global_obj_val = jerry_get_global_object (); + + jerry_value_t name_val = jerry_create_string ((const jerry_char_t *) name_p); + jerry_value_t result_val = jerry_set_property (global_obj_val, name_val, value); + + jerry_release_value (name_val); + jerry_release_value (global_obj_val); + + jerry_release_value (result_val); +} /* register_js_value */ + +static jerry_value_t +assert_handler (const jerry_value_t func_obj_val, /**< function object */ + const jerry_value_t this_val, /**< this arg */ + const jerry_value_t args_p[], /**< function arguments */ + const jerry_length_t args_cnt) /**< number of function arguments */ +{ + JERRY_UNUSED (func_obj_val); + JERRY_UNUSED (this_val); + + if (jerry_value_is_boolean (args_p[0]) + && jerry_get_boolean_value (args_p[0])) + { + return jerry_create_boolean (true); + } + else + { + if (args_cnt > 1 + && jerry_value_is_string (args_p[1])) + { + jerry_length_t utf8_sz = jerry_get_string_size (args_p[1]); + char string_from_utf8[utf8_sz]; + string_from_utf8[utf8_sz] = 0; + + jerry_string_to_char_buffer (args_p[1], (jerry_char_t *) string_from_utf8, utf8_sz); + + printf ("JS assert: %s\n", string_from_utf8); + } + TEST_ASSERT (false); + } +} /* assert_handler */ + +/** + * Checks whether global object has typedarray. + */ +static bool +typedarray_is_available (void) +{ + jerry_value_t global_obj_val = jerry_get_global_object (); + jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) "Int8Array"); + + jerry_value_t prop_value = jerry_has_property (global_obj_val, prop_name); + bool has_prop = jerry_get_boolean_value (prop_value); + + jerry_release_value (global_obj_val); + jerry_release_value (prop_name); + jerry_release_value (prop_value); + + return has_prop; +} /* typedarray_is_available */ + +/** + * Do simple TypedArray property validation. + */ +static void +test_typedarray_info (jerry_value_t typedarray, /**< target TypedArray to query */ + jerry_typedarray_type_t typedarray_type, /**< expected TypedArray type */ + jerry_length_t element_count, /**< expected element count */ + jerry_length_t bytes_per_element) /**< bytes per element for the given type */ +{ + TEST_ASSERT (!jerry_value_has_error_flag (typedarray)); + TEST_ASSERT (jerry_value_is_typedarray (typedarray)); + TEST_ASSERT (jerry_get_typedarray_type (typedarray) == typedarray_type); + TEST_ASSERT (jerry_get_typedarray_length (typedarray) == element_count); + + jerry_length_t byte_length = (uint32_t) -1; + jerry_length_t byte_offset = (uint32_t) -1; + jerry_value_t arraybuffer = jerry_get_typedarray_buffer (typedarray, &byte_offset, &byte_length); + TEST_ASSERT (jerry_value_is_arraybuffer (arraybuffer)); + + TEST_ASSERT (byte_length == element_count * bytes_per_element); + TEST_ASSERT (byte_offset == 0); + + jerry_release_value (arraybuffer); +} /* test_typedarray_info */ + +/** + * Test construction of TypedArrays and validate properties. + */ +static void +test_typedarray_queries (test_entry_t test_entries[]) /**< test cases */ +{ + jerry_value_t global_obj_val = jerry_get_global_object (); + + for (uint32_t i = 0; test_entries[i].constructor_name != NULL; i++) + { + /* Create TypedArray via construct call */ + { + jerry_value_t prop_name = jerry_create_string ((const jerry_char_t *) test_entries[i].constructor_name); + jerry_value_t prop_value = jerry_get_property (global_obj_val, prop_name); + TEST_ASSERT (!jerry_value_has_error_flag (prop_value)); + jerry_value_t length_arg = jerry_create_number (test_entries[i].element_count); + + jerry_value_t typedarray = jerry_construct_object (prop_value, &length_arg, 1); + + jerry_release_value (prop_name); + jerry_release_value (prop_value); + jerry_release_value (length_arg); + + test_typedarray_info (typedarray, + test_entries[i].typedarray_type, + test_entries[i].element_count, + test_entries[i].bytes_per_element); + jerry_release_value (typedarray); + } + + /* Create TypedArray via api call */ + { + jerry_value_t typedarray = jerry_create_typedarray (test_entries[i].typedarray_type, + test_entries[i].element_count); + test_typedarray_info (typedarray, + test_entries[i].typedarray_type, + test_entries[i].element_count, + test_entries[i].bytes_per_element); + jerry_release_value (typedarray); + } + } + + jerry_release_value (global_obj_val); +} /* test_typedarray_queries */ + +/** + * Test value at given position in the buffer based on TypedArray type. + */ +static +void test_buffer_value (uint64_t value, /**< value to test for */ + const uint8_t *buffer, /**< buffer to read value from */ + uint32_t start_offset, /**< start offset of the value */ + jerry_typedarray_type_t typedarray_type, /**< type of TypedArray */ + uint32_t bytes_per_element) /**< bytes per element for the given type */ +{ + uint32_t offset = start_offset / bytes_per_element; + +#define TEST_VALUE_AT(TYPE, BUFFER, OFFSET, VALUE) TEST_ASSERT (((TYPE *) BUFFER)[OFFSET] == (TYPE) (VALUE)) + + switch (typedarray_type) + { + case JERRY_TYPEDARRAY_UINT8: TEST_VALUE_AT (uint8_t, buffer, offset, value); break; + case JERRY_TYPEDARRAY_INT8: TEST_VALUE_AT (int8_t, buffer, offset, value); break; + case JERRY_TYPEDARRAY_UINT16: TEST_VALUE_AT (uint16_t, buffer, offset, value); break; + case JERRY_TYPEDARRAY_INT16: TEST_VALUE_AT (int16_t, buffer, offset, value); break; + case JERRY_TYPEDARRAY_UINT32: TEST_VALUE_AT (uint32_t, buffer, offset, value); break; + case JERRY_TYPEDARRAY_INT32: TEST_VALUE_AT (int32_t, buffer, offset, value); break; + case JERRY_TYPEDARRAY_FLOAT32: TEST_VALUE_AT (float, buffer, offset, value); break; + case JERRY_TYPEDARRAY_FLOAT64: TEST_VALUE_AT (double, buffer, offset, value); break; + + case JERRY_TYPEDARRAY_UINT8CLAMPED: + { + int64_t signed_value = (int64_t) value; + uint8_t expected = (uint8_t) value; + + /* clamp the value if required*/ + if (signed_value > 0xFF) + { + expected = 0xFF; + } + else if (signed_value < 0) + { + expected = 0; + } + + TEST_VALUE_AT (uint8_t, buffer, offset, expected); break; + } + default: TEST_ASSERT (false); break; + } + +#undef TEST_VALUE_AT +} /* test_buffer_value */ + +static void +test_typedarray_complex_creation (test_entry_t test_entries[], /**< test cases */ + bool use_external_buffer) /**< run tests using arraybuffer with external memory */ +{ + const uint32_t arraybuffer_size = 256; + + 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; + + /* new %TypedArray% (buffer, offset, length); */ + jerry_value_t typedarray; + { + jerry_value_t arraybuffer; + + if (use_external_buffer) + { + arraybuffer = jerry_create_arraybuffer_external (arraybuffer_size, buffer_ext, NULL); + } + else + { + arraybuffer = jerry_create_arraybuffer (arraybuffer_size); + } + + jerry_value_t js_offset = jerry_create_number (offset); + jerry_value_t js_element_count = jerry_create_number (element_count); + + register_js_value ("expected_offset", js_offset); + register_js_value ("expected_length", js_element_count); + + typedarray = jerry_create_typedarray_for_arraybuffer_sz (test_entries[i].typedarray_type, + arraybuffer, + offset, + element_count); + TEST_ASSERT (!jerry_value_has_error_flag (typedarray)); + + jerry_release_value (js_offset); + jerry_release_value (js_element_count); + jerry_release_value (arraybuffer); + } + + register_js_value ("array", typedarray); + + const char *eval_src_p = ( + "assert (array.length == expected_length," + " 'expected length: ' + expected_length + ' got: ' + array.length);" + "assert (array.byteOffset == expected_offset);" + "array[0] = 0x11223344;"); + jerry_value_t result = jerry_eval ((jerry_char_t *) eval_src_p, + strlen (eval_src_p), + true); + TEST_ASSERT (!jerry_value_has_error_flag (result)); + jerry_release_value (result); + + { + jerry_length_t byte_length = 0; + jerry_length_t byte_offset = 0; + jerry_value_t buffer = jerry_get_typedarray_buffer (typedarray, &byte_offset, &byte_length); + TEST_ASSERT (byte_length == element_count * bytes_per_element); + TEST_ASSERT (byte_offset == offset); + + uint8_t test_buffer[arraybuffer_size]; + + jerry_typedarray_type_t type = jerry_get_typedarray_type (typedarray); + jerry_value_t read_count = jerry_arraybuffer_read (buffer, 0, test_buffer, offset + byte_length); + TEST_ASSERT (read_count == offset + byte_length); + test_buffer_value (0x11223344, test_buffer, offset, type, bytes_per_element); + + 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); + } + + jerry_release_value (buffer); + } + + jerry_release_value (typedarray); + } +} /* test_typedarray_complex_creation */ + +int +main (void) +{ + jerry_init (JERRY_INIT_EMPTY); + + if (!typedarray_is_available ()) + { + jerry_port_log (JERRY_LOG_LEVEL_ERROR, "TypedArray is disabled!\n"); + jerry_cleanup (); + return 0; + } + + jerry_value_t function_val = jerry_create_external_function (assert_handler); + register_js_value ("assert", function_val); + jerry_release_value (function_val); + + test_entry_t test_entries[] = + { +#define TEST_ENTRY(TYPE, CONSTRUCTOR, COUNT, BYTES_PER_ELEMENT) \ + { TYPE, CONSTRUCTOR, COUNT, BYTES_PER_ELEMENT } + + TEST_ENTRY (JERRY_TYPEDARRAY_UINT8, "Uint8Array", 12, 1), + TEST_ENTRY (JERRY_TYPEDARRAY_UINT8CLAMPED, "Uint8ClampedArray", 12, 1), + TEST_ENTRY (JERRY_TYPEDARRAY_INT8, "Int8Array", 12, 1), + TEST_ENTRY (JERRY_TYPEDARRAY_UINT16, "Uint16Array", 12, 2), + TEST_ENTRY (JERRY_TYPEDARRAY_INT16, "Int16Array", 12, 2), + TEST_ENTRY (JERRY_TYPEDARRAY_UINT16, "Uint16Array", 12, 2), + TEST_ENTRY (JERRY_TYPEDARRAY_INT32, "Int32Array", 12, 4), + TEST_ENTRY (JERRY_TYPEDARRAY_UINT32, "Uint32Array", 12, 4), + TEST_ENTRY (JERRY_TYPEDARRAY_FLOAT32, "Float32Array", 12, 4), + /* TODO: add check if the float64 is supported */ + TEST_ENTRY (JERRY_TYPEDARRAY_FLOAT64, "Float64Array", 12, 8), + + TEST_ENTRY (JERRY_TYPEDARRAY_INVALID, NULL, 0, 0) +#undef TEST_ENTRY + }; + + /* Test TypedArray queries */ + test_typedarray_queries (test_entries); + + /* Test TypedArray operations in js */ + { + const uint32_t element_count = 14; + uint8_t expected_value = 42; + + jerry_value_t array = jerry_create_typedarray (JERRY_TYPEDARRAY_UINT8, element_count); + + { + uint8_t expected_data[element_count]; + memset (expected_data, expected_value, element_count); + + jerry_length_t byte_length; + jerry_length_t offset; + jerry_value_t buffer = jerry_get_typedarray_buffer (array, &offset, &byte_length); + TEST_ASSERT (byte_length == element_count); + jerry_length_t written = jerry_arraybuffer_write (buffer, offset, expected_data, element_count); + TEST_ASSERT (written == element_count); + jerry_release_value (buffer); + + jerry_value_t js_element_count = jerry_create_number (element_count); + jerry_value_t js_expected_value = jerry_create_number (expected_value); + + register_js_value ("array", array); + register_js_value ("expected_length", js_element_count); + register_js_value ("expected_value", js_expected_value); + + jerry_release_value (js_element_count); + jerry_release_value (js_expected_value); + } + + /* Check read and to write */ + const char *eval_src_p = ( + "assert (array.length == expected_length, 'expected length: ' + expected_length + ' got: ' + array.length);" + "for (var i = 0; i < array.length; i++)" + "{" + " assert (array[i] == expected_value);" + " array[i] = i;" + "};"); + jerry_value_t result = jerry_eval ((jerry_char_t *) eval_src_p, + strlen (eval_src_p), + true); + + TEST_ASSERT (!jerry_value_has_error_flag (result)); + jerry_release_value (result); + + /* Check write results */ + { + jerry_length_t byte_length; + jerry_length_t offset; + jerry_value_t buffer = jerry_get_typedarray_buffer (array, &offset, &byte_length); + TEST_ASSERT (byte_length == element_count); + + uint8_t result_data[element_count]; + + jerry_length_t read_count = jerry_arraybuffer_read (buffer, offset, result_data, byte_length); + TEST_ASSERT (read_count == byte_length); + + for (uint8_t i = 0; i < read_count; i++) + { + TEST_ASSERT (result_data[i] == i); + } + + jerry_release_value (buffer); + } + + jerry_release_value (array); + } + + test_typedarray_complex_creation (test_entries, false); + test_typedarray_complex_creation (test_entries, true); + + /* test invalid things */ + { + jerry_value_t values[] = + { + jerry_create_number (11), + jerry_create_boolean (false), + jerry_create_string ((const jerry_char_t *) "test"), + jerry_create_object (), + jerry_create_null (), + jerry_create_arraybuffer (16), + jerry_create_error (JERRY_ERROR_TYPE, (const jerry_char_t *) "error"), + jerry_create_undefined (), + jerry_create_promise (), + }; + + for (size_t idx = 0; idx < sizeof (values) / sizeof (values[0]); idx++) + { + /* A non-TypedArray object should not be regarded a TypedArray. */ + bool is_typedarray = jerry_value_is_typedarray (values[idx]); + TEST_ASSERT (is_typedarray == false); + + /* JERRY_TYPEDARRAY_INVALID should be returned for non-TypedArray objects */ + jerry_typedarray_type_t type = jerry_get_typedarray_type (values[idx]); + TEST_ASSERT (type == JERRY_TYPEDARRAY_INVALID); + + /* Zero should be returned for non-TypedArray objects */ + jerry_length_t length = jerry_get_typedarray_length (values[idx]); + TEST_ASSERT (length == 0); + + /** + * Getting the ArrayBuffer from a non-TypedArray object(s) should return an error + * and should not modify the output parameter values. + */ + { + jerry_length_t offset = 22; + jerry_length_t byte_count = 23; + jerry_value_t error = jerry_get_typedarray_buffer (values[idx], &offset, &byte_count); + TEST_ASSERT (jerry_value_has_error_flag (error)); + TEST_ASSERT (offset == 22); + TEST_ASSERT (byte_count == 23); + jerry_release_value (error); + } + + /** + * Creating a TypedArray from a non-ArrayBuffer should result an error. + */ + if (!jerry_value_is_arraybuffer (values[idx])) + { + jerry_value_t error = jerry_create_typedarray_for_arraybuffer (JERRY_TYPEDARRAY_UINT8, values[idx]); + TEST_ASSERT (jerry_value_has_error_flag (error)); + jerry_release_value (error); + } + + jerry_release_value (values[idx]); + } + } + + jerry_cleanup (); +} /* main */