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 01b58c293..95c31c4b6 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 @@ -736,11 +736,11 @@ ecma_builtin_typedarray_prototype_filter (ecma_value_t this_arg, /**< this argum ecma_value_t call_args[] = { get_value, current_index, this_arg }; + ecma_value_t call_value = ecma_op_function_call (func_object_p, cb_this_arg, call_args, 3); + ecma_fast_free_value (current_index); ecma_fast_free_value (get_value); - ecma_value_t call_value = ecma_op_function_call (func_object_p, cb_this_arg, call_args, 3); - if (ECMA_IS_VALUE_ERROR (call_value)) { goto cleanup; diff --git a/jerry-core/ecma/operations/ecma-typedarray-object.c b/jerry-core/ecma/operations/ecma-typedarray-object.c index e35b750e5..d4e6b66ae 100644 --- a/jerry-core/ecma/operations/ecma-typedarray-object.c +++ b/jerry-core/ecma/operations/ecma-typedarray-object.c @@ -15,6 +15,7 @@ #include +#include "ecma-iterator-object.h" #include "ecma-typedarray-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-function-object.h" @@ -675,7 +676,64 @@ ecma_typedarray_create_object_with_typedarray (ecma_object_t *typedarray_p, /**< } /* ecma_typedarray_create_object_with_typedarray */ /** - * Create a TypedArray object by transforming from an array-like object + * Helper method for ecma_op_typedarray_from + * + * @return ECMA_VALUE_TRUE - if setting the given value to the new typedarray was successful + * ECMA_VALUE_ERROR - otherwise + */ +static ecma_value_t +ecma_op_typedarray_from_helper (ecma_value_t this_val, /**< this_arg for the above from function */ + 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 */ + ecma_typedarray_setter_fn_t setter_cb) /**< setter callback function */ +{ + ecma_value_t mapped_value; + if (func_object_p != NULL) + { + /* 17.d 17.f */ + ecma_value_t current_index = ecma_make_uint32_value (index); + ecma_value_t call_args[] = { current_value, current_index }; + + ecma_value_t cb_value = ecma_op_function_call (func_object_p, this_val, call_args, 2); + + ecma_free_value (current_index); + ecma_free_value (current_value); + + if (ECMA_IS_VALUE_ERROR (cb_value)) + { + return cb_value; + } + + mapped_value = cb_value; + } + else + { + mapped_value = current_value; + } + + ecma_number_t num_var; + ecma_value_t mapped_number = ecma_get_number (mapped_value, &num_var); + ecma_free_value (mapped_value); + + if (ECMA_IS_VALUE_ERROR (mapped_number)) + { + return mapped_number; + } + + if (index >= info_p->length) + { + return ecma_raise_type_error (ECMA_ERR_MSG ("Invalid argument type.")); + } + + setter_cb (info_p->buffer_p + (index << info_p->shift), num_var); + + return ECMA_VALUE_TRUE; +} /* ecma_op_typedarray_from_helper */ + +/** + * Create a TypedArray object by transforming from an array-like object or iterable object * * See also: ES2015 22.2.2.1 * @@ -693,12 +751,130 @@ ecma_op_typedarray_from (ecma_value_t items_val, /**< the source array-like obje /* 3 */ JERRY_ASSERT (ecma_op_is_callable (map_fn_val) || ecma_is_value_undefined (map_fn_val)); + /* 4-5 */ ecma_object_t *func_object_p = NULL; if (!ecma_is_value_undefined (map_fn_val)) { func_object_p = ecma_get_object_from_value (map_fn_val); } + /* 6 */ + ecma_value_t using_iterator = ecma_op_get_method_by_symbol_id (items_val, LIT_GLOBAL_SYMBOL_ITERATOR); + + /* 7 */ + if (ECMA_IS_VALUE_ERROR (using_iterator)) + { + return using_iterator; + } + + /* 8 */ + if (!ecma_is_value_undefined (using_iterator)) + { + /* 8.a */ + ecma_value_t iterator = ecma_op_get_iterator (items_val, using_iterator); + ecma_free_value (using_iterator); + + /* 8.b */ + if (ECMA_IS_VALUE_ERROR (iterator)) + { + return iterator; + } + + /* 8.c */ + ecma_collection_t *values_p = ecma_new_collection (); + ecma_value_t ret_value = ECMA_VALUE_EMPTY; + + /* 8.e */ + while (true) + { + /* 8.e.i */ + ecma_value_t next = ecma_op_iterator_step (iterator); + + /* 8.e.ii */ + if (ECMA_IS_VALUE_ERROR (next)) + { + ret_value = next; + break; + } + + if (next == ECMA_VALUE_FALSE) + { + break; + } + + /* 8.e.iii */ + ecma_value_t next_value = ecma_op_iterator_value (next); + ecma_free_value (next); + + if (ECMA_IS_VALUE_ERROR (next_value)) + { + ret_value = next_value; + break; + } + + ecma_collection_push_back (values_p, next_value); + } + + ecma_free_value (iterator); + + if (ECMA_IS_VALUE_ERROR (ret_value)) + { + ecma_collection_free (values_p); + return ret_value; + } + + /* 8.g */ + ecma_value_t new_typedarray = ecma_typedarray_create_object_with_length (values_p->item_count, + NULL, + proto_p, + element_size_shift, + typedarray_id); + + /* 8.h */ + if (ECMA_IS_VALUE_ERROR (new_typedarray)) + { + ecma_collection_free (values_p); + return new_typedarray; + } + + 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); + + ret_value = ecma_make_object_value (new_typedarray_p); + + /* 8.j */ + for (uint32_t index = 0; index < values_p->item_count; index++) + { + ecma_value_t set_value = ecma_op_typedarray_from_helper (this_val, + values_p->buffer_p[index], + index, + func_object_p, + &info, + setter_cb); + + if (ECMA_IS_VALUE_ERROR (set_value)) + { + for (uint32_t j = index + 1; j < values_p->item_count; j++) + { + ecma_free_value (values_p->buffer_p[j]); + } + + ret_value = set_value; + break; + } + } + + ecma_collection_destroy (values_p); + + if (ECMA_IS_VALUE_ERROR (ret_value)) + { + ecma_deref_object (new_typedarray_p); + } + + return ret_value; + } + /* 10 */ ecma_value_t arraylike_object_val = ecma_op_to_object (items_val); @@ -713,7 +889,6 @@ ecma_op_typedarray_from (ecma_value_t items_val, /**< the source array-like obje uint32_t len; ecma_value_t len_value = ecma_op_object_get_length (arraylike_object_p, &len); - ecma_value_t ret_value = ECMA_VALUE_ERROR; if (ECMA_IS_VALUE_ERROR (len_value)) { ecma_deref_object (arraylike_object_p); @@ -736,71 +911,43 @@ ecma_op_typedarray_from (ecma_value_t items_val, /**< the source array-like obje 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_number_t num_var; + ecma_value_t ret_value = ecma_make_object_value (new_typedarray_p); /* 17 */ for (uint32_t index = 0; index < len; index++) { - /* 17.b */ ecma_value_t current_value = ecma_op_object_find_by_uint32_index (arraylike_object_p, index); if (ECMA_IS_VALUE_ERROR (current_value)) { - goto cleanup; + ret_value = current_value; + break; } if (ecma_is_value_found (current_value)) { - ecma_value_t mapped_value; - if (func_object_p != NULL) + ecma_value_t set_value = ecma_op_typedarray_from_helper (this_val, + current_value, + index, + func_object_p, + &info, + setter_cb); + + if (ECMA_IS_VALUE_ERROR (set_value)) { - /* 17.d 17.f */ - ecma_value_t current_index = ecma_make_uint32_value (index); - ecma_value_t call_args[] = { current_value, current_index }; - - ecma_value_t cb_value = ecma_op_function_call (func_object_p, this_val, call_args, 2); - - ecma_free_value (current_index); - ecma_free_value (current_value); - - if (ECMA_IS_VALUE_ERROR (cb_value)) - { - goto cleanup; - } - - mapped_value = cb_value; + ret_value = set_value; + break; } - else - { - mapped_value = current_value; - } - - ecma_value_t mapped_number = ecma_get_number (mapped_value, &num_var); - ecma_free_value (mapped_value); - - if (ECMA_IS_VALUE_ERROR (mapped_number)) - { - goto cleanup; - } - - if (index >= info.length) - { - ecma_raise_type_error (ECMA_ERR_MSG ("Invalid argument type.")); - goto cleanup; - } - - ecma_length_t byte_pos = (index << info.shift); - setter_cb (info.buffer_p + byte_pos, num_var); } } - ecma_ref_object (new_typedarray_p); - ret_value = ecma_make_object_value (new_typedarray_p); - -cleanup: - ecma_deref_object (new_typedarray_p); ecma_deref_object (arraylike_object_p); + if (ECMA_IS_VALUE_ERROR (ret_value)) + { + ecma_deref_object (new_typedarray_p); + } + return ret_value; } /* ecma_op_typedarray_from */ diff --git a/tests/jerry/es2015/typedarray-from.js b/tests/jerry/es2015/typedarray-from.js new file mode 100644 index 000000000..cab6b9797 --- /dev/null +++ b/tests/jerry/es2015/typedarray-from.js @@ -0,0 +1,157 @@ +// 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 typedArrayConstructors = [ + Uint8Array, + Int8Array, + Uint16Array, + Int16Array, + Uint32Array, + Int32Array, + Uint8ClampedArray, + Float32Array, + Float64Array +]; + +var set = new Set([1,2,3]); + +var foo = {}; +var bar = {}; +var weak_set = new WeakSet(); +weak_set.add(foo); +weak_set.add(bar); + +var string = new String('123'); + +var map = new Map(); +map.set(0, 'zero'); +map.set(1, 'one'); + +var weak_map = new WeakMap(); +weak_map.set(foo, 'foo'); +weak_map.set(bar, 'bar'); + +var string = '123'; + +for (var constructor of typedArrayConstructors) +{ + assert(constructor.from.length === 1); + + try { + function f() {constructor.from.call(Array, []);} + } catch (e) { + assert(e instanceof TypeError); + } + + assert(constructor.from(set).toString() === '1,2,3'); + assert(constructor.from(weak_set).toString() === ''); + + if (constructor == Float32Array || constructor == Float64Array) + { + assert(constructor.from(map).toString() === 'NaN,NaN'); + } + else + { + assert(constructor.from(map).toString() === '0,0'); + } + + assert(constructor.from(weak_map).toString() === ''); + assert(constructor.from(string).toString() === '1,2,3'); + + try { + function f() {constructor.from.call({}, []);} + } catch (e) { + assert(e instanceof TypeError); + } + + try { + function f() {constructor.from.call([], []);} + } catch (e) { + assert(e instanceof TypeError); + } + + try { + function f() {constructor.from.call(1, []);} + } catch (e) { + assert(e instanceof TypeError); + } + + try { + function f() {constructor.from.call(undefined, []);} + } catch (e) { + assert(e instanceof TypeError); + } + + assert(constructor.from([1,2,3,4]).toString() === '1,2,3,4'); + assert(constructor.from([12,45]).toString() === '12,45'); + assert(constructor.from(NaN).toString() === ''); + assert(constructor.from(Infinity).toString() === ''); + + assert(constructor.from([4,5,6], x => x + 1).toString() === '5,6,7'); + assert(constructor.from([2,4,8], x => x * 2).toString() === '4,8,16'); + + try { + constructor.from([1,2,3], x => {throw 5}); + assert(false); + } catch (e) { + assert(e === 5); + } + + try { + constructor.from([Symbol.match]); + assert(false); + } catch (e) { + assert(e instanceof TypeError); + } + + try { + constructor.from([1,1,1], 'foo'); + assert(false); + } catch (e) { + assert (e instanceof TypeError); + } + + try { + function f() {constructor.from(null)} + } catch (e) { + assert(e instanceof TypeError); + } + + try { + function f() {constructor.from(undefined);} + } catch (e) { + assert(e instanceof TypeError); + } + + var called = 0; + var arr = [1,2,3]; + var obj = {}; + + function testIterator() { + called++; + return arr[Symbol.iterator](); + } + + var getCalled = 0; + Object.defineProperty(obj, Symbol.iterator, { + get: function() { + getCalled++; + return testIterator; + }, + }); + + assert(constructor.from(obj).toString() === '1,2,3'); + assert(called === 1); + assert(getCalled === 1); +}