Fix Array.prototype.concat() when 'this' is not an array.
JerryScript-DCO-1.0-Signed-off-by: Dániel Bátyai dbatyai.u-szeged@partner.samsung.com
This commit is contained in:
@@ -239,129 +239,48 @@ ecma_builtin_array_prototype_object_concat (ecma_value_t this_arg, /**< this arg
|
||||
ecma_length_t args_number) /**< number of arguments */
|
||||
{
|
||||
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
|
||||
|
||||
/* 1. */
|
||||
ECMA_TRY_CATCH (obj_this,
|
||||
ecma_op_to_object (this_arg),
|
||||
ret_value);
|
||||
|
||||
ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
|
||||
ecma_string_t *magic_string_length_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
|
||||
|
||||
ECMA_TRY_CATCH (len_value,
|
||||
ecma_op_object_get (obj_p, magic_string_length_p),
|
||||
ret_value);
|
||||
|
||||
ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value);
|
||||
|
||||
uint32_t len = ecma_number_to_uint32 (len_number);
|
||||
|
||||
uint32_t new_array_index = 0;
|
||||
|
||||
/* 2. */
|
||||
ecma_completion_value_t new_array = ecma_op_create_array_object (0, 0, false);
|
||||
ecma_object_t *new_array_p = ecma_get_object_from_completion_value (new_array);
|
||||
uint32_t new_length = 0;
|
||||
|
||||
/* 5.b - 5.c for this_arg */
|
||||
ECMA_TRY_CATCH (concat_this_value,
|
||||
ecma_builtin_helper_array_concat_value (new_array_p, &new_length, obj_this),
|
||||
ret_value);
|
||||
|
||||
for (uint32_t index = 0;
|
||||
index < len && ecma_is_completion_value_empty (ret_value);
|
||||
index++, new_array_index++)
|
||||
{
|
||||
ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index);
|
||||
ECMA_TRY_CATCH (get_value, ecma_op_object_get (obj_p, index_string_p), ret_value);
|
||||
|
||||
/* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */
|
||||
ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p,
|
||||
index_string_p,
|
||||
get_value,
|
||||
false);
|
||||
JERRY_ASSERT (ecma_is_completion_value_normal (put_comp));
|
||||
ecma_free_completion_value (put_comp);
|
||||
|
||||
ECMA_FINALIZE (get_value);
|
||||
ecma_deref_ecma_string (index_string_p);
|
||||
}
|
||||
|
||||
/* 5. */
|
||||
for (uint32_t arg_index = 0;
|
||||
arg_index < args_number && ecma_is_completion_value_empty (ret_value);
|
||||
arg_index++)
|
||||
{
|
||||
/* 5.b */
|
||||
if (ecma_is_value_object (args[arg_index]) &&
|
||||
(ecma_object_get_class_name (ecma_get_object_from_value (args[arg_index])) == LIT_MAGIC_STRING_ARRAY_UL))
|
||||
{
|
||||
/* 5.b.ii */
|
||||
ECMA_TRY_CATCH (arg_len_value,
|
||||
ecma_op_object_get (ecma_get_object_from_value (args[arg_index]),
|
||||
magic_string_length_p),
|
||||
ret_value);
|
||||
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_len_number, arg_len_value, ret_value);
|
||||
|
||||
uint32_t arg_len = ecma_number_to_uint32 (arg_len_number);
|
||||
|
||||
for (uint32_t array_index = 0;
|
||||
array_index < arg_len && ecma_is_completion_value_empty (ret_value);
|
||||
array_index++, new_array_index++)
|
||||
{
|
||||
ecma_string_t *array_index_string_p = ecma_new_ecma_string_from_uint32 (array_index);
|
||||
|
||||
/* 5.b.iii.2 */
|
||||
if (ecma_op_object_get_property (ecma_get_object_from_value (args[arg_index]),
|
||||
array_index_string_p) != NULL)
|
||||
{
|
||||
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (new_array_index);
|
||||
|
||||
ECMA_TRY_CATCH (get_value,
|
||||
ecma_op_object_get (ecma_get_object_from_value (args[arg_index]),
|
||||
array_index_string_p),
|
||||
ret_value);
|
||||
|
||||
/* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */
|
||||
ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p,
|
||||
new_array_index_string_p,
|
||||
get_value,
|
||||
false);
|
||||
JERRY_ASSERT (ecma_is_completion_value_normal (put_comp));
|
||||
ecma_free_completion_value (put_comp);
|
||||
|
||||
ECMA_FINALIZE (get_value);
|
||||
ecma_deref_ecma_string (new_array_index_string_p);
|
||||
}
|
||||
|
||||
ecma_deref_ecma_string (array_index_string_p);
|
||||
}
|
||||
|
||||
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
|
||||
ECMA_FINALIZE (arg_len_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (new_array_index);
|
||||
|
||||
/* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */
|
||||
ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p,
|
||||
new_array_index_string_p,
|
||||
args[arg_index],
|
||||
false);
|
||||
JERRY_ASSERT (ecma_is_completion_value_normal (put_comp));
|
||||
ecma_free_completion_value (put_comp);
|
||||
|
||||
ecma_deref_ecma_string (new_array_index_string_p);
|
||||
new_array_index++;
|
||||
}
|
||||
ECMA_TRY_CATCH (concat_value,
|
||||
ecma_builtin_helper_array_concat_value (new_array_p, &new_length, args[arg_index]),
|
||||
ret_value);
|
||||
ECMA_FINALIZE (concat_value);
|
||||
}
|
||||
|
||||
ECMA_FINALIZE (concat_this_value);
|
||||
|
||||
if (ecma_is_completion_value_empty (ret_value))
|
||||
{
|
||||
ECMA_TRY_CATCH (set_length_value,
|
||||
ecma_builtin_array_prototype_helper_set_length (new_array_p, new_length),
|
||||
ret_value);
|
||||
ret_value = new_array;
|
||||
ECMA_FINALIZE (set_length_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ecma_free_completion_value (new_array);
|
||||
}
|
||||
|
||||
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
|
||||
ECMA_FINALIZE (len_value);
|
||||
ecma_deref_ecma_string (magic_string_length_p);
|
||||
ECMA_FINALIZE (obj_this);
|
||||
|
||||
return ret_value;
|
||||
|
||||
@@ -325,6 +325,132 @@ ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */
|
||||
return norm_index;
|
||||
} /* ecma_builtin_helper_array_index_normalize */
|
||||
|
||||
/**
|
||||
* Helper function for concatenating an ecma_value_t to an Array.
|
||||
*
|
||||
* See also:
|
||||
* ECMA-262 v5, 15.4.4.4 steps 5.b - 5.c
|
||||
*
|
||||
* Used by:
|
||||
* - The Array.prototype.concat routine.
|
||||
*
|
||||
* @return completion value
|
||||
* Returned value must be freed with ecma_free_completion_value.
|
||||
*/
|
||||
ecma_completion_value_t
|
||||
ecma_builtin_helper_array_concat_value (ecma_object_t *obj_p, /**< array */
|
||||
uint32_t *length_p, /**< in-out: array's length */
|
||||
ecma_value_t value) /**< value to concat */
|
||||
{
|
||||
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
|
||||
|
||||
/* 5.b */
|
||||
if (ecma_is_value_object (value)
|
||||
&& (ecma_object_get_class_name (ecma_get_object_from_value (value)) == LIT_MAGIC_STRING_ARRAY_UL))
|
||||
{
|
||||
ecma_string_t *magic_string_length_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
|
||||
/* 5.b.ii */
|
||||
ECMA_TRY_CATCH (arg_len_value,
|
||||
ecma_op_object_get (ecma_get_object_from_value (value),
|
||||
magic_string_length_p),
|
||||
ret_value);
|
||||
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_len_number, arg_len_value, ret_value);
|
||||
|
||||
uint32_t arg_len = ecma_number_to_uint32 (arg_len_number);
|
||||
|
||||
/* 5.b.iii */
|
||||
for (uint32_t array_index = 0;
|
||||
array_index < arg_len && ecma_is_completion_value_empty (ret_value);
|
||||
array_index++)
|
||||
{
|
||||
ecma_string_t *array_index_string_p = ecma_new_ecma_string_from_uint32 (array_index);
|
||||
|
||||
/* 5.b.iii.2 */
|
||||
if (ecma_op_object_get_property (ecma_get_object_from_value (value),
|
||||
array_index_string_p) != NULL)
|
||||
{
|
||||
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (*length_p + array_index);
|
||||
|
||||
/* 5.b.iii.3.a */
|
||||
ECMA_TRY_CATCH (get_value,
|
||||
ecma_op_object_get (ecma_get_object_from_value (value),
|
||||
array_index_string_p),
|
||||
ret_value);
|
||||
|
||||
ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor ();
|
||||
{
|
||||
prop_desc.is_value_defined = true;
|
||||
prop_desc.value = get_value;
|
||||
|
||||
prop_desc.is_writable_defined = true;
|
||||
prop_desc.is_writable = true;
|
||||
|
||||
prop_desc.is_enumerable_defined = true;
|
||||
prop_desc.is_enumerable = true;
|
||||
|
||||
prop_desc.is_configurable_defined = true;
|
||||
prop_desc.is_configurable = true;
|
||||
}
|
||||
|
||||
/* 5.b.iii.3.b */
|
||||
/* This will always be a simple value since 'is_throw' is false, so no need to free. */
|
||||
ecma_completion_value_t put_comp = ecma_op_object_define_own_property (obj_p,
|
||||
new_array_index_string_p,
|
||||
&prop_desc,
|
||||
false);
|
||||
JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp));
|
||||
|
||||
ECMA_FINALIZE (get_value);
|
||||
ecma_deref_ecma_string (new_array_index_string_p);
|
||||
}
|
||||
|
||||
ecma_deref_ecma_string (array_index_string_p);
|
||||
}
|
||||
|
||||
*length_p += arg_len;
|
||||
|
||||
ECMA_OP_TO_NUMBER_FINALIZE (arg_len_number);
|
||||
ECMA_FINALIZE (arg_len_value);
|
||||
ecma_deref_ecma_string (magic_string_length_p);
|
||||
}
|
||||
else
|
||||
{
|
||||
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 ((*length_p)++);
|
||||
|
||||
ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor ();
|
||||
{
|
||||
prop_desc.is_value_defined = true;
|
||||
prop_desc.value = value;
|
||||
|
||||
prop_desc.is_writable_defined = true;
|
||||
prop_desc.is_writable = true;
|
||||
|
||||
prop_desc.is_enumerable_defined = true;
|
||||
prop_desc.is_enumerable = true;
|
||||
|
||||
prop_desc.is_configurable_defined = true;
|
||||
prop_desc.is_configurable = true;
|
||||
}
|
||||
|
||||
/* 5.c.i */
|
||||
/* This will always be a simple value since 'is_throw' is false, so no need to free. */
|
||||
ecma_completion_value_t put_comp = ecma_op_object_define_own_property (obj_p,
|
||||
new_array_index_string_p,
|
||||
&prop_desc,
|
||||
false);
|
||||
JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp));
|
||||
|
||||
ecma_deref_ecma_string (new_array_index_string_p);
|
||||
}
|
||||
|
||||
if (ecma_is_completion_value_empty (ret_value))
|
||||
{
|
||||
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_TRUE);
|
||||
}
|
||||
|
||||
return ret_value;
|
||||
} /* ecma_builtin_helper_array_concat_value */
|
||||
|
||||
/**
|
||||
* Helper function to normalizing a string index
|
||||
*
|
||||
|
||||
@@ -30,6 +30,9 @@ extern ecma_completion_value_t ecma_builtin_helper_object_to_string (const ecma_
|
||||
extern ecma_completion_value_t ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, uint32_t index);
|
||||
extern ecma_completion_value_t ecma_builtin_helper_object_get_properties (ecma_object_t *obj,
|
||||
bool only_enumerable_properties);
|
||||
extern ecma_completion_value_t ecma_builtin_helper_array_concat_value (ecma_object_t *obj,
|
||||
uint32_t *length,
|
||||
ecma_value_t);
|
||||
extern uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length);
|
||||
extern uint32_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length);
|
||||
|
||||
|
||||
@@ -21,10 +21,17 @@ for (i = 0; i < array.length; i++) {
|
||||
assert(array[i] === new_arr[i]);
|
||||
}
|
||||
|
||||
var obj = {};
|
||||
var obj = { concat : Array.prototype.concat };
|
||||
var arr1 = ["Apple", 6, "Peach"];
|
||||
var arr2 = [obj, "Cherry", "Grape"];
|
||||
|
||||
var new_array = obj.concat(arr1);
|
||||
assert(new_array.length === 4);
|
||||
assert(new_array[0] === obj);
|
||||
assert(new_array[1] === "Apple");
|
||||
assert(new_array[2] === 6);
|
||||
assert(new_array[3] === "Peach");
|
||||
|
||||
var new_array = arr1.concat(arr2, obj, 1);
|
||||
|
||||
assert(new_array.length === 8);
|
||||
@@ -50,24 +57,19 @@ for (i = 0; i < result.length; i++) {
|
||||
assert(result[i] === expected[i]);
|
||||
}
|
||||
|
||||
// Checking behavior when unable to get length
|
||||
var obj = { concat : Array.prototype.concat }
|
||||
Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } });
|
||||
|
||||
try {
|
||||
obj.concat();
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e.message === "foo");
|
||||
assert(e instanceof ReferenceError);
|
||||
}
|
||||
var arr1 = [];
|
||||
arr1.length = 2;
|
||||
var arr2 = [];
|
||||
arr2.length = 3;
|
||||
assert(arr1.concat(arr2).length === arr1.length + arr2.length);
|
||||
|
||||
// Checking behavior when unable to get element
|
||||
var obj = { concat : Array.prototype.concat, length : 1 }
|
||||
Object.defineProperty(obj, '0', { 'get' : function () {throw new ReferenceError ("foo"); } });
|
||||
var arr = []
|
||||
Object.defineProperty(arr, '0', { 'get' : function () {throw new ReferenceError ("foo"); } });
|
||||
arr.length = 1;
|
||||
|
||||
try {
|
||||
obj.concat();
|
||||
arr.concat();
|
||||
assert(false);
|
||||
} catch (e) {
|
||||
assert(e.message === "foo");
|
||||
|
||||
Reference in New Issue
Block a user