Implement Object.prototype.__proto__ accessor property (#3546)
We are using the already existing Object.getPrototypeOf and Object.setProtoypeOf methods JerryScript-DCO-1.0-Signed-off-by: Adam Szilagyi aszilagy@inf.u-szeged.hu
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include "ecma-alloc.h"
|
||||
#include "ecma-array-object.h"
|
||||
#include "ecma-builtins.h"
|
||||
#include "ecma-builtin-object.h"
|
||||
#include "ecma-conversion.h"
|
||||
#include "ecma-function-object.h"
|
||||
#include "ecma-exceptions.h"
|
||||
|
||||
@@ -116,6 +116,14 @@ const ecma_builtin_property_descriptor_t PROPERTY_DESCRIPTOR_LIST_NAME[] =
|
||||
prop_attributes, \
|
||||
ECMA_ACCESSOR_ ## name ## c_getter_func_name \
|
||||
},
|
||||
#define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \
|
||||
{ \
|
||||
name, \
|
||||
ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE, \
|
||||
prop_attributes, \
|
||||
ECMA_ACCESSOR_READ_WRITE (ECMA_ACCESSOR_ ## name ## c_getter_func_name, \
|
||||
ECMA_ACCESSOR_ ## name ## c_setter_func_name) \
|
||||
},
|
||||
#else /* BUILTIN_CUSTOM_DISPATCH */
|
||||
#define ROUTINE(name, c_function_name, args_number, length_prop_value) \
|
||||
{ \
|
||||
@@ -145,6 +153,13 @@ const ecma_builtin_property_descriptor_t PROPERTY_DESCRIPTOR_LIST_NAME[] =
|
||||
prop_attributes, \
|
||||
c_getter_func_name \
|
||||
},
|
||||
#define ACCESSOR_READ_WRITE(name, c_getter_func_name, c_setter_func_name, prop_attributes) \
|
||||
{ \
|
||||
name, \
|
||||
ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE, \
|
||||
prop_attributes, \
|
||||
ECMA_ACCESSOR_READ_WRITE (c_getter_func_name, c_setter_func_name) \
|
||||
},
|
||||
#endif /* !BUILTIN_CUSTOM_DISPATCH */
|
||||
#define OBJECT_VALUE(name, obj_builtin_id, prop_attributes) \
|
||||
{ \
|
||||
@@ -197,13 +212,6 @@ const ecma_builtin_property_descriptor_t PROPERTY_DESCRIPTOR_LIST_NAME[] =
|
||||
ECMA_ACCESSOR_READ_WRITE (getter_builtin_id, setter_builtin_id) \
|
||||
},
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
#define ACCESSOR_READ_WRITE(name, c_getter_name, c_setter_name, prop_attributes) \
|
||||
{ \
|
||||
name, \
|
||||
ECMA_BUILTIN_PROPERTY_ACCESSOR_READ_WRITE, \
|
||||
prop_attributes, \
|
||||
ECMA_ACCESSOR_READ_WRITE (ECMA_ACCESSOR_ ## name ## c_getter_name, ECMA_ACCESSOR_ ## name ## c_setter_name) \
|
||||
},
|
||||
#include BUILTIN_INC_HEADER_NAME
|
||||
{
|
||||
LIT_MAGIC_STRING__COUNT,
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "ecma-alloc.h"
|
||||
#include "ecma-builtin-helpers.h"
|
||||
#include "ecma-builtins.h"
|
||||
#include "ecma-builtin-object.h"
|
||||
#include "ecma-conversion.h"
|
||||
#include "ecma-exceptions.h"
|
||||
#include "ecma-function-object.h"
|
||||
@@ -46,9 +47,11 @@ enum
|
||||
ECMA_OBJECT_PROTOTYPE_TO_STRING,
|
||||
ECMA_OBJECT_PROTOTYPE_VALUE_OF,
|
||||
ECMA_OBJECT_PROTOTYPE_TO_LOCALE_STRING,
|
||||
ECMA_OBJECT_PROTOTYPE_GET_PROTO,
|
||||
ECMA_OBJECT_PROTOTYPE_IS_PROTOTYPE_OF,
|
||||
ECMA_OBJECT_PROTOTYPE_HAS_OWN_PROPERTY,
|
||||
ECMA_OBJECT_PROTOTYPE_PROPERTY_IS_ENUMERABLE,
|
||||
ECMA_OBJECT_PROTOTYPE_SET_PROTO
|
||||
};
|
||||
|
||||
#define BUILTIN_INC_HEADER_NAME "ecma-builtin-object-prototype.inc.h"
|
||||
@@ -276,6 +279,14 @@ ecma_builtin_object_prototype_dispatch_routine (uint16_t builtin_routine_id, /**
|
||||
{
|
||||
ret_value = ecma_builtin_object_prototype_object_is_prototype_of (obj_p, arguments_list_p[0]);
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
else if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_GET_PROTO)
|
||||
{
|
||||
ret_value = ecma_builtin_object_object_get_prototype_of (obj_p);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015)*/
|
||||
|
||||
else
|
||||
{
|
||||
ret_value = ecma_builtin_object_prototype_object_to_locale_string (obj_p);
|
||||
@@ -288,6 +299,13 @@ ecma_builtin_object_prototype_dispatch_routine (uint16_t builtin_routine_id, /**
|
||||
|
||||
JERRY_ASSERT (builtin_routine_id >= ECMA_OBJECT_PROTOTYPE_HAS_OWN_PROPERTY);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (builtin_routine_id == ECMA_OBJECT_PROTOTYPE_SET_PROTO)
|
||||
{
|
||||
return ecma_builtin_object_object_set_proto (this_arg, arguments_list_p[0]);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015)*/
|
||||
|
||||
ecma_string_t *prop_name_p = ecma_op_to_prop_name (arguments_list_p[0]);
|
||||
|
||||
if (prop_name_p == NULL)
|
||||
|
||||
@@ -27,6 +27,11 @@ OBJECT_VALUE (LIT_MAGIC_STRING_CONSTRUCTOR,
|
||||
ECMA_BUILTIN_ID_OBJECT,
|
||||
ECMA_PROPERTY_CONFIGURABLE_WRITABLE)
|
||||
|
||||
ACCESSOR_READ_WRITE (LIT_MAGIC_STRING__PROTO__,
|
||||
ECMA_OBJECT_PROTOTYPE_GET_PROTO,
|
||||
ECMA_OBJECT_PROTOTYPE_SET_PROTO,
|
||||
ECMA_PROPERTY_FLAG_CONFIGURABLE)
|
||||
|
||||
/* Routine properties:
|
||||
* (property name, C routine name, arguments number or NON_FIXED, value of the routine's length property) */
|
||||
ROUTINE (LIT_MAGIC_STRING_TO_STRING_UL, ECMA_OBJECT_PROTOTYPE_TO_STRING, 0, 0)
|
||||
|
||||
@@ -221,6 +221,68 @@ ecma_builtin_object_object_set_prototype_of (ecma_value_t arg1, /**< routine's f
|
||||
return arg1;
|
||||
} /* ecma_builtin_object_object_set_prototype_of */
|
||||
|
||||
/**
|
||||
* The Object object's set __proto__ routine
|
||||
*
|
||||
* See also:
|
||||
* ECMA-262 v6, B.2.2.1.2
|
||||
*
|
||||
* @return ecma value
|
||||
* Returned value must be freed with ecma_free_value.
|
||||
*/
|
||||
ecma_value_t
|
||||
ecma_builtin_object_object_set_proto (ecma_value_t arg1, /**< routine's first argument */
|
||||
ecma_value_t arg2) /**< routine's second argument */
|
||||
{
|
||||
/* 1., 2. */
|
||||
if (ECMA_IS_VALUE_ERROR (ecma_op_check_object_coercible (arg1)))
|
||||
{
|
||||
return ECMA_VALUE_ERROR;
|
||||
}
|
||||
|
||||
/* 3. */
|
||||
if (!ecma_is_value_object (arg2) && !ecma_is_value_null (arg2))
|
||||
{
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
/* 4. */
|
||||
if (!ecma_is_value_object (arg1))
|
||||
{
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
}
|
||||
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (arg1);
|
||||
ecma_value_t status;
|
||||
|
||||
/* 5. */
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
|
||||
if (ECMA_OBJECT_IS_PROXY (obj_p))
|
||||
{
|
||||
status = ecma_proxy_object_set_prototype_of (obj_p, arg2);
|
||||
|
||||
if (ECMA_IS_VALUE_ERROR (status))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
|
||||
status = ecma_op_ordinary_object_set_prototype_of (obj_p, arg2);
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_PROXY)
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015_BUILTIN_PROXY) */
|
||||
|
||||
if (ecma_is_value_false (status))
|
||||
{
|
||||
return ecma_raise_type_error (ECMA_ERR_MSG ("Cannot set [[Prototype]]."));
|
||||
}
|
||||
|
||||
JERRY_ASSERT (ecma_is_value_true (status));
|
||||
|
||||
return ECMA_VALUE_UNDEFINED;
|
||||
} /* ecma_builtin_object_object_set_proto */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,10 @@ ecma_value_t ecma_builtin_object_object_get_prototype_of (ecma_object_t *obj_p);
|
||||
|
||||
ecma_value_t ecma_builtin_object_object_set_prototype_of (ecma_value_t arg1,
|
||||
ecma_value_t arg2);
|
||||
|
||||
ecma_value_t ecma_builtin_object_object_set_proto (ecma_value_t arg1,
|
||||
ecma_value_t arg2);
|
||||
|
||||
ecma_value_t ecma_builtin_object_object_prevent_extensions (ecma_object_t *obj_p);
|
||||
|
||||
ecma_value_t ecma_builtin_object_object_is_extensible (ecma_object_t *obj_p);
|
||||
|
||||
@@ -587,6 +587,7 @@ LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_MIN_VALUE_U, "MIN_VALUE")
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_TYPE_ERROR_UL, "TypeError")
|
||||
#endif
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_UNDEFINED_UL, "Undefined")
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING__PROTO__, "__proto__")
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_ARGUMENTS, "arguments")
|
||||
#if ENABLED (JERRY_ES2015_BUILTIN_REFLECT)
|
||||
LIT_MAGIC_STRING_DEF (LIT_MAGIC_STRING_CONSTRUCT, "construct")
|
||||
|
||||
@@ -240,6 +240,7 @@ LIT_MAGIC_STRING_MAX_VALUE_U = "MAX_VALUE"
|
||||
LIT_MAGIC_STRING_MIN_VALUE_U = "MIN_VALUE"
|
||||
LIT_MAGIC_STRING_TYPE_ERROR_UL = "TypeError"
|
||||
LIT_MAGIC_STRING_UNDEFINED_UL = "Undefined"
|
||||
LIT_MAGIC_STRING__PROTO__ = "__proto__"
|
||||
LIT_MAGIC_STRING_ARGUMENTS = "arguments"
|
||||
LIT_MAGIC_STRING_DECODE_URI = "decodeURI"
|
||||
LIT_MAGIC_STRING_ENCODE_URI = "encodeURI"
|
||||
|
||||
@@ -847,6 +847,10 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
|
||||
parser_stack_push_uint8 (context_p, PARSER_OBJECT_PROPERTY_START);
|
||||
#endif /* !ENABLED (JERRY_ES2015) */
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
bool proto_seen = false;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
while (true)
|
||||
{
|
||||
lexer_expect_object_literal_id (context_p, LEXER_OBJ_IDENT_NO_OPTS);
|
||||
@@ -991,6 +995,20 @@ parser_parse_object_literal (parser_context_t *context_p) /**< context */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
default:
|
||||
{
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if ((context_p->token.lit_location.type == LEXER_IDENT_LITERAL
|
||||
|| context_p->token.lit_location.type == LEXER_STRING_LITERAL)
|
||||
&& lexer_compare_literal_to_string (context_p, "__proto__", 9))
|
||||
{
|
||||
if (proto_seen)
|
||||
{
|
||||
parser_raise_error (context_p, PARSER_ERR_DUPLICATED_PROTO);
|
||||
}
|
||||
|
||||
proto_seen = true;
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
uint16_t literal_index = context_p->lit_object.index;
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
|
||||
@@ -966,6 +966,10 @@ parser_error_to_string (parser_error_t error) /**< error code */
|
||||
{
|
||||
return "for in-of loop variable declaration may not have an initializer.";
|
||||
}
|
||||
case PARSER_ERR_DUPLICATED_PROTO:
|
||||
{
|
||||
return "Duplicate __proto__ fields are not allowed in object literals.";
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
case PARSER_ERR_DELETE_IDENT_NOT_ALLOWED:
|
||||
{
|
||||
|
||||
@@ -81,6 +81,7 @@ typedef enum
|
||||
PARSER_ERR_USE_STRICT_NOT_ALLOWED, /**< use strict directive is not allowed */
|
||||
PARSER_ERR_YIELD_NOT_ALLOWED, /**< yield keyword is not allowed */
|
||||
PARSER_ERR_FOR_IN_OF_DECLARATION, /**< variable declaration in for-in or for-of loop */
|
||||
PARSER_ERR_DUPLICATED_PROTO, /**< duplicated __proto__ fields are not allowed */
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
PARSER_ERR_DELETE_IDENT_NOT_ALLOWED, /**< identifier delete is not allowed in strict mode */
|
||||
PARSER_ERR_EVAL_CANNOT_ASSIGNED, /**< eval cannot be assigned in strict mode */
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "ecma-alloc.h"
|
||||
#include "ecma-array-object.h"
|
||||
#include "ecma-builtins.h"
|
||||
#include "ecma-builtin-object.h"
|
||||
#include "ecma-comparison.h"
|
||||
#include "ecma-conversion.h"
|
||||
#include "ecma-exceptions.h"
|
||||
@@ -91,6 +92,15 @@ vm_op_get_value (ecma_value_t object, /**< base object */
|
||||
else if (ecma_is_value_string (property))
|
||||
{
|
||||
property_name_p = ecma_get_string_from_value (property);
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
if (JERRY_UNLIKELY (ecma_compare_ecma_string_to_magic_id (property_name_p, LIT_MAGIC_STRING__PROTO__)))
|
||||
{
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (object);
|
||||
|
||||
return ecma_builtin_object_object_get_prototype_of (obj_p);
|
||||
}
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
}
|
||||
|
||||
#if ENABLED (JERRY_ES2015)
|
||||
@@ -1586,6 +1596,13 @@ vm_loop (vm_frame_ctx_t *frame_ctx_p) /**< frame context */
|
||||
}
|
||||
|
||||
const int index = (int) (opcode_data >> VM_OC_NON_STATIC_SHIFT) - 2;
|
||||
|
||||
if (JERRY_UNLIKELY (ecma_compare_ecma_string_to_magic_id (prop_name_p, LIT_MAGIC_STRING__PROTO__)))
|
||||
{
|
||||
result = ecma_builtin_object_object_set_proto (stack_top_p[index], left_value);
|
||||
ecma_deref_ecma_string (prop_name_p);
|
||||
goto free_both_values;
|
||||
}
|
||||
#else /* !ENABLED (JERRY_ES2015) */
|
||||
const int index = -1;
|
||||
#endif /* ENABLED (JERRY_ES2015) */
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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.
|
||||
|
||||
try {
|
||||
eval('var o = { __proto__ : 5, __proto__ : 5 }');
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof SyntaxError);
|
||||
}
|
||||
|
||||
var f = function(){};
|
||||
assert((new f()).__proto__ === f.prototype);
|
||||
|
||||
var o = {};
|
||||
o.__proto__ = Array.prototype;
|
||||
assert(o instanceof Array);
|
||||
|
||||
var obj = Object.create(null)
|
||||
p = {};
|
||||
obj.__proto__ = p;
|
||||
assert(Object.getPrototypeOf(obj) !== p);
|
||||
|
||||
var Circle = function () {};
|
||||
var shape = {};
|
||||
var circle = new Circle();
|
||||
|
||||
shape.__proto__ = circle;
|
||||
|
||||
assert(Object.getPrototypeOf(shape) === circle);
|
||||
assert(shape.__proto__ === circle);
|
||||
|
||||
assert(Object.prototype.hasOwnProperty('__proto__') === true);
|
||||
|
||||
var desc = Object.getOwnPropertyDescriptor(Object.prototype,"__proto__");
|
||||
assert((desc && "get" in desc && "set" in desc && desc.configurable && !desc.enumerable) === true);
|
||||
|
||||
assert((Object.getOwnPropertyNames(Object.prototype).indexOf('__proto__') > -1) === true);
|
||||
|
||||
try {
|
||||
shape.__proto__ = shape;
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e instanceof TypeError);
|
||||
}
|
||||
|
||||
var o2 = { ["__proto__"] : null };
|
||||
assert(o2.__proto__ === null);
|
||||
Reference in New Issue
Block a user