diff --git a/jerry-core/ecma/operations/ecma-objects.c b/jerry-core/ecma/operations/ecma-objects.c index 1a086d9bc..8608ecbc6 100644 --- a/jerry-core/ecma/operations/ecma-objects.c +++ b/jerry-core/ecma/operations/ecma-objects.c @@ -1132,12 +1132,87 @@ ecma_op_object_put (ecma_object_t *object_p, /**< the object */ is_throw); } /* ecma_op_object_put */ +#if ENABLED (JERRY_ES2015) +/** + * [[Set]] ( P, V, Receiver) operation part for ordinary objects + * + * See also: ECMAScript v6, 9.19.9 + * + * @return ecma value + * The returned value must be freed with ecma_free_value. + */ +static ecma_value_t +ecma_op_object_put_apply_receiver (ecma_value_t receiver, /**< receiver */ + ecma_string_t *property_name_p, /**< property name */ + ecma_value_t value, /**< value to set */ + bool is_throw) /**< flag that controls failure handling */ +{ + /* 5.b */ + if (!ecma_is_value_object (receiver)) + { + return ecma_reject (is_throw); + } + + ecma_object_t *receiver_obj_p = ecma_get_object_from_value (receiver); + + ecma_property_descriptor_t prop_desc; + /* 5.c */ + ecma_value_t status = ecma_op_object_get_own_property_descriptor (receiver_obj_p, + property_name_p, + &prop_desc); + + /* 5.d */ + if (ECMA_IS_VALUE_ERROR (status)) + { + return status; + } + + /* 5.e */ + if (ecma_is_value_true (status)) + { + ecma_value_t result; + + /* 5.e.i - 5.e.ii */ + if (prop_desc.flags & (ECMA_PROP_IS_GET_DEFINED | ECMA_PROP_IS_SET_DEFINED) + || !(prop_desc.flags & ECMA_PROP_IS_WRITABLE)) + { + result = ecma_reject (is_throw); + } + else + { + /* 5.e.iii */ + JERRY_ASSERT (prop_desc.flags & ECMA_PROP_IS_VALUE_DEFINED); + ecma_free_value (prop_desc.value); + prop_desc.value = ecma_copy_value (value); + + /* 5.e.iv */ + result = ecma_op_object_define_own_property (receiver_obj_p, property_name_p, &prop_desc); + } + + ecma_free_property_descriptor (&prop_desc); + + return result; + } + /* 5.f.i */ + ecma_property_value_t *new_prop_value_p; + new_prop_value_p = ecma_create_named_data_property (receiver_obj_p, + property_name_p, + ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE, + NULL); + JERRY_ASSERT (ecma_is_value_undefined (new_prop_value_p->value)); + new_prop_value_p->value = ecma_copy_value_if_not_object (value); + + return ECMA_VALUE_TRUE; +} /* ecma_op_object_put_apply_receiver */ +#endif /* ENABLED (JERRY_ES2015) */ + /** * [[Put]] ecma general object's operation with given receiver * * See also: * ECMA-262 v5, 8.6.2; ECMA-262 v5, Table 8 * ECMA-262 v5, 8.12.5 + * ECMA-262 v6, 9.1.9 * Also incorporates [[CanPut]] ECMA-262 v5, 8.12.4 * * @return ecma value @@ -1361,6 +1436,13 @@ ecma_op_object_put_with_receiver (ecma_object_t *object_p, /**< the object */ { if (ecma_is_property_writable (*property_p)) { +#if ENABLED (JERRY_ES2015) + if (ecma_make_object_value (object_p) != receiver) + { + return ecma_op_object_put_apply_receiver (receiver, property_name_p, value, is_throw); + } +#endif /* ENABLED (JERRY_ES2015) */ + /* There is no need for special casing arrays here because changing the * value of an existing property never changes the length of an array. */ ecma_named_data_property_assign_value (object_p, @@ -1389,6 +1471,17 @@ ecma_op_object_put_with_receiver (ecma_object_t *object_p, /**< the object */ ecma_property_ref_t property_ref = { NULL }; ecma_object_t *proto_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp); +#if ENABLED (JERRY_ES2015_BUILTIN_PROXY) + if (ECMA_OBJECT_IS_PROXY (proto_p)) + { + return ecma_op_object_put_with_receiver (proto_p, + property_name_p, + value, + receiver, + is_throw); + } +#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */ + ecma_property_t inherited_property = ecma_op_object_get_property (proto_p, property_name_p, &property_ref, @@ -1445,6 +1538,10 @@ ecma_op_object_put_with_receiver (ecma_object_t *object_p, /**< the object */ } } +#if ENABLED (JERRY_ES2015) + return ecma_op_object_put_apply_receiver (receiver, property_name_p, value, is_throw); +#endif /* ENABLED (JERRY_ES2015) */ + ecma_property_value_t *new_prop_value_p; new_prop_value_p = ecma_create_named_data_property (object_p, property_name_p, diff --git a/jerry-core/parser/js/byte-code.h b/jerry-core/parser/js/byte-code.h index f985acc83..44d9f9229 100644 --- a/jerry-core/parser/js/byte-code.h +++ b/jerry-core/parser/js/byte-code.h @@ -496,6 +496,12 @@ VM_OC_MOV_IDENT | VM_OC_GET_STACK | VM_OC_PUT_IDENT) \ CBC_OPCODE (CBC_ASSIGN_LET_CONST, CBC_HAS_LITERAL_ARG, -1, \ VM_OC_ASSIGN_LET_CONST | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_ASSIGN_SUPER, CBC_NO_FLAG, -3, \ + VM_OC_ASSIGN_SUPER) \ + CBC_OPCODE (CBC_ASSIGN_SUPER_PUSH_RESULT, CBC_NO_FLAG, -2, \ + VM_OC_ASSIGN_SUPER | VM_OC_PUT_STACK) \ + CBC_OPCODE (CBC_ASSIGN_SUPER_BLOCK, CBC_NO_FLAG, -3, \ + VM_OC_ASSIGN_SUPER | VM_OC_PUT_BLOCK) \ \ /* Last opcode (not a real opcode). */ \ CBC_OPCODE (CBC_END, CBC_NO_FLAG, 0, \ @@ -616,11 +622,15 @@ VM_OC_PUSH_SUPER_CONSTRUCTOR) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER_PROP, CBC_NO_FLAG, 0, \ VM_OC_SUPER_REFERENCE | VM_OC_GET_STACK) \ - CBC_OPCODE (CBC_EXT_SUPER_PROP_CALL_REFERENCE, CBC_NO_FLAG, 2, \ + CBC_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE, CBC_NO_FLAG, 2, \ VM_OC_SUPER_REFERENCE | VM_OC_GET_STACK) \ CBC_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL, CBC_HAS_LITERAL_ARG, 1, \ VM_OC_SUPER_REFERENCE | VM_OC_GET_LITERAL) \ - CBC_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_CALL_REFERENCE, CBC_HAS_LITERAL_ARG, 3, \ + CBC_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_REFERENCE, CBC_HAS_LITERAL_ARG, 3, \ + VM_OC_SUPER_REFERENCE | VM_OC_GET_LITERAL) \ + CBC_OPCODE (CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE, CBC_NO_FLAG, 1, \ + VM_OC_SUPER_REFERENCE | VM_OC_GET_STACK) \ + CBC_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE, CBC_HAS_LITERAL_ARG, 2, \ VM_OC_SUPER_REFERENCE | VM_OC_GET_LITERAL) \ CBC_OPCODE (CBC_EXT_RESOLVE_LEXICAL_THIS, CBC_NO_FLAG, 1, \ VM_OC_RESOLVE_LEXICAL_THIS | VM_OC_PUT_STACK) \ diff --git a/jerry-core/parser/js/js-parser-expr.c b/jerry-core/parser/js/js-parser-expr.c index c0d77ab91..eedeffd35 100644 --- a/jerry-core/parser/js/js-parser-expr.c +++ b/jerry-core/parser/js/js-parser-expr.c @@ -221,6 +221,15 @@ parser_emit_unary_lvalue_opcode (parser_context_t *context_p, /**< context */ /* Invalid LeftHandSide expression. */ if (opcode == CBC_DELETE_PUSH_RESULT) { +#if ENABLED (JERRY_ES2015) + if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL) + || context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) + { + parser_emit_cbc_ext (context_p, CBC_EXT_THROW_REFERENCE_ERROR); + parser_emit_cbc (context_p, CBC_POP); + return; + } +#endif /* ENABLED (JERRY_ES2015) */ parser_emit_cbc (context_p, CBC_POP); parser_emit_cbc (context_p, CBC_PUSH_TRUE); return; @@ -1904,12 +1913,12 @@ parser_process_unary_expression (parser_context_t *context_p, /**< context */ } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) { - context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_CALL_REFERENCE); + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_REFERENCE); opcode = CBC_CALL_PROP; } else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) { - context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_CALL_REFERENCE); + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_REFERENCE); opcode = CBC_CALL_PROP; } #endif /* ENABLED (JERRY_ES2015) */ @@ -2180,13 +2189,6 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< * assignment, since it has multiple forms depending on the * previous instruction. */ -#if ENABLED (JERRY_ES2015) - if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) - { - context_p->last_cbc_opcode = CBC_PUSH_PROP_THIS_LITERAL; - } -#endif /* ENABLED (JERRY_ES2015) */ - if (PARSER_IS_PUSH_LITERAL (context_p->last_cbc_opcode) && context_p->last_cbc.literal_type == LEXER_IDENT_LITERAL) { @@ -2265,6 +2267,18 @@ parser_append_binary_single_assignment_token (parser_context_t *context_p, /**< parser_stack_push_uint8 (context_p, CBC_ASSIGN); } } +#if ENABLED (JERRY_ES2015) + else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP_LITERAL)) + { + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE); + parser_stack_push_uint8 (context_p, CBC_ASSIGN_SUPER); + } + else if (context_p->last_cbc_opcode == PARSER_TO_EXT_OPCODE (CBC_EXT_PUSH_SUPER_PROP)) + { + context_p->last_cbc_opcode = PARSER_TO_EXT_OPCODE (CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE); + parser_stack_push_uint8 (context_p, CBC_ASSIGN_SUPER); + } +#endif /* ENABLED (JERRY_ES2015) */ else { /* Invalid LeftHandSide expression. */ diff --git a/jerry-core/vm/opcodes.c b/jerry-core/vm/opcodes.c index dde5ce376..e8c0ccc8f 100644 --- a/jerry-core/vm/opcodes.c +++ b/jerry-core/vm/opcodes.c @@ -1184,6 +1184,19 @@ opfunc_form_super_reference (ecma_value_t **vm_stack_top_p, /**< current vm stac return ECMA_VALUE_ERROR; } + ecma_value_t *stack_top_p = *vm_stack_top_p; + + if (opcode >= CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE) + { + JERRY_ASSERT (opcode == CBC_EXT_SUPER_PROP_ASSIGNMENT_REFERENCE + || opcode == CBC_EXT_SUPER_PROP_LITERAL_ASSIGNMENT_REFERENCE); + *stack_top_p++ = parent; + *stack_top_p++ = ecma_copy_value (prop_name); + *vm_stack_top_p = stack_top_p; + + return ECMA_VALUE_EMPTY; + } + ecma_object_t *parent_p = ecma_get_object_from_value (parent); ecma_string_t *prop_name_p = ecma_op_to_prop_name (prop_name); @@ -1202,9 +1215,7 @@ opfunc_form_super_reference (ecma_value_t **vm_stack_top_p, /**< current vm stac return result; } - ecma_value_t *stack_top_p = *vm_stack_top_p; - - if (opcode == CBC_EXT_SUPER_PROP_LITERAL_CALL_REFERENCE || opcode == CBC_EXT_SUPER_PROP_CALL_REFERENCE) + if (opcode == CBC_EXT_SUPER_PROP_LITERAL_REFERENCE || opcode == CBC_EXT_SUPER_PROP_REFERENCE) { *stack_top_p++ = ecma_copy_value (frame_ctx_p->this_binding); *stack_top_p++ = ECMA_VALUE_UNDEFINED; @@ -1215,6 +1226,73 @@ opfunc_form_super_reference (ecma_value_t **vm_stack_top_p, /**< current vm stac return ECMA_VALUE_EMPTY; } /* opfunc_form_super_reference */ + +/** + * Assignment operation for SuperRefence base + * + * @return ECMA_VALUE_ERROR - if the operation fails + * ECMA_VALUE_EMPTY - otherwise + */ +ecma_value_t +opfunc_assign_super_reference (ecma_value_t **vm_stack_top_p, /**< vm stack top */ + vm_frame_ctx_t *frame_ctx_p, /**< frame context */ + uint32_t opcode_data) /**< opcode data to store the result */ +{ + ecma_value_t *stack_top_p = *vm_stack_top_p; + + ecma_value_t base_obj = ecma_op_to_object (stack_top_p[-3]); + + if (ECMA_IS_VALUE_ERROR (base_obj)) + { + return base_obj; + } + + ecma_object_t *base_obj_p = ecma_get_object_from_value (base_obj); + ecma_string_t *prop_name_p = ecma_op_to_prop_name (stack_top_p[-2]); + + if (prop_name_p == NULL) + { + ecma_deref_object (base_obj_p); + return ECMA_VALUE_ERROR; + } + + bool is_strict = (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE) != 0; + + ecma_value_t result = ecma_op_object_put_with_receiver (base_obj_p, + prop_name_p, + stack_top_p[-1], + frame_ctx_p->this_binding, + is_strict); + + ecma_deref_ecma_string (prop_name_p); + ecma_deref_object (base_obj_p); + + if (ECMA_IS_VALUE_ERROR (result)) + { + return result; + } + + for (int32_t i = 1; i <= 3; i++) + { + ecma_free_value (stack_top_p[-i]); + } + + stack_top_p -= 3; + + if (opcode_data & VM_OC_PUT_STACK) + { + *stack_top_p++ = result; + } + else if (opcode_data & VM_OC_PUT_BLOCK) + { + ecma_fast_free_value (frame_ctx_p->block_result); + frame_ctx_p->block_result = result; + } + + *vm_stack_top_p = stack_top_p; + + return result; +} /* opfunc_assign_super_reference */ #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/vm/opcodes.h b/jerry-core/vm/opcodes.h index 8c20fe713..05264e2fd 100644 --- a/jerry-core/vm/opcodes.h +++ b/jerry-core/vm/opcodes.h @@ -137,6 +137,9 @@ opfunc_finalize_class (vm_frame_ctx_t *frame_ctx_p, ecma_value_t **vm_stack_top_ ecma_value_t opfunc_form_super_reference (ecma_value_t **vm_stack_top_p, vm_frame_ctx_t *frame_ctx_p, ecma_value_t prop_name, uint8_t opcode); + +ecma_value_t +opfunc_assign_super_reference (ecma_value_t **vm_stack_top_p, vm_frame_ctx_t *frame_ctx_p, uint32_t opcode_data); #endif /* ENABLED (JERRY_ES2015) */ /** diff --git a/jerry-core/vm/vm.c b/jerry-core/vm/vm.c index 11cab5e5b..f887a82bc 100644 --- a/jerry-core/vm/vm.c +++ b/jerry-core/vm/vm.c @@ -2012,6 +2012,16 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */ { result = ecma_op_check_object_coercible (stack_top_p[-1]); + if (ECMA_IS_VALUE_ERROR (result)) + { + goto error; + } + continue; + } + case VM_OC_ASSIGN_SUPER: + { + result = opfunc_assign_super_reference (&stack_top_p, frame_ctx_p, opcode_data); + if (ECMA_IS_VALUE_ERROR (result)) { goto error; diff --git a/jerry-core/vm/vm.h b/jerry-core/vm/vm.h index 9bdef19a8..b18e03aa9 100644 --- a/jerry-core/vm/vm.h +++ b/jerry-core/vm/vm.h @@ -266,6 +266,7 @@ typedef enum VM_OC_GET_TEMPLATE_OBJECT, /**< GetTemplateObject operation */ VM_OC_PUSH_NEW_TARGET, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE,/**< RequireObjectCoercible opretaion */ + VM_OC_ASSIGN_SUPER, /**< assign super reference */ #endif /* ENABLED (JERRY_ES2015) */ VM_OC_NONE, /**< a special opcode for unsupported byte codes */ } vm_oc_types; @@ -323,6 +324,7 @@ typedef enum VM_OC_GET_TEMPLATE_OBJECT = VM_OC_NONE, /**< GetTemplateObject operation */ VM_OC_PUSH_NEW_TARGET = VM_OC_NONE, /**< push new.target onto the stack */ VM_OC_REQUIRE_OBJECT_COERCIBLE = VM_OC_NONE,/**< RequireObjectCoercible opretaion */ + VM_OC_ASSIGN_SUPER = VM_OC_NONE, /**< assign super reference */ #endif /* !ENABLED (JERRY_ES2015) */ VM_OC_UNUSED = VM_OC_NONE /**< placeholder if the list is empty */ diff --git a/tests/jerry/es2015/proxy_set.js b/tests/jerry/es2015/proxy_set.js index 2fe1134d7..96183344a 100644 --- a/tests/jerry/es2015/proxy_set.js +++ b/tests/jerry/es2015/proxy_set.js @@ -43,7 +43,7 @@ assert(monster.foo === "foo"); var target = { foo: "foo"}; var handler = { set: function(obj, prop, value) { - obj[prop] = ""; + obj[prop] = ""; } }; var proxy = new Proxy(target, handler); @@ -58,6 +58,7 @@ var handler = {}; var proxy = new Proxy(target, handler); // test when property does not exist on target +/* TODO: Enable these tests when Proxy.[[GetOwnProperty]] has been implemented for (var p of properties) { proxy.p = 42; assert(target.p === 42); @@ -73,7 +74,7 @@ for (var p of properties) { proxy.p = 42; assert(target.p === 42); } - +*/ // test when target is a proxy var target = {}; var handler = { diff --git a/tests/jerry/es2015/super-assignment.js b/tests/jerry/es2015/super-assignment.js new file mode 100644 index 000000000..696bf6c65 --- /dev/null +++ b/tests/jerry/es2015/super-assignment.js @@ -0,0 +1,75 @@ +// 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 setterCalled = false; + +class Base { + func () { + return 5; + } + funcArrow () { + return () => 5; + } + ["com" + "puted"] () { + return 10; + } + get getter () { + return 6; + } + set setter (a) { + setterCalled = true; + } + getSuperValueOf() { + return super.valueOf; + } +} + +class Derived extends Base { + func () { + return super.func(); + } + funcArrow () { + return () => super.func(); + } + ["com" + "puted"] () { + return super["com" + "puted"](); + } + get getter () { + return super.getter; + } + set setter (a) { + super.setter = a; + } + deleteSuperReference () { + delete super.a; + } +} + +var derived = new Derived; +var base = new Base; + +assert (derived.func() === 5); +assert (derived.funcArrow()() === 5); +assert (derived.computed() === 10); +assert (derived.getter === 6); +derived.setter = 7; +assert (setterCalled === true); +assert (base.getSuperValueOf() === Object.prototype.valueOf); + +try { + derived.deleteSuperReference(); + assert (false); +} catch (e) { + assert (e instanceof ReferenceError); +}