Fix super property assignment for namedaccessor properties (#3654)

Tha patch also updates the [[Put]] internal method with the new steps from the ES6 standard.

JerryScript-DCO-1.0-Signed-off-by: Robert Fancsik frobert@inf.u-szeged.hu
This commit is contained in:
Robert Fancsik
2020-04-09 17:08:08 +02:00
committed by GitHub
parent 901e57c7d0
commit 60db840cf4
9 changed files with 306 additions and 16 deletions
+97
View File
@@ -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,
+12 -2
View File
@@ -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) \
+23 -9
View File
@@ -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. */
+81 -3
View File
@@ -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) */
/**
+3
View File
@@ -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) */
/**
+10
View File
@@ -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;
+2
View File
@@ -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 */
+3 -2
View File
@@ -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 = {
+75
View File
@@ -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);
}