Properly handle primitive arguments in Object methods (#3342)

ES2015 allows primitive arguments for most of the Object built-ins.
This change implements handling for these arguments in affected methods.

JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai@inf.u-szeged.hu
This commit is contained in:
Dániel Bátyai 2019-11-22 12:48:10 +01:00 committed by Robert Fancsik
parent d006f068f4
commit 3bf2bc50bc
10 changed files with 317 additions and 171 deletions

View File

@ -40,23 +40,32 @@
enum
{
ECMA_OBJECT_ROUTINE_START = ECMA_BUILTIN_ID__COUNT - 1,
ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY,
ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR,
ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES,
ECMA_OBJECT_ROUTINE_CREATE,
ECMA_OBJECT_ROUTINE_SEAL,
ECMA_OBJECT_ROUTINE_FREEZE,
ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS,
ECMA_OBJECT_ROUTINE_IS_SEALED,
ECMA_OBJECT_ROUTINE_IS_FROZEN,
ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE,
ECMA_OBJECT_ROUTINE_IS,
ECMA_OBJECT_ROUTINE_SET_PROTOTYPE_OF,
/* These should be in this order. */
ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY,
ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES,
/* These should be in this order. */
ECMA_OBJECT_ROUTINE_ASSIGN,
ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR,
ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES,
ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS,
ECMA_OBJECT_ROUTINE_KEYS,
ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF,
ECMA_OBJECT_ROUTINE_SET_PROTOTYPE_OF,
ECMA_OBJECT_ROUTINE_ASSIGN,
ECMA_OBJECT_ROUTINE_IS,
ECMA_OBJECT_ROUTINE_KEYS,
/* These should be in this order. */
ECMA_OBJECT_ROUTINE_FREEZE,
ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS,
ECMA_OBJECT_ROUTINE_SEAL,
/* These should be in this order. */
ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE,
ECMA_OBJECT_ROUTINE_IS_FROZEN,
ECMA_OBJECT_ROUTINE_IS_SEALED,
};
#define BUILTIN_INC_HEADER_NAME "ecma-builtin-object.inc.h"
@ -125,47 +134,18 @@ ecma_builtin_object_dispatch_construct (const ecma_value_t *arguments_list_p, /*
* Returned value must be freed with ecma_free_value.
*/
ecma_value_t
ecma_builtin_object_object_get_prototype_of (ecma_value_t arg) /**< routine's argument */
ecma_builtin_object_object_get_prototype_of (ecma_object_t *obj_p) /**< routine's argument */
{
ecma_value_t ret_value = ECMA_VALUE_EMPTY;
bool was_object = ecma_is_value_object (arg);
/* 1. */
if (!was_object)
{
#if ENABLED (JERRY_ES2015_BUILTIN)
arg = ecma_op_to_object (arg);
if (ECMA_IS_VALUE_ERROR (arg))
{
return arg;
}
#else /* !ENABLED (JERRY_ES2015_BUILTIN) */
return ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not an object."));
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
}
/* 2. */
ecma_object_t *obj_p = ecma_get_object_from_value (arg);
jmem_cpointer_t prototype_cp = obj_p->u2.prototype_cp;
if (prototype_cp != JMEM_CP_NULL)
{
ecma_object_t *prototype_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, prototype_cp);
ret_value = ecma_make_object_value (prototype_p);
ecma_ref_object (prototype_p);
}
else
{
ret_value = ECMA_VALUE_NULL;
return ecma_make_object_value (prototype_p);
}
#if ENABLED (JERRY_ES2015_BUILTIN)
if (!was_object)
{
ecma_deref_object (obj_p);
}
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
return ret_value;
return ECMA_VALUE_NULL;
} /* ecma_builtin_object_object_get_prototype_of */
#if ENABLED (JERRY_ES2015_BUILTIN)
@ -772,31 +752,14 @@ ecma_builtin_object_object_define_property (ecma_object_t *obj_p, /**< routine's
* Returned value must be freed with ecma_free_value.
*/
static ecma_value_t
ecma_builtin_object_object_assign (const ecma_value_t arguments_list_p[], /**< arguments list */
ecma_builtin_object_object_assign (ecma_object_t *target_p, /**< target object */
const ecma_value_t arguments_list_p[], /**< arguments list */
ecma_length_t arguments_list_len) /**< number of arguments */
{
ecma_value_t target = arguments_list_len > 0 ? arguments_list_p[0] : ECMA_VALUE_UNDEFINED;
/* 1. */
ecma_value_t to_value = ecma_op_to_object (target);
if (ECMA_IS_VALUE_ERROR (to_value))
{
return to_value;
}
ecma_object_t *to_obj_p = ecma_get_object_from_value (to_value);
/* 2. */
if (arguments_list_len == 1)
{
return to_value;
}
ecma_value_t ret_value = ECMA_VALUE_EMPTY;
/* 4-5. */
for (uint32_t i = 1; i < arguments_list_len && ecma_is_value_empty (ret_value); i++)
for (uint32_t i = 0; i < arguments_list_len && ecma_is_value_empty (ret_value); i++)
{
ecma_value_t next_source = arguments_list_p[i];
@ -848,7 +811,7 @@ ecma_builtin_object_object_assign (const ecma_value_t arguments_list_p[], /**< a
else
{
/* 5.c.iii.3 */
ecma_value_t status = ecma_op_object_put (to_obj_p, property_name_p, prop_value, true);
ecma_value_t status = ecma_op_object_put (target_p, property_name_p, prop_value, true);
/* 5.c.iii.4 */
if (ECMA_IS_VALUE_ERROR (status))
@ -869,10 +832,10 @@ ecma_builtin_object_object_assign (const ecma_value_t arguments_list_p[], /**< a
/* 6. */
if (ecma_is_value_empty (ret_value))
{
return to_value;
ecma_ref_object (target_p);
return ecma_make_object_value (target_p);
}
ecma_deref_object (to_obj_p);
return ret_value;
} /* ecma_builtin_object_object_assign */
#endif /* ENABLED (JERRY_ES2015_BUILTIN) */
@ -924,19 +887,11 @@ ecma_builtin_object_dispatch_routine (uint16_t builtin_routine_id, /**< built-in
{
return ecma_builtin_object_object_create (arg1, arg2);
}
case ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF:
{
return ecma_builtin_object_object_get_prototype_of (arg1);
}
#if ENABLED (JERRY_ES2015_BUILTIN)
case ECMA_OBJECT_ROUTINE_SET_PROTOTYPE_OF:
{
return ecma_builtin_object_object_set_prototype_of (arg1, arg2);
}
case ECMA_OBJECT_ROUTINE_ASSIGN:
{
return ecma_builtin_object_object_assign (arguments_list_p, arguments_number);
}
case ECMA_OBJECT_ROUTINE_IS:
{
return ecma_builtin_object_object_is (arg1, arg2);
@ -948,83 +903,169 @@ ecma_builtin_object_dispatch_routine (uint16_t builtin_routine_id, /**< built-in
}
}
ecma_object_t *obj_p;
#if !ENABLED (JERRY_ES2015)
if (!ecma_is_value_object (arg1))
{
return ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not an object."));
}
#endif /* !ENABLED (JERRY_ES2015) */
ecma_object_t *obj_p = ecma_get_object_from_value (arg1);
if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR)
if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES)
{
ecma_string_t *prop_name_p = ecma_op_to_prop_name (arg2);
if (prop_name_p == NULL)
#if ENABLED (JERRY_ES2015)
if (!ecma_is_value_object (arg1))
{
return ECMA_VALUE_ERROR;
return ecma_raise_type_error (ECMA_ERR_MSG ("Argument is not an object."));
}
#endif /* ENABLED (JERRY_ES2015) */
ecma_value_t ret_value;
obj_p = ecma_get_object_from_value (arg1);
if (builtin_routine_id == ECMA_OBJECT_ROUTINE_DEFINE_PROPERTY)
{
ret_value = ecma_builtin_object_object_define_property (obj_p, prop_name_p, arguments_list_p[2]);
}
else
{
JERRY_ASSERT (builtin_routine_id == ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR);
ret_value = ecma_builtin_object_object_get_own_property_descriptor (obj_p, prop_name_p);
ecma_string_t *prop_name_p = ecma_op_to_prop_name (arg2);
if (prop_name_p == NULL)
{
return ECMA_VALUE_ERROR;
}
ecma_value_t result = ecma_builtin_object_object_define_property (obj_p, prop_name_p, arguments_list_p[2]);
ecma_deref_ecma_string (prop_name_p);
return result;
}
ecma_deref_ecma_string (prop_name_p);
return ret_value;
JERRY_ASSERT (builtin_routine_id == ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES);
return ecma_builtin_object_object_define_properties (obj_p, arg2);
}
switch (builtin_routine_id)
else if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_KEYS)
{
case ECMA_OBJECT_ROUTINE_SEAL:
{
return ecma_builtin_object_object_seal (obj_p);
}
case ECMA_OBJECT_ROUTINE_FREEZE:
{
return ecma_builtin_object_object_freeze (obj_p);
}
case ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS:
{
return ecma_builtin_object_object_prevent_extensions (obj_p);
}
case ECMA_OBJECT_ROUTINE_IS_SEALED:
case ECMA_OBJECT_ROUTINE_IS_FROZEN:
{
return ecma_builtin_object_frozen_or_sealed_helper (obj_p,
builtin_routine_id);
}
case ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE:
{
return ecma_builtin_object_object_is_extensible (obj_p);
}
case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES:
{
return ecma_builtin_object_object_get_own_property_names (obj_p);
}
#if ENABLED (JERRY_ES2015)
case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS:
ecma_value_t object = ecma_op_to_object (arg1);
if (ECMA_IS_VALUE_ERROR (object))
{
return ecma_builtin_object_object_get_own_property_symbols (obj_p);
return object;
}
obj_p = ecma_get_object_from_value (object);
#else /* !ENABLED (JERRY_ES2015) */
obj_p = ecma_get_object_from_value (arg1);
#endif /* ENABLED (JERRY_ES2015) */
ecma_value_t result;
switch (builtin_routine_id)
{
case ECMA_OBJECT_ROUTINE_GET_PROTOTYPE_OF:
{
result = ecma_builtin_object_object_get_prototype_of (obj_p);
break;
}
case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_NAMES:
{
result = ecma_builtin_object_object_get_own_property_names (obj_p);
break;
}
#if ENABLED (JERRY_ES2015)
case ECMA_OBJECT_ROUTINE_ASSIGN:
{
result = ecma_builtin_object_object_assign (obj_p, arguments_list_p + 1, arguments_number - 1);
break;
}
case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_SYMBOLS:
{
result = ecma_builtin_object_object_get_own_property_symbols (obj_p);
break;
}
#endif /* ENABLED (JERRY_ES2015) */
case ECMA_OBJECT_ROUTINE_KEYS:
{
result = ecma_builtin_object_object_keys (obj_p);
break;
}
case ECMA_OBJECT_ROUTINE_GET_OWN_PROPERTY_DESCRIPTOR:
{
ecma_string_t *prop_name_p = ecma_op_to_prop_name (arg2);
if (prop_name_p == NULL)
{
result = ECMA_VALUE_ERROR;
break;
}
result = ecma_builtin_object_object_get_own_property_descriptor (obj_p, prop_name_p);
ecma_deref_ecma_string (prop_name_p);
break;
}
default:
{
JERRY_UNREACHABLE ();
}
}
#if ENABLED (JERRY_ES2015)
ecma_deref_object (obj_p);
#endif /* ENABLED (JERRY_ES2015) */
return result;
}
else if (builtin_routine_id <= ECMA_OBJECT_ROUTINE_SEAL)
{
#if ENABLED (JERRY_ES2015)
if (!ecma_is_value_object (arg1))
{
return arg1;
}
#endif /* ENABLED (JERRY_ES2015) */
case ECMA_OBJECT_ROUTINE_KEYS:
obj_p = ecma_get_object_from_value (arg1);
switch (builtin_routine_id)
{
return ecma_builtin_object_object_keys (obj_p);
case ECMA_OBJECT_ROUTINE_SEAL:
{
return ecma_builtin_object_object_seal (obj_p);
}
case ECMA_OBJECT_ROUTINE_FREEZE:
{
return ecma_builtin_object_object_freeze (obj_p);
}
case ECMA_OBJECT_ROUTINE_PREVENT_EXTENSIONS:
{
return ecma_builtin_object_object_prevent_extensions (obj_p);
}
default:
{
JERRY_UNREACHABLE ();
}
}
case ECMA_OBJECT_ROUTINE_DEFINE_PROPERTIES:
}
else
{
JERRY_ASSERT (builtin_routine_id <= ECMA_OBJECT_ROUTINE_IS_SEALED);
#if ENABLED (JERRY_ES2015)
if (!ecma_is_value_object (arg1))
{
return ecma_builtin_object_object_define_properties (obj_p, arg2);
return ecma_make_boolean_value (builtin_routine_id != ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE);
}
default:
#endif /* ENABLED (JERRY_ES2015) */
obj_p = ecma_get_object_from_value (arg1);
switch (builtin_routine_id)
{
JERRY_UNREACHABLE ();
case ECMA_OBJECT_ROUTINE_IS_SEALED:
case ECMA_OBJECT_ROUTINE_IS_FROZEN:
{
return ecma_builtin_object_frozen_or_sealed_helper (obj_p,
builtin_routine_id);
}
case ECMA_OBJECT_ROUTINE_IS_EXTENSIBLE:
{
return ecma_builtin_object_object_is_extensible (obj_p);
}
default:
{
JERRY_UNREACHABLE ();
}
}
}
} /* ecma_builtin_object_dispatch_routine */

View File

@ -15,7 +15,7 @@
#ifndef ECMA_BUILTIN_OBJECT_H
#define ECMA_BUILTIN_OBJECT_H
ecma_value_t ecma_builtin_object_object_get_prototype_of (ecma_value_t arg);
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);

View File

@ -87,7 +87,7 @@ ecma_builtin_reflect_dispatch_routine (uint16_t builtin_routine_id, /**< built-i
{
case ECMA_REFLECT_OBJECT_GET_PROTOTYPE_OF_UL:
{
return ecma_builtin_object_object_get_prototype_of (arguments_list[0]);
return ecma_builtin_object_object_get_prototype_of (ecma_get_object_from_value (arguments_list[0]));
}
case ECMA_REFLECT_OBJECT_SET_PROTOTYPE_OF_UL:
{

View File

@ -109,11 +109,3 @@ assert (props.indexOf(foo2) !== -1);
assert (props.length === 2);
assert (Object.getOwnPropertyDescriptor (object, foo).enumerable === true);
assert (Object.getOwnPropertyDescriptor (object, foo2).enumerable === false);
// Test non-object argument
try {
Object.getOwnPropertySymbols ('hello');
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}

View File

@ -0,0 +1,48 @@
// 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.
assert (JSON.stringify (Object.getOwnPropertyNames("hello")) === '["0","1","2","3","4","length"]');
var n = Object.assign(42, {a: "str"});
assert (n instanceof Number);
assert (n.valueOf() === 42);
assert (n.a === "str");
assert (JSON.stringify (Object.getOwnPropertySymbols("hello")) === '[]');
assert (JSON.stringify (Object.keys("str")) === '["0","1","2"]');
var d = Object.getOwnPropertyDescriptor("hello", '1');
assert (d.value === "e");
assert (d.writable === false);
assert (d.enumerable === true);
assert (d.configurable === false);
assert (Object.seal(42) === 42);
assert (Object.seal(undefined) === undefined);
assert (Object.isSealed(42) === true);
assert (Object.isSealed(undefined) === true);
assert (Object.freeze(42) === 42);
assert (Object.freeze(undefined) === undefined);
assert (Object.isFrozen(42) === true);
assert (Object.isFrozen(undefined) === true);
assert (Object.preventExtensions(42) === 42);
assert (Object.preventExtensions(undefined) === undefined);
assert (Object.isExtensible(42) === false);
assert (Object.isExtensible(undefined) === false);

View File

@ -0,0 +1,89 @@
// 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 {
Object.getOwnPropertyNames("hello");
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.preventExtensions(42);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.isExtensible(42);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.seal(42);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.isSealed(42);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.freeze(42);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.isFrozen(42);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try {
Object.keys("hello");
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try {
Object.getOwnPropertyNames("hello");
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try {
Object.getOwnPropertyDescriptor("hello", '1');
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}

View File

@ -68,3 +68,12 @@ try {
} catch (err) {
assert (err === 234);
}
try {
Object.defineProperty(42, "prop", {
set: undefined
});
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}

View File

@ -62,11 +62,3 @@ assert (props.indexOf("prop") !== -1);
assert (props.indexOf("method") !== -1);
assert (props.length === 2);
// Test non-object argument
try {
Object.getOwnPropertyNames("hello");
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}

View File

@ -20,23 +20,6 @@ assert (Object.isExtensible(empty) === true);
Object.preventExtensions(empty);
assert(Object.isExtensible(empty) === false);
// Call on undefined should throw TypeError.
try
{
Object.isExtensible(undefined);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
try
{
Object.preventExtensions(undefined);
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
// Sealed objects are by definition non-extensible.
var sealed = Object.seal({});
assert (Object.isExtensible(sealed) === false);

View File

@ -61,14 +61,6 @@ assert (props.indexOf("prop") !== -1);
assert (props.indexOf("method") !== -1);
assert (props.length === 2);
// Test non-object argument
try {
Object.keys("hello");
assert (false);
} catch (e) {
assert (e instanceof TypeError);
}
var o = {};
Object.defineProperty(o, 'a', {