Skip to content

Commit f0500cb

Browse files
committed
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
1 parent b414329 commit f0500cb

File tree

4 files changed

+163
-113
lines changed

4 files changed

+163
-113
lines changed

jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.cpp

Lines changed: 17 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -91,129 +91,48 @@ ecma_builtin_array_prototype_object_concat (ecma_value_t this_arg, /**< this arg
9191
ecma_length_t args_number) /**< number of arguments */
9292
{
9393
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
94+
9495
/* 1. */
9596
ECMA_TRY_CATCH (obj_this,
9697
ecma_op_to_object (this_arg),
9798
ret_value);
9899

99-
ecma_object_t *obj_p = ecma_get_object_from_value (obj_this);
100-
ecma_string_t *magic_string_length_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
101-
102-
ECMA_TRY_CATCH (len_value,
103-
ecma_op_object_get (obj_p, magic_string_length_p),
104-
ret_value);
105-
106-
ECMA_OP_TO_NUMBER_TRY_CATCH (len_number, len_value, ret_value);
107-
108-
uint32_t len = ecma_number_to_uint32 (len_number);
109-
110-
uint32_t new_array_index = 0;
111-
112100
/* 2. */
113101
ecma_completion_value_t new_array = ecma_op_create_array_object (0, 0, false);
114102
ecma_object_t *new_array_p = ecma_get_object_from_completion_value (new_array);
103+
uint32_t new_length = 0;
115104

105+
/* 5.b - 5.c for this_arg */
106+
ECMA_TRY_CATCH (concat_this_value,
107+
ecma_builtin_helper_array_concat_value (new_array_p, &new_length, obj_this),
108+
ret_value);
116109

117-
for (uint32_t index = 0;
118-
index < len && ecma_is_completion_value_empty (ret_value);
119-
index++, new_array_index++)
120-
{
121-
ecma_string_t *index_string_p = ecma_new_ecma_string_from_uint32 (index);
122-
ECMA_TRY_CATCH (get_value, ecma_op_object_get (obj_p, index_string_p), ret_value);
123-
124-
/* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */
125-
ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p,
126-
index_string_p,
127-
get_value,
128-
false);
129-
JERRY_ASSERT (ecma_is_completion_value_normal (put_comp));
130-
ecma_free_completion_value (put_comp);
131-
132-
ECMA_FINALIZE (get_value);
133-
ecma_deref_ecma_string (index_string_p);
134-
}
135-
110+
/* 5. */
136111
for (uint32_t arg_index = 0;
137112
arg_index < args_number && ecma_is_completion_value_empty (ret_value);
138113
arg_index++)
139114
{
140-
/* 5.b */
141-
if (ecma_is_value_object (args[arg_index]) &&
142-
(ecma_object_get_class_name (ecma_get_object_from_value (args[arg_index])) == LIT_MAGIC_STRING_ARRAY_UL))
143-
{
144-
/* 5.b.ii */
145-
ECMA_TRY_CATCH (arg_len_value,
146-
ecma_op_object_get (ecma_get_object_from_value (args[arg_index]),
147-
magic_string_length_p),
148-
ret_value);
149-
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_len_number, arg_len_value, ret_value);
150-
151-
uint32_t arg_len = ecma_number_to_uint32 (arg_len_number);
152-
153-
for (uint32_t array_index = 0;
154-
array_index < arg_len && ecma_is_completion_value_empty (ret_value);
155-
array_index++, new_array_index++)
156-
{
157-
ecma_string_t *array_index_string_p = ecma_new_ecma_string_from_uint32 (array_index);
158-
159-
/* 5.b.iii.2 */
160-
if (ecma_op_object_get_property (ecma_get_object_from_value (args[arg_index]),
161-
array_index_string_p) != NULL)
162-
{
163-
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (new_array_index);
164-
165-
ECMA_TRY_CATCH (get_value,
166-
ecma_op_object_get (ecma_get_object_from_value (args[arg_index]),
167-
array_index_string_p),
168-
ret_value);
169-
170-
/* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */
171-
ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p,
172-
new_array_index_string_p,
173-
get_value,
174-
false);
175-
JERRY_ASSERT (ecma_is_completion_value_normal (put_comp));
176-
ecma_free_completion_value (put_comp);
177-
178-
ECMA_FINALIZE (get_value);
179-
ecma_deref_ecma_string (new_array_index_string_p);
180-
}
181-
182-
ecma_deref_ecma_string (array_index_string_p);
183-
}
184-
185-
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
186-
ECMA_FINALIZE (arg_len_value);
187-
}
188-
else
189-
{
190-
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (new_array_index);
191-
192-
/* Using [[Put]] is equvalent to [[DefineOwnProperty]] in this case, so we use it for simplicity. */
193-
ecma_completion_value_t put_comp = ecma_op_object_put (new_array_p,
194-
new_array_index_string_p,
195-
args[arg_index],
196-
false);
197-
JERRY_ASSERT (ecma_is_completion_value_normal (put_comp));
198-
ecma_free_completion_value (put_comp);
199-
200-
ecma_deref_ecma_string (new_array_index_string_p);
201-
new_array_index++;
202-
}
115+
ECMA_TRY_CATCH (concat_value,
116+
ecma_builtin_helper_array_concat_value (new_array_p, &new_length, args[arg_index]),
117+
ret_value);
118+
ECMA_FINALIZE (concat_value);
203119
}
204120

121+
ECMA_FINALIZE (concat_this_value);
122+
205123
if (ecma_is_completion_value_empty (ret_value))
206124
{
125+
ECMA_TRY_CATCH (set_length_value,
126+
ecma_builtin_array_prototype_helper_set_length (new_array_p, new_length),
127+
ret_value);
207128
ret_value = new_array;
129+
ECMA_FINALIZE (set_length_value);
208130
}
209131
else
210132
{
211133
ecma_free_completion_value (new_array);
212134
}
213135

214-
ECMA_OP_TO_NUMBER_FINALIZE (len_number);
215-
ECMA_FINALIZE (len_value);
216-
ecma_deref_ecma_string (magic_string_length_p);
217136
ECMA_FINALIZE (obj_this);
218137

219138
return ret_value;

jerry-core/ecma/builtin-objects/ecma-builtin-helpers.cpp

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,132 @@ ecma_builtin_helper_array_index_normalize (ecma_number_t index, /**< index */
325325
return norm_index;
326326
} /* ecma_builtin_helper_array_index_normalize */
327327

328+
/**
329+
* Helper function for concatenating an ecma_value_t to an Array.
330+
*
331+
* See also:
332+
* ECMA-262 v5, 15.4.4.4 steps 5.b - 5.c
333+
*
334+
* Used by:
335+
* - The Array.prototype.concat routine.
336+
*
337+
* @return completion value
338+
* Returned value must be freed with ecma_free_completion_value.
339+
*/
340+
ecma_completion_value_t
341+
ecma_builtin_helper_array_concat_value (ecma_object_t *obj_p, /**< array */
342+
uint32_t *length, /**< in-out: array's length */
343+
ecma_value_t value) /**< value to concat */
344+
{
345+
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
346+
347+
/* 5.b */
348+
if (ecma_is_value_object (value)
349+
&& (ecma_object_get_class_name (ecma_get_object_from_value (value)) == LIT_MAGIC_STRING_ARRAY_UL))
350+
{
351+
ecma_string_t *magic_string_length_p = ecma_get_magic_string (LIT_MAGIC_STRING_LENGTH);
352+
/* 5.b.ii */
353+
ECMA_TRY_CATCH (arg_len_value,
354+
ecma_op_object_get (ecma_get_object_from_value (value),
355+
magic_string_length_p),
356+
ret_value);
357+
ECMA_OP_TO_NUMBER_TRY_CATCH (arg_len_number, arg_len_value, ret_value);
358+
359+
uint32_t arg_len = ecma_number_to_uint32 (arg_len_number);
360+
361+
/* 5.b.iii */
362+
for (uint32_t array_index = 0;
363+
array_index < arg_len && ecma_is_completion_value_empty (ret_value);
364+
array_index++)
365+
{
366+
ecma_string_t *array_index_string_p = ecma_new_ecma_string_from_uint32 (array_index);
367+
368+
/* 5.b.iii.2 */
369+
if (ecma_op_object_get_property (ecma_get_object_from_value (value),
370+
array_index_string_p) != NULL)
371+
{
372+
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 (*length + array_index);
373+
374+
/* 5.b.iii.3.a */
375+
ECMA_TRY_CATCH (get_value,
376+
ecma_op_object_get (ecma_get_object_from_value (value),
377+
array_index_string_p),
378+
ret_value);
379+
380+
ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor ();
381+
{
382+
prop_desc.is_value_defined = true;
383+
prop_desc.value = get_value;
384+
385+
prop_desc.is_writable_defined = true;
386+
prop_desc.is_writable = true;
387+
388+
prop_desc.is_enumerable_defined = true;
389+
prop_desc.is_enumerable = true;
390+
391+
prop_desc.is_configurable_defined = true;
392+
prop_desc.is_configurable = true;
393+
}
394+
395+
/* 5.b.iii.3.b */
396+
ecma_completion_value_t put_comp = ecma_op_object_define_own_property (obj_p,
397+
new_array_index_string_p,
398+
&prop_desc,
399+
false);
400+
JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp));
401+
ecma_free_completion_value (put_comp);
402+
403+
ECMA_FINALIZE (get_value);
404+
ecma_deref_ecma_string (new_array_index_string_p);
405+
}
406+
407+
ecma_deref_ecma_string (array_index_string_p);
408+
}
409+
410+
*length += arg_len;
411+
412+
ECMA_OP_TO_NUMBER_FINALIZE (arg_len_number);
413+
ECMA_FINALIZE (arg_len_value);
414+
ecma_deref_ecma_string (magic_string_length_p);
415+
}
416+
else
417+
{
418+
ecma_string_t *new_array_index_string_p = ecma_new_ecma_string_from_uint32 ((*length)++);
419+
420+
ecma_property_descriptor_t prop_desc = ecma_make_empty_property_descriptor ();
421+
{
422+
prop_desc.is_value_defined = true;
423+
prop_desc.value = value;
424+
425+
prop_desc.is_writable_defined = true;
426+
prop_desc.is_writable = true;
427+
428+
prop_desc.is_enumerable_defined = true;
429+
prop_desc.is_enumerable = true;
430+
431+
prop_desc.is_configurable_defined = true;
432+
prop_desc.is_configurable = true;
433+
}
434+
435+
/* 5.c.i */
436+
ecma_completion_value_t put_comp = ecma_op_object_define_own_property (obj_p,
437+
new_array_index_string_p,
438+
&prop_desc,
439+
false);
440+
JERRY_ASSERT (ecma_is_completion_value_normal_true (put_comp));
441+
ecma_free_completion_value (put_comp);
442+
443+
ecma_deref_ecma_string (new_array_index_string_p);
444+
}
445+
446+
if (ecma_is_completion_value_empty (ret_value))
447+
{
448+
ret_value = ecma_make_simple_completion_value (ECMA_SIMPLE_VALUE_TRUE);
449+
}
450+
451+
return ret_value;
452+
} /* ecma_builtin_helper_array_concat_value */
453+
328454
/**
329455
* Helper function to normalizing a string index
330456
*

jerry-core/ecma/builtin-objects/ecma-builtin-helpers.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ extern ecma_completion_value_t ecma_builtin_helper_object_to_string (const ecma_
3030
extern ecma_completion_value_t ecma_builtin_helper_get_to_locale_string_at_index (ecma_object_t *obj_p, uint32_t index);
3131
extern ecma_completion_value_t ecma_builtin_helper_object_get_properties (ecma_object_t *obj,
3232
bool only_enumerable_properties);
33+
extern ecma_completion_value_t ecma_builtin_helper_array_concat_value (ecma_object_t *obj,
34+
uint32_t *length,
35+
ecma_value_t);
3336
extern uint32_t ecma_builtin_helper_array_index_normalize (ecma_number_t index, uint32_t length);
3437
extern uint32_t ecma_builtin_helper_string_index_normalize (ecma_number_t index, uint32_t length);
3538

tests/jerry/array-prototype-concat.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ for (i = 0; i < array.length; i++) {
2121
assert(array[i] === new_arr[i]);
2222
}
2323

24-
var obj = {};
24+
var obj = { concat : Array.prototype.concat };
2525
var arr1 = ["Apple", 6, "Peach"];
2626
var arr2 = [obj, "Cherry", "Grape"];
2727

28+
var new_array = obj.concat(arr1);
29+
assert(new_array.length === 4);
30+
assert(new_array[0] === obj);
31+
assert(new_array[1] === "Apple");
32+
assert(new_array[2] === 6);
33+
assert(new_array[3] === "Peach");
34+
2835
var new_array = arr1.concat(arr2, obj, 1);
2936

3037
assert(new_array.length === 8);
@@ -50,24 +57,19 @@ for (i = 0; i < result.length; i++) {
5057
assert(result[i] === expected[i]);
5158
}
5259

53-
// Checking behavior when unable to get length
54-
var obj = { concat : Array.prototype.concat }
55-
Object.defineProperty(obj, 'length', { 'get' : function () {throw new ReferenceError ("foo"); } });
56-
57-
try {
58-
obj.concat();
59-
assert(false);
60-
} catch (e) {
61-
assert(e.message === "foo");
62-
assert(e instanceof ReferenceError);
63-
}
60+
var arr1 = [];
61+
arr1.length = 2;
62+
var arr2 = [];
63+
arr2.length = 3;
64+
assert(arr1.concat(arr2).length === arr1.length + arr2.length);
6465

6566
// Checking behavior when unable to get element
66-
var obj = { concat : Array.prototype.concat, length : 1 }
67-
Object.defineProperty(obj, '0', { 'get' : function () {throw new ReferenceError ("foo"); } });
67+
var arr = []
68+
Object.defineProperty(arr, '0', { 'get' : function () {throw new ReferenceError ("foo"); } });
69+
arr.length = 1;
6870

6971
try {
70-
obj.concat();
72+
arr.concat();
7173
assert(false);
7274
} catch (e) {
7375
assert(e.message === "foo");

0 commit comments

Comments
 (0)