From d5cd32b0c0e1ff426ea42b4c5675a0c20705cd58 Mon Sep 17 00:00:00 2001 From: Anthony Calandra Date: Tue, 3 Jul 2018 02:19:53 -0400 Subject: [PATCH] Support for %TypedArray%.prototype.set(typedArray [, offset]). (#2405) This patch allows developers to set a typedarray given a source typedarray. This patch attempts to follow section 22.2.3.22.2 in the ECMAScript spec as closely as possible. JerryScript-DCO-1.0-Signed-off-by: AnthonyCalandra anthony@anthony-calandra.com --- .../ecma-builtin-typedarray-prototype.c | 132 ++++++++++++++++-- .../es2015/typedArray-set-with-typedArray.js | 94 +++++++++++++ 2 files changed, 218 insertions(+), 8 deletions(-) create mode 100644 tests/jerry/es2015/typedArray-set-with-typedArray.js 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 b15777a6d..4c6e090a3 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 @@ -646,32 +646,148 @@ ecma_builtin_typedarray_prototype_reverse (ecma_value_t this_arg) /**< this argu return ecma_copy_value (this_arg); } /* ecma_builtin_typedarray_prototype_reverse */ +/** + * The %TypedArray%.prototype object's 'set' routine for a typedArray source + * + * See also: + * ES2015, 22.2.3.22, 22.2.3.22.2 + * + * @return ecma value of undefined if success, error otherwise. + * Returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_op_typedarray_set_with_typedarray (ecma_value_t this_arg, /**< this argument */ + ecma_value_t arr_val, /**< typedarray object */ + ecma_value_t offset_val) /**< offset value */ +{ + /* 6.~ 8. targetOffset */ + ecma_number_t target_offset_num; + if (!ecma_is_value_empty (ecma_get_number (offset_val, &target_offset_num))) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Invalid offset")); + } + + if (ecma_number_is_nan (target_offset_num)) + { + target_offset_num = 0; + } + + if (target_offset_num <= -1.0 || target_offset_num >= (ecma_number_t) UINT32_MAX + 0.5) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Invalid offset")); + } + + ecma_object_t *target_typedarray_p = ecma_get_object_from_value (this_arg); + ecma_object_t *src_typedarray_p = ecma_get_object_from_value (arr_val); + + /* 9. targetBuffer */ + ecma_object_t *target_arraybuffer_p = ecma_typedarray_get_arraybuffer (target_typedarray_p); + lit_utf8_byte_t *target_buffer_p = ecma_typedarray_get_buffer (target_typedarray_p); + + /* 11. targetLength */ + ecma_length_t target_length = ecma_typedarray_get_length (target_typedarray_p); + + /* 12. srcBuffer */ + ecma_object_t *src_arraybuffer_p = ecma_typedarray_get_arraybuffer (src_typedarray_p); + lit_utf8_byte_t *src_buffer_p = ecma_typedarray_get_buffer (src_typedarray_p); + + /* 15. targetType */ + lit_magic_string_id_t target_class_id = ecma_object_get_class_name (target_typedarray_p); + + /* 16. targetElementSize */ + uint8_t target_shift = ecma_typedarray_get_element_size_shift (target_typedarray_p); + uint8_t target_element_size = (uint8_t) (1 << target_shift); + + /* 17. targetByteOffset */ + ecma_length_t target_byte_offset = ecma_typedarray_get_offset (target_typedarray_p); + + /* 19. srcType */ + lit_magic_string_id_t src_class_id = ecma_object_get_class_name (src_typedarray_p); + + /* 20. srcElementSize */ + uint8_t src_shift = ecma_typedarray_get_element_size_shift (src_typedarray_p); + uint8_t src_element_size = (uint8_t) (1 << src_shift); + + /* 21. srcLength */ + ecma_length_t src_length = ecma_typedarray_get_length (src_typedarray_p); + uint32_t src_length_uint32 = ecma_number_to_uint32 (src_length); + + if ((ecma_number_t) src_length_uint32 != src_length) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Invalid source length")); + } + + /* 22. srcByteOffset */ + ecma_length_t src_byte_offset = ecma_typedarray_get_offset (src_typedarray_p); + + /* 23. */ + uint32_t target_offset_uint32 = ecma_number_to_uint32 (target_offset_num); + + if ((int64_t) src_length_uint32 + target_offset_uint32 > target_length) + { + return ecma_raise_range_error (ECMA_ERR_MSG ("Invalid range of index")); + } + + /* 24.d, 25. srcByteIndex */ + ecma_length_t src_byte_index = 0; + + if (src_arraybuffer_p != target_arraybuffer_p) + { + src_byte_index = src_byte_offset; + } + + /* 26. targetByteIndex */ + uint32_t target_byte_index = target_offset_uint32 * target_element_size + target_byte_offset; + + /* 27. limit */ + uint32_t limit = target_byte_index + target_element_size * src_length_uint32; + + if (src_class_id == target_class_id) + { + memmove (target_buffer_p + target_byte_index, src_buffer_p + src_byte_index, + target_element_size * src_length_uint32); + } + else + { + while (target_byte_index < limit) + { + ecma_number_t elem_num = ecma_get_typedarray_element (src_buffer_p + src_byte_index, src_class_id); + ecma_set_typedarray_element (target_buffer_p + target_byte_index, elem_num, target_class_id); + src_byte_index += src_element_size; + target_byte_index += target_element_size; + } + } + + return ECMA_VALUE_UNDEFINED; +} /* ecma_op_typedarray_set_with_typedarray */ + /** * The %TypedArray%.prototype object's 'set' routine * * See also: * ES2015, 22.2.3.22, 22.2.3.22.1 * - * @return ecma value of undefined. + * @return ecma value of undefined if success, error otherwise. + * Returned value must be freed with ecma_free_value. */ ecma_value_t ecma_builtin_typedarray_prototype_set (ecma_value_t this_arg, /**< this argument */ ecma_value_t arr_val, /**< array object */ ecma_value_t offset_val) /**< offset value */ { - /* 1. */ - if (ecma_is_typedarray (arr_val)) - { - /* 22.2.3.22.2 %TypedArray%.prototype(typedArray [, offset ]) is not supported */ - return ecma_raise_type_error (ECMA_ERR_MSG ("TypedArray.set(typedArray [,offset]) is not supported.")); - } - /* 2.~ 4. */ if (!ecma_is_typedarray (this_arg)) { return ecma_raise_type_error (ECMA_ERR_MSG ("Argument 'this' is not a TypedArray.")); } + /* 1. */ + if (ecma_is_typedarray (arr_val)) + { + /* 22.2.3.22.2 */ + return ecma_op_typedarray_set_with_typedarray (this_arg, arr_val, offset_val); + } + /* 6.~ 8. targetOffset */ ecma_value_t ret_val = ECMA_VALUE_EMPTY; ECMA_OP_TO_NUMBER_TRY_CATCH (target_offset_num, offset_val, ret_val); diff --git a/tests/jerry/es2015/typedArray-set-with-typedArray.js b/tests/jerry/es2015/typedArray-set-with-typedArray.js new file mode 100644 index 000000000..2697a8b21 --- /dev/null +++ b/tests/jerry/es2015/typedArray-set-with-typedArray.js @@ -0,0 +1,94 @@ +/* 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. + */ + +var a = new Int32Array([1, 2, 3, 4, 5]); +var b = new Int32Array(5); + +try { + a.set(b, 123456); + assert(1 === 0); // Should not get here. +} catch (e) { + assert(e instanceof RangeError); +} + +b.set(a); +assert(b.join() === '1,2,3,4,5'); +try { + b.set(a, 1); + assert(1 === 0); // Should not get here. +} catch (e) { + assert(e instanceof RangeError); +} + +b.set(new Int32Array([99, 98]), 2); +assert(b.join() === '1,2,99,98,5'); + +b.set(new Int32Array([99, 98, 97]), 2); +assert(b.join() === '1,2,99,98,97'); + +try { + b.set(new Int32Array([99, 98, 97, 96]), 2); + assert(1 === 0); // Should not get here. +} catch (e) { + assert(e instanceof RangeError); +} + +try { + b.set([101, 102, 103, 104], 4); + assert(1 === 0); // Should not get here. +} catch (e) { + assert(e instanceof RangeError); +} + +// ab = [ 0, 1, 2, 3, 4, 5, 6, 7 ] +// a1 = [ ^, ^, ^, ^, ^, ^, ^, ^ ] +// a2 = [ ^, ^, ^, ^ ] +var ab = new ArrayBuffer(8); +var a1 = new Uint8Array(ab); +for (var i = 0; i < a1.length; i += 1) { + a1.set([i], i); +} + +var a2 = new Uint8Array(ab, 4); +a1.set(a2, 2); +assert(a1.join() === '0,1,4,5,6,7,6,7'); +assert(a2.join() === '6,7,6,7'); + +var a3 = new Uint32Array(ab, 4); +a1.set(a3, 2); +assert(a1.join() === '0,1,6,5,6,7,6,7'); +assert(a3.join() === '117835526'); + +var a4 = new Uint8Array(ab, 0, 4); +a1.set(a4, 2); +assert(a1.join() === '0,1,0,1,6,5,6,7'); +assert(a4.join() === '0,1,0,1'); + +var a5 = new Uint32Array(ab, 4, 1); +a1.set(a5, 2); +assert(a1.join() === '0,1,6,1,6,5,6,7'); +assert(a5.join() === '117835014'); + +var c = new Int32Array([0xFFFFFFFF]); +var d = new Uint8Array(4); +d.set(c); +assert(d.join() === '255,0,0,0'); + +var e = new Float32Array([3.33]); +var f = new Uint8Array(1); +f.set(e); +assert(f.join() === '3'); +e.set(f); +assert(e.join() === '3');