diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 2226b11ca..951c9abcc 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -2147,9 +2147,9 @@ ecma_op_object_get_property_names (ecma_object_t *obj_p, /**< object */ if (type == ECMA_OBJECT_TYPE_FUNCTION && ecma_builtin_function_is_routine (obj_p)) { ecma_builtin_routine_list_lazy_property_names (obj_p, - opts, - prop_names_p, - skipped_non_enumerable_p); + opts, + prop_names_p, + skipped_non_enumerable_p); } else { diff --git a/jerry-core/include/jerryscript-snapshot.h b/jerry-core/include/jerryscript-snapshot.h index 7e6afac9c..8193cf267 100644 --- a/jerry-core/include/jerryscript-snapshot.h +++ b/jerry-core/include/jerryscript-snapshot.h @@ -30,7 +30,7 @@ extern "C" /** * Jerry snapshot format version. */ -#define JERRY_SNAPSHOT_VERSION (52u) +#define JERRY_SNAPSHOT_VERSION (53u) /** * Flags for jerry_generate_snapshot and jerry_generate_function_snapshot. diff --git a/jerry-core/parser/js/byte-code.c b/jerry-core/parser/js/byte-code.c index 7ebe7fab5..6849e52af 100644 --- a/jerry-core/parser/js/byte-code.c +++ b/jerry-core/parser/js/byte-code.c @@ -27,7 +27,7 @@ JERRY_STATIC_ASSERT ((sizeof (cbc_uint16_arguments_t) % sizeof (jmem_cpointer_t) */ JERRY_STATIC_ASSERT (CBC_END == 238, number_of_cbc_opcodes_changed); -JERRY_STATIC_ASSERT (CBC_EXT_END == 127, +JERRY_STATIC_ASSERT (CBC_EXT_END == 128, number_of_cbc_ext_opcodes_changed); #if ENABLED (JERRY_PARSER) diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index 4c612b80c..8ba94a292 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -608,6 +608,8 @@ VM_OC_THROW_CONST_ERROR) \ CBC_OPCODE (CBC_EXT_REQUIRE_OBJECT_COERCIBLE, CBC_NO_FLAG, 0, \ VM_OC_REQUIRE_OBJECT_COERCIBLE) \ + CBC_OPCODE (CBC_EXT_COPY_DATA_PROPERTIES, CBC_NO_FLAG, -1, \ + VM_OC_COPY_DATA_PROPERTIES) \ CBC_OPCODE (CBC_EXT_SET_FUNCTION_NAME, CBC_HAS_LITERAL_ARG, 0, \ VM_OC_SET_FUNCTION_NAME | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_SET_CLASS_NAME, CBC_HAS_LITERAL_ARG, 0, \ diff --git a/jerry-core/parser/js/js-lexer.c b/jerry-core/parser/js/js-lexer.c index 19aff1f34..20f2551e5 100644 --- a/jerry-core/parser/js/js-lexer.c +++ b/jerry-core/parser/js/js-lexer.c @@ -3061,6 +3061,22 @@ lexer_expect_object_literal_id (parser_context_t *context_p, /**< context */ lexer_consume_next_character (context_p); return; } + case LIT_CHAR_DOT: + { + if (ident_opts != LEXER_OBJ_IDENT_NO_OPTS + || context_p->source_p + 2 >= context_p->source_end_p + || context_p->source_p[1] != LIT_CHAR_DOT + || context_p->source_p[2] != LIT_CHAR_DOT) + { + break; + } + + context_p->token.type = LEXER_THREE_DOTS; + context_p->token.flags &= (uint8_t) ~LEXER_NO_SKIP_SPACES; + PARSER_PLUS_EQUAL_LC (context_p->column, 3); + context_p->source_p += 3; + return; + } #endif /* ENABLED (JERRY_ESNEXT) */ case LIT_CHAR_RIGHT_BRACE: { diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index 20661d3df..7bbf9047a 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -1068,6 +1068,13 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */ } break; } + case LEXER_THREE_DOTS: + { + lexer_next_token (context_p); + parser_parse_expression (context_p, PARSE_EXPR_NO_COMMA); + parser_emit_cbc_ext (context_p, CBC_EXT_COPY_DATA_PROPERTIES); + break; + } case LEXER_KEYW_ASYNC: case LEXER_MULTIPLY: { @@ -3418,7 +3425,6 @@ parser_process_group_expression (parser_context_t *context_p, /**< context */ parser_stack_push_uint8 (context_p, LEXER_ASSIGN_GROUP_EXPR); } #endif /* ENABLED (JERRY_ESNEXT) */ - } /* parser_process_group_expression */ /** diff --git a/jerry-core/parser/js/js-scanner.c b/jerry-core/parser/js/js-scanner.c index f5f14c7b6..5cd680ef8 100644 --- a/jerry-core/parser/js/js-scanner.c +++ b/jerry-core/parser/js/js-scanner.c @@ -2883,6 +2883,17 @@ scanner_scan_all (parser_context_t *context_p, /**< context */ scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; break; } + + if (context_p->token.type == LEXER_THREE_DOTS) + { + scanner_context.mode = SCAN_MODE_PRIMARY_EXPRESSION; + + if (scanner_context.binding_type != SCANNER_BINDING_NONE) + { + scanner_context.mode = SCAN_MODE_BINDING; + } + break; + } #endif /* ENABLED (JERRY_ESNEXT) */ if (context_p->token.type == LEXER_RIGHT_BRACE) diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index 681588f12..e19f70c5a 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -120,6 +120,42 @@ opfunc_typeof (ecma_value_t left_value) /**< left value */ return ecma_make_magic_string_value (ecma_get_typeof_lit_id (left_value)); } /* opfunc_typeof */ +/** + * Update data property for object literals. + */ +void +opfunc_set_data_property (ecma_object_t *object_p, /**< object */ + ecma_string_t *prop_name_p, /**< data property name */ + ecma_value_t value) /**< new value */ +{ + JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); + + ecma_property_t *property_p = ecma_find_named_property (object_p, prop_name_p); + + if (property_p != NULL + && ECMA_PROPERTY_GET_TYPE (*property_p) != ECMA_PROPERTY_TYPE_NAMEDDATA) + { + ecma_delete_property (object_p, ECMA_PROPERTY_VALUE_PTR (property_p)); + property_p = NULL; + } + + ecma_property_value_t *prop_value_p; + + if (property_p == NULL) + { + prop_value_p = ecma_create_named_data_property (object_p, + prop_name_p, + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, + NULL); + } + else + { + prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); + } + + ecma_named_data_property_assign_value (object_p, prop_value_p, value); +} /* opfunc_set_data_property */ + /** * Update getter or setter for object literals. */ diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 876824e60..6064ac6f2 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -105,6 +105,9 @@ opfunc_instanceof (ecma_value_t left_value, ecma_value_t right_value); ecma_value_t opfunc_typeof (ecma_value_t left_value); +void +opfunc_set_data_property (ecma_object_t *object_p, ecma_string_t *prop_name_p, ecma_value_t value); + void opfunc_set_accessor (bool is_getter, ecma_value_t object, ecma_string_t *accessor_name_p, ecma_value_t accessor); diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 52c54d23a..7ac2af6ef 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -1758,6 +1758,63 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ } goto free_left_value; } + case VM_OC_COPY_DATA_PROPERTIES: + { + result = *(--stack_top_p); + + if (ecma_is_value_undefined (result) || ecma_is_value_null (result)) + { + continue; + } + + if (!ecma_is_value_object (result)) + { + ecma_value_t value = result; + result = ecma_op_to_object (value); + ecma_free_value (value); + + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + } + + ecma_object_t *object_p = ecma_get_object_from_value (result); + ecma_collection_t *names_p = ecma_op_object_get_property_names (object_p, ECMA_LIST_ENUMERABLE); + +#if ENABLED (JERRY_BUILTIN_PROXY) + if (names_p == NULL) + { + ecma_deref_object (object_p); + result = ECMA_VALUE_ERROR; + goto error; + } +#endif /* ENABLED (JERRY_BUILTIN_PROXY) */ + + ecma_object_t *target_object_p = ecma_get_object_from_value (stack_top_p[-1]); + ecma_value_t *buffer_p = names_p->buffer_p; + ecma_value_t *buffer_end_p = buffer_p + names_p->item_count; + + while (buffer_p < buffer_end_p) + { + ecma_string_t *property_name_p = ecma_get_string_from_value (*buffer_p++); + result = ecma_op_object_get (object_p, property_name_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + ecma_collection_free (names_p); + ecma_deref_object (object_p); + goto error; + } + + opfunc_set_data_property (target_object_p, property_name_p, result); + ecma_free_value (result); + } + + ecma_collection_free (names_p); + ecma_deref_object (object_p); + continue; + } case VM_OC_SET_COMPUTED_PROPERTY: { /* Swap values. */ @@ -1797,33 +1854,7 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ ecma_object_t *object_p = ecma_get_object_from_value (stack_top_p[index]); - JERRY_ASSERT (!ecma_op_object_is_fast_array (object_p)); - - ecma_property_t *property_p = ecma_find_named_property (object_p, prop_name_p); - - if (property_p != NULL - && ECMA_PROPERTY_GET_TYPE (*property_p) != ECMA_PROPERTY_TYPE_NAMEDDATA) - { - ecma_delete_property (object_p, ECMA_PROPERTY_VALUE_PTR (property_p)); - property_p = NULL; - } - - ecma_property_value_t *prop_value_p; - - if (property_p == NULL) - { - prop_value_p = ecma_create_named_data_property (object_p, - prop_name_p, - ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, - NULL); - } - else - { - prop_value_p = ECMA_PROPERTY_VALUE_PTR (property_p); - } - - ecma_named_data_property_assign_value (object_p, prop_value_p, left_value); - + opfunc_set_data_property (object_p, prop_name_p, left_value); ecma_deref_ecma_string (prop_name_p); goto free_both_values; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index df274a121..bafd49afd 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -241,6 +241,7 @@ typedef enum VM_OC_COPY_TO_GLOBAL, /**< copy value to global lex env */ VM_OC_COPY_FROM_ARG, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT, /**< clone lexical environment with let/const declarations */ + VM_OC_COPY_DATA_PROPERTIES, /**< copy data properties of an object */ VM_OC_SET_COMPUTED_PROPERTY, /**< set computed property */ VM_OC_FOR_OF_INIT, /**< for-of init context */ @@ -312,6 +313,7 @@ typedef enum VM_OC_COPY_TO_GLOBAL = VM_OC_NONE, /**< copy value to global lex env */ VM_OC_COPY_FROM_ARG = VM_OC_NONE, /**< copy value from arg lex env */ VM_OC_CLONE_CONTEXT = VM_OC_NONE, /**< clone lexical environment with let/const declarations */ + VM_OC_COPY_DATA_PROPERTIES = VM_OC_NONE, /**< copy data properties of an object */ VM_OC_SET_COMPUTED_PROPERTY = VM_OC_NONE, /**< set computed property is unused */ VM_OC_FOR_OF_INIT = VM_OC_NONE, /**< for-of init context */ diff --git a/tests/jerry/es.next/object-copy-data.js b/tests/jerry/es.next/object-copy-data.js new file mode 100644 index 000000000..9e5db3e06 --- /dev/null +++ b/tests/jerry/es.next/object-copy-data.js @@ -0,0 +1,68 @@ +// 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. + +function checkOwnProperties(obj, propList) +{ + names = Object.getOwnPropertyNames(obj) + assert(names.length === propList.length) + + for (var i = 0; i < propList.length; ++i) + { + assert(names[i] === propList[i]) + + var descriptor = Object.getOwnPropertyDescriptor(obj, names[i]) + assert(descriptor.writable == true && descriptor.get === undefined && descriptor.set === undefined); + } +} + +var o = {..."a" + "bc"} + +assert(o[0] === "a") +assert(o[1] === "b") +assert(o[2] === "c") +checkOwnProperties(o, ["0", "1", "2"]) + +s = { a:3.5, b() {}, get c() { return "Prop" } } +o = {...null, ...undefined, ...s} + +assert(o.a === 3.5) +assert(o.b === s.b) +assert(o.c === "Prop") +checkOwnProperties(o, ["a", "b", "c"]) + +s = { a:"X", b:-1.25, c:"Str" } +o = { get a() {}, set b(v) {}, ...s, c() { return 1 }} + +assert(o.a === "X") +assert(o.b === -1.25) +assert(o.c() === 1) +checkOwnProperties(o, ["a", "b", "c"]) + +s = { p1:[1,2], "(a)":"Msg" } +Object.defineProperty(s, 'nonEnumerable', { value: 8.75, writable: true, enumerable:false }); +assert(s.nonEnumerable === 8.75) + +o = { ...s, "p!":5, ...s, ...s } +assert(o.p1 === s.p1) +assert(o["(a)"] === "Msg") +assert(o["p!"] === 5) +checkOwnProperties(o, ["p1", "(a)", "p!"]) + +var sym = Symbol('Any') +s = [ { [sym]:5, a:6 } ] +o = { ...((s))[0] } + +assert(o[sym] === undefined) +assert(o.a === 6) +checkOwnProperties(o, ["a"])