From 4ed9e2c0336e4b372411cd38302f357552ca43b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20G=C3=A1l?= Date: Fri, 28 Aug 2020 12:46:35 +0200 Subject: [PATCH] Correctly report error in for-in start in case of proxies (#4165) In case of Proxies the "ownKeys" call can return an incompatible value for a for-in statement. In such cases the error should be propagated to the user. Fixes: #4159 JerryScript-DCO-1.0-Signed-off-by: Peter Gal pgal.usz@partner.samsung.com --- jerry-core/vm/opcodes.c | 28 +++++++++-- jerry-core/vm/vm.c | 8 +++ tests/jerry/es.next/proxy-for-in.js | 78 +++++++++++++++++++++++++++++ tests/test262-es6-excludelist.xml | 1 - 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 tests/jerry/es.next/proxy-for-in.js diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index c6e3b1bf1..1bc46dffc 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -324,29 +324,47 @@ vm_op_delete_var (ecma_value_t name_literal, /**< name literal */ /** * 'for-in' opcode handler * + * Note: from ES2015 (ES6) the for-in can trigger error when + * the property names are not available (ex.: via Proxy ownKeys). + * In these cases an error must be returned. + * + * This error is returned as the `result_obj_p` and the + * function's return value is NULL. + * * See also: * ECMA-262 v5, 12.6.4 * - * @return chain list of property names + * @return - chain list of property names + * - In case of error: NULL is returned and the `result_obj_p` + * must be checked. */ ecma_collection_t * -opfunc_for_in (ecma_value_t left_value, /**< left value */ +opfunc_for_in (ecma_value_t iterable_value, /**< ideally an iterable value */ ecma_value_t *result_obj_p) /**< expression object */ { /* 3. */ - if (ecma_is_value_undefined (left_value) - || ecma_is_value_null (left_value)) + if (ecma_is_value_undefined (iterable_value) + || ecma_is_value_null (iterable_value)) { return NULL; } /* 4. */ - ecma_value_t obj_expr_value = ecma_op_to_object (left_value); + ecma_value_t obj_expr_value = ecma_op_to_object (iterable_value); /* ecma_op_to_object will only raise error on null/undefined values but those are handled above. */ JERRY_ASSERT (!ECMA_IS_VALUE_ERROR (obj_expr_value)); ecma_object_t *obj_p = ecma_get_object_from_value (obj_expr_value); ecma_collection_t *prop_names_p = ecma_op_object_enumerate (obj_p); +#if ENABLED (JERRY_ESNEXT) + if (JERRY_UNLIKELY (prop_names_p == NULL)) + { + ecma_deref_object (obj_p); + *result_obj_p = ECMA_VALUE_ERROR; + return NULL; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + if (prop_names_p->item_count != 0) { *result_obj_p = ecma_make_object_value (obj_p); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index f6d81b248..9744bece9 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -3737,6 +3737,14 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ if (prop_names_p == NULL) { +#if ENABLED (JERRY_ESNEXT) + if (JERRY_UNLIKELY (ECMA_IS_VALUE_ERROR (expr_obj_value))) + { + result = expr_obj_value; + goto error; + } +#endif /* ENABLED (JERRY_ESNEXT) */ + /* The collection is already released */ byte_code_p = byte_code_start_p + branch_offset; continue; diff --git a/tests/jerry/es.next/proxy-for-in.js b/tests/jerry/es.next/proxy-for-in.js new file mode 100644 index 000000000..7927c8605 --- /dev/null +++ b/tests/jerry/es.next/proxy-for-in.js @@ -0,0 +1,78 @@ +// 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. + +// Copyright 2015 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var handlerBoolean = { + ownKeys: function() { return true; }, +} +var boolProxy = new Proxy({}, handlerBoolean); + +try { + for (var a in boolProxy) { + // This should never be called. + assert (false); + } + + assert (false); +} catch (ex) { + assert (ex instanceof TypeError); +} + + +var handlerSymbol = { + ownKeys: function() { return Symbol("alma"); }, +} +var symbolProxy = new Proxy({}, handlerSymbol); + +try { + for (var a in symbolProxy) { + // This should never be called. + assert (false); + } + + assert (false); +} catch (ex) { + assert (ex instanceof TypeError); +} + + +var handlerNumber = { + ownKeys: function() { return 1; }, +} +var numberProxy = new Proxy({}, handlerNumber); + +try { + for (var a in numberProxy) { + // This should never be called. + assert (false); + } + + assert (false); +} catch (ex) { + assert (ex instanceof TypeError); +} + + +var handlerObject = { + ownKeys: function() { return {}; }, +} +var objectProxy = new Proxy({}, handlerObject); + +for (var a in objectProxy) { + // This should never be called. + assert (false); +} diff --git a/tests/test262-es6-excludelist.xml b/tests/test262-es6-excludelist.xml index 8fdcc9345..d01545488 100644 --- a/tests/test262-es6-excludelist.xml +++ b/tests/test262-es6-excludelist.xml @@ -56,7 +56,6 @@ For-in supports proxy For-in supports proxy For-in supports proxy - For-in supports proxy For-in supports proxy