From 08da8bc7aa0fc1cf3a7734d0c30833ebfcb9d86b Mon Sep 17 00:00:00 2001 From: Peter Marki Date: Fri, 10 Jan 2020 12:16:53 +0100 Subject: [PATCH] Add length check to ecma_builtin_array_prototype_object_slice (#3481) Fixes #3479 JerryScript-DCO-1.0-Signed-off-by: Peter Marki marpeter@inf.u-szeged.hu --- .../ecma-builtin-array-prototype.c | 37 ++++++++++--- tests/jerry/es2015/array-prototype-slice.js | 55 +++++++++++++++++++ .../es2015/regression-test-issue-3479.js | 21 +++++++ 3 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 tests/jerry/es2015/array-prototype-slice.js create mode 100644 tests/jerry/es2015/regression-test-issue-3479.js diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c index c63298850..8b96bf217 100644 --- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c +++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c @@ -828,8 +828,9 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t arg1, /**< start */ JERRY_ASSERT (start <= len && end <= len); bool use_fast_path = ecma_op_object_is_fast_array (obj_p); + uint32_t copied_length = (end > start) ? end - start : 0; #if ENABLED (JERRY_ES2015) - ecma_value_t new_array = ecma_op_array_species_create (obj_p, 0); + ecma_value_t new_array = ecma_op_array_species_create (obj_p, copied_length); if (ECMA_IS_VALUE_ERROR (new_array)) { @@ -846,25 +847,45 @@ ecma_builtin_array_prototype_object_slice (ecma_value_t arg1, /**< start */ /* 9. */ uint32_t n = 0; - if (use_fast_path) + if (use_fast_path && copied_length > 0) { ecma_extended_object_t *ext_from_obj_p = (ecma_extended_object_t *) obj_p; - if (ext_from_obj_p->u.array.u.hole_count < ECMA_FAST_ARRAY_HOLE_ONE - && len != 0 - && start < end) + if (ext_from_obj_p->u.array.u.hole_count < ECMA_FAST_ARRAY_HOLE_ONE) { - uint32_t length = end - start; ecma_extended_object_t *ext_to_obj_p = (ecma_extended_object_t *) new_array_p; - ecma_value_t *to_buffer_p = ecma_fast_array_extend (new_array_p, length); + +#if ENABLED (JERRY_ES2015) + uint32_t target_length = ext_to_obj_p->u.array.length; + ecma_value_t *to_buffer_p; + if (copied_length == target_length) + { + to_buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, new_array_p->u1.property_list_cp); + } + else if (copied_length > target_length) + { + to_buffer_p = ecma_fast_array_extend (new_array_p, copied_length); + } + else + { + ecma_delete_fast_array_properties (new_array_p, copied_length); + to_buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, new_array_p->u1.property_list_cp); + } +#else /* !ENABLED (JERRY_ES2015) */ + ecma_value_t *to_buffer_p = ecma_fast_array_extend (new_array_p, copied_length); +#endif /* ENABLED (JERRY_ES2015) */ + ecma_value_t *from_buffer_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, obj_p->u1.property_list_cp); for (uint32_t k = start; k < end; k++, n++) { +#if ENABLED (JERRY_ES2015) + ecma_free_value_if_not_object (to_buffer_p[n]); +#endif /* ENABLED (JERRY_ES2015) */ to_buffer_p[n] = ecma_copy_value_if_not_object (from_buffer_p[k]); } - ext_to_obj_p->u.array.u.hole_count -= length * ECMA_FAST_ARRAY_HOLE_ONE; + ext_to_obj_p->u.array.u.hole_count &= ECMA_FAST_ARRAY_HOLE_ONE - 1; return new_array; } diff --git a/tests/jerry/es2015/array-prototype-slice.js b/tests/jerry/es2015/array-prototype-slice.js new file mode 100644 index 000000000..46308748d --- /dev/null +++ b/tests/jerry/es2015/array-prototype-slice.js @@ -0,0 +1,55 @@ +// 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. + +// Constructor creates longer array than expected. +class LongArray extends Array { + constructor(len) { + super (42); + this.fill ("foo"); + } +} + +var a = new LongArray (5); +a.length = 5; +var sliced = a.slice (); +assert (sliced.length == 5); +assert (JSON.stringify (sliced) == '["foo","foo","foo","foo","foo"]') + +// Constructor creates shorter array than expected. +class ShortArray extends Array { + constructor(len) { + super (2); + this.fill ("bar"); + } +} + +var b = new ShortArray (8); +b.length = 8; +b.fill ("asd", 2); +var sliced2 = b.slice (); +assert (sliced2.length == 8); +assert (JSON.stringify (sliced2) == '["bar","bar","asd","asd","asd","asd","asd","asd"]'); + +// Constructor creates array of the expected size. +class ExactArray extends Array { + constructor(len) { + super (len); + this.fill ("baz"); + } +} + +var c = new ExactArray (5); +var sliced3 = c.slice(); +assert (sliced3.length == 5); +assert (JSON.stringify (sliced3) == '["baz","baz","baz","baz","baz"]'); diff --git a/tests/jerry/es2015/regression-test-issue-3479.js b/tests/jerry/es2015/regression-test-issue-3479.js new file mode 100644 index 000000000..489c95dd4 --- /dev/null +++ b/tests/jerry/es2015/regression-test-issue-3479.js @@ -0,0 +1,21 @@ +// 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. + +class MyObservedArray extends Array { + constructor() { + super('"use strict"; var x = "\\411";') + } [Symbol]() {} +} + +new MyObservedArray().slice()