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