Skip to content

Commit 6ebb324

Browse files
committed
Refactor the Object.defineProperties function
Fix assert and corner cases in Object.defineProperties function. JerryScript-DCO-1.0-Signed-off-by: Kristof Kosztyo [email protected]
1 parent f6b875c commit 6ebb324

File tree

2 files changed

+276
-39
lines changed

2 files changed

+276
-39
lines changed

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

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -412,67 +412,134 @@ ecma_builtin_object_object_define_properties (ecma_value_t this_arg __attr_unuse
412412
{
413413
ecma_completion_value_t ret_value = ecma_make_empty_completion_value ();
414414

415+
// 1.
415416
if (!ecma_is_value_object (arg1))
416417
{
417418
ret_value = ecma_make_throw_obj_completion_value (ecma_new_standard_error (ECMA_ERROR_TYPE));
418-
return ret_value;
419419
}
420+
else
421+
{
422+
ecma_object_t *obj_p = ecma_get_object_from_value (arg1);
420423

421-
ecma_object_t *obj_p = ecma_get_object_from_value (arg1);
422-
ecma_object_t *props_p = ecma_get_object_from_value (arg2);
424+
// 2.
425+
ECMA_TRY_CATCH (props,
426+
ecma_op_to_object (arg2),
427+
ret_value);
423428

424-
ecma_property_t *property_p;
429+
ecma_object_t *props_p = ecma_get_object_from_completion_value (props);
430+
ecma_property_t *property_p;
425431

426-
for (property_p = ecma_get_property_list (props_p);
427-
property_p != NULL;
428-
property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p))
429-
{
430-
ecma_string_t *property_name_p;
432+
// First we need to know how many properties should be stored
433+
uint32_t property_number = 0;
434+
for (property_p = ecma_get_property_list (props_p);
435+
property_p != NULL;
436+
property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p))
437+
{
438+
if ((property_p->type == ECMA_PROPERTY_NAMEDDATA || property_p->type == ECMA_PROPERTY_NAMEDACCESSOR)
439+
&& ecma_is_property_enumerable (property_p))
440+
{
441+
property_number++;
442+
}
443+
}
431444

432-
if (property_p->type == ECMA_PROPERTY_NAMEDDATA)
445+
// 3.
446+
MEM_DEFINE_LOCAL_ARRAY (property_names_p, property_number, ecma_string_t*);
447+
448+
uint32_t index = 0;
449+
for (property_p = ecma_get_property_list (props_p);
450+
property_p != NULL;
451+
property_p = ECMA_GET_POINTER (ecma_property_t, property_p->next_property_p))
433452
{
434-
property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
435-
property_p->u.named_data_property.name_p);
453+
ecma_string_t *property_name_p;
454+
455+
if (property_p->type == ECMA_PROPERTY_NAMEDDATA)
456+
{
457+
property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
458+
property_p->u.named_data_property.name_p);
459+
}
460+
else if (property_p->type == ECMA_PROPERTY_NAMEDACCESSOR)
461+
{
462+
property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
463+
property_p->u.named_accessor_property.name_p);
464+
}
465+
else
466+
{
467+
continue;
468+
}
469+
470+
if (ecma_is_property_enumerable (property_p))
471+
{
472+
property_names_p[index++] = ecma_copy_or_ref_ecma_string (property_name_p);
473+
}
436474
}
437-
else if (property_p->type == ECMA_PROPERTY_NAMEDACCESSOR)
475+
476+
// 4.
477+
MEM_DEFINE_LOCAL_ARRAY (property_descriptors, property_number, ecma_property_descriptor_t);
478+
uint32_t property_descriptor_number = 0;
479+
480+
for (index = 0;
481+
index < property_number && ecma_is_completion_value_empty (ret_value);
482+
index++)
438483
{
439-
property_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t,
440-
property_p->u.named_accessor_property.name_p);
484+
// 5.a
485+
ECMA_TRY_CATCH (desc_obj,
486+
ecma_op_object_get (props_p, property_names_p[index]),
487+
ret_value);
488+
489+
// 5.b
490+
ECMA_TRY_CATCH (conv_result,
491+
ecma_op_to_property_descriptor (ecma_get_completion_value_value (desc_obj),
492+
&property_descriptors[index]),
493+
ret_value);
494+
495+
property_descriptor_number++;
496+
497+
ECMA_FINALIZE (conv_result);
498+
ECMA_FINALIZE (desc_obj);
441499
}
442-
else
500+
501+
// 6.
502+
for (index = 0;
503+
index < property_number && ecma_is_completion_value_empty (ret_value);
504+
index++)
443505
{
444-
continue;
506+
ECMA_TRY_CATCH (define_own_prop_ret,
507+
ecma_op_object_define_own_property (obj_p,
508+
property_names_p[index],
509+
&property_descriptors[index],
510+
true),
511+
ret_value);
512+
513+
ECMA_FINALIZE (define_own_prop_ret);
445514
}
446-
ecma_property_descriptor_t prop_desc;
447515

448-
ECMA_TRY_CATCH (descObj,
449-
ecma_op_general_object_get (props_p, property_name_p),
450-
ret_value);
516+
// Clean up
517+
for (index = 0;
518+
index < property_descriptor_number;
519+
index++)
520+
{
521+
ecma_free_property_descriptor (&property_descriptors[index]);
522+
}
451523

452-
ECMA_TRY_CATCH (conv_result,
453-
ecma_op_to_property_descriptor (ecma_get_completion_value_value (descObj),
454-
&prop_desc),
455-
ret_value);
524+
MEM_FINALIZE_LOCAL_ARRAY (property_descriptors);
456525

457-
ECMA_TRY_CATCH (define_own_prop_ret,
458-
ecma_op_object_define_own_property (obj_p,
459-
property_name_p,
460-
&prop_desc,
461-
true),
462-
ret_value);
526+
for (index = 0;
527+
index < property_number;
528+
index++)
529+
{
530+
ecma_deref_ecma_string (property_names_p[index]);
531+
}
463532

464-
ECMA_FINALIZE (define_own_prop_ret);
465-
ecma_free_property_descriptor (&prop_desc);
466-
ECMA_FINALIZE (conv_result);
467-
ECMA_FINALIZE (descObj);
533+
MEM_FINALIZE_LOCAL_ARRAY (property_names_p);
468534

469-
if (ecma_is_completion_value_throw (ret_value))
535+
// 7.
536+
if (ecma_is_completion_value_empty (ret_value))
470537
{
471-
return ret_value;
538+
ret_value = ecma_make_normal_completion_value (ecma_copy_value (arg1, true));
472539
}
473-
}
474540

475-
ret_value = ecma_make_normal_completion_value (ecma_copy_value (arg1, true));
541+
ECMA_FINALIZE (props);
542+
}
476543

477544
return ret_value;
478545
} /* ecma_builtin_object_object_define_properties */

tests/jerry/object_define_properties.js

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,179 @@ Object.defineProperties(obj, {
2626
"Hello": {
2727
value: "world",
2828
writable: false
29+
},
30+
"inner_object": {
31+
value : {
32+
"a" : 1,
33+
"b" : {
34+
value: "foo"
35+
}
36+
}
2937
}
3038
});
3139

3240
assert (obj.foo === true);
3341
assert (obj.bar === "baz");
3442
assert (obj.Hello === "world");
43+
assert (obj.inner_object.a === 1);
44+
assert (obj.inner_object.b.value === "foo");
45+
46+
// These cases should throw TypeError
47+
try {
48+
Object.defineProperties(obj, undefined);
49+
assert (false);
50+
} catch (e) {
51+
assert (e instanceof TypeError);
52+
}
53+
54+
try {
55+
Object.defineProperties(obj, null);
56+
assert (false);
57+
} catch (e) {
58+
assert (e instanceof TypeError);
59+
}
60+
61+
try {
62+
Object.defineProperties(undefined, {
63+
"foo": {
64+
value: true,
65+
writable: true
66+
}
67+
});
68+
assert (false);
69+
} catch (e) {
70+
assert (e instanceof TypeError);
71+
}
72+
73+
// Check for internal assert, see issue #131.
74+
try {
75+
Object.defineProperties([], undefined);
76+
assert (false);
77+
} catch (e) {
78+
assert (e instanceof TypeError);
79+
}
80+
81+
// If one of the properties is wrong than it shouldn't update the object.
82+
var obj2 = {
83+
a: 5
84+
};
85+
try {
86+
Object.defineProperties(obj2, {
87+
"foo": {
88+
value: true,
89+
writable: true
90+
},
91+
"bar": {
92+
value: 3,
93+
set: 3
94+
},
95+
"Hello": {
96+
value: "world",
97+
writable: false
98+
}
99+
});
100+
assert (false);
101+
} catch (e) {
102+
assert (e instanceof TypeError);
103+
assert (obj2.foo === undefined);
104+
assert (obj2.set === undefined);
105+
assert (obj2.Hello === undefined);
106+
assert (obj2.a === 5);
107+
}
108+
109+
// Define accessors
110+
var obj = {};
111+
Object.defineProperties(obj, {
112+
"foo": {
113+
value: 42,
114+
writable: true,
115+
},
116+
"bar": {
117+
get: function() { return this.foo },
118+
set: function(v) { this.foo = v }
119+
}
120+
});
121+
122+
assert (obj.bar === 42);
123+
obj.bar = "baz";
124+
assert (obj.foo === "baz");
125+
126+
// Define get method which throws error
127+
var obj = {};
128+
var props = {
129+
prop1: {
130+
value: 1,
131+
writable: true,
132+
},
133+
get bar() {
134+
throw TypeError;
135+
return { value : 2, writable : true };
136+
},
137+
prop2: {
138+
value: 3,
139+
writable: true,
140+
},
141+
prop3: {
142+
value: 4,
143+
writable: true,
144+
}
145+
};
146+
147+
try {
148+
Object.defineProperties(obj, props);
149+
assert (false);
150+
} catch (e) {
151+
assert (e instanceof TypeError);
152+
}
153+
154+
// Define get method which deletes a property
155+
var obj = {};
156+
Object.defineProperties(obj, {
157+
"foo": {
158+
value: 42,
159+
writable: true,
160+
},
161+
"a": {
162+
value: "b",
163+
configurable: true
164+
},
165+
"bar": {
166+
get: function() {
167+
delete this.a;
168+
return this.foo;
169+
},
170+
}
171+
});
172+
173+
assert (obj.a === "b");
174+
assert (obj.bar === 42);
175+
assert (obj.a === undefined);
176+
177+
// This code should throw TypeError
178+
var obj = {};
179+
var props = {
180+
prop1: {
181+
value: 1,
182+
writable: true,
183+
},
184+
get bar() {
185+
delete props.prop1;
186+
delete props.prop2;
187+
return { value : 2, writable : true };
188+
},
189+
prop2: {
190+
value: 3,
191+
writable: true,
192+
},
193+
prop3: {
194+
value: 4,
195+
writable: true,
196+
}
197+
};
198+
199+
try {
200+
Object.defineProperties(obj, props);
201+
assert (false);
202+
} catch (e) {
203+
assert (e instanceof TypeError);
204+
}

0 commit comments

Comments
 (0)