Skip to content

Commit 9d586fe

Browse files
authored
Handle errors in JSON.parse triggered by Proxies (#4134)
Fixes: #4130 JerryScript-DCO-1.0-Signed-off-by: Peter Gal [email protected]
1 parent 8964a2b commit 9d586fe

File tree

3 files changed

+168
-45
lines changed

3 files changed

+168
-45
lines changed

jerry-core/ecma/builtin-objects/ecma-builtin-json.c

Lines changed: 80 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,11 @@ ecma_builtin_json_parse_value (ecma_json_token_t *token_p) /**< token argument *
601601
}
602602
} /* ecma_builtin_json_parse_value */
603603

604+
static ecma_value_t
605+
ecma_builtin_json_internalize_process_property (ecma_object_t *reviver_p,
606+
ecma_object_t *object_p,
607+
ecma_string_t *prop_name);
608+
604609
/**
605610
* Abstract operation InternalizeJSONProperty
606611
*
@@ -666,75 +671,50 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f
666671
for (ecma_length_t i = 0; i < length; i++)
667672
{
668673
ecma_string_t *prop_index = ecma_new_ecma_string_from_length (i);
674+
ecma_value_t result = ecma_builtin_json_internalize_process_property (reviver_p, object_p, prop_index);
669675

670-
ecma_value_t new_element = ecma_builtin_json_internalize_property (reviver_p, object_p, prop_index);
676+
ecma_deref_ecma_string (prop_index);
671677

672-
if (ECMA_IS_VALUE_ERROR (new_element))
678+
if (ECMA_IS_VALUE_ERROR (result))
673679
{
674-
ecma_deref_ecma_string (prop_index);
675680
ecma_deref_object (object_p);
676-
return new_element;
677-
}
678-
679-
if (ecma_is_value_undefined (new_element))
680-
{
681-
ecma_value_t delete_val = ecma_op_object_delete (object_p, prop_index, false);
682-
JERRY_ASSERT (ecma_is_value_boolean (delete_val));
683-
}
684-
else
685-
{
686-
ecma_builtin_json_define_value_property (object_p,
687-
prop_index,
688-
new_element);
689-
ecma_free_value (new_element);
681+
return result;
690682
}
691683

692-
ecma_deref_ecma_string (prop_index);
684+
JERRY_ASSERT (result == ECMA_VALUE_TRUE);
693685
}
694686
}
695687
/* 3.d */
696688
else
697689
{
698690
ecma_collection_t *props_p = ecma_op_object_get_enumerable_property_names (object_p,
699691
ECMA_ENUMERABLE_PROPERTY_KEYS);
700-
692+
#if ENABLED (JERRY_ESNEXT)
693+
if (JERRY_UNLIKELY (props_p == NULL))
694+
{
695+
ecma_deref_object (object_p);
696+
return ECMA_VALUE_ERROR;
697+
}
698+
#else /* !ENABLED (JERRY_ESNEXT) */
701699
JERRY_ASSERT (props_p != NULL);
700+
#endif /* ENABLED (JERRY_ESNEXT) */
702701

703702
ecma_value_t *buffer_p = props_p->buffer_p;
704703

705704
/* 3.d.iii */
706705
for (uint32_t i = 0; i < props_p->item_count; i++)
707706
{
708707
ecma_string_t *property_name_p = ecma_get_string_from_value (buffer_p[i]);
708+
ecma_value_t result = ecma_builtin_json_internalize_process_property (reviver_p, object_p, property_name_p);
709709

710-
/* 3.d.iii.1 */
711-
ecma_value_t result = ecma_builtin_json_internalize_property (reviver_p, object_p, property_name_p);
712-
713-
/* 3.d.iii.2 */
714710
if (ECMA_IS_VALUE_ERROR (result))
715711
{
716712
ecma_collection_free (props_p);
717713
ecma_deref_object (object_p);
718-
719714
return result;
720715
}
721716

722-
/* 3.d.iii.3 */
723-
if (ecma_is_value_undefined (result))
724-
{
725-
ecma_value_t delete_val = ecma_op_general_object_delete (object_p,
726-
property_name_p,
727-
false);
728-
JERRY_ASSERT (ecma_is_value_boolean (delete_val));
729-
}
730-
/* 3.d.iii.4 */
731-
else
732-
{
733-
ecma_builtin_json_define_value_property (object_p,
734-
property_name_p,
735-
result);
736-
ecma_free_value (result);
737-
}
717+
JERRY_ASSERT (result == ECMA_VALUE_TRUE);
738718
}
739719

740720
ecma_collection_free (props_p);
@@ -754,6 +734,66 @@ ecma_builtin_json_internalize_property (ecma_object_t *reviver_p, /**< reviver f
754734
return ret_value;
755735
} /* ecma_builtin_json_internalize_property */
756736

737+
/**
738+
* Part of the InternalizeJSONProperty abstract method.
739+
*
740+
* See also:
741+
* ECMA-262 v5, 15.12.2
742+
* ECMA-262 v11, 24.5.1.1 in step 2
743+
*
744+
* @return ECMA_VALUE_TRUE - if no error occured.
745+
* error if one of the operation failed.
746+
*/
747+
static
748+
ecma_value_t ecma_builtin_json_internalize_process_property (ecma_object_t *reviver_p, /**< reviver function */
749+
ecma_object_t *object_p, /**< holder object */
750+
ecma_string_t *prop_name) /**< property name */
751+
{
752+
/* ES11: 2.b.iii.1 / 2.c.ii.1 */
753+
ecma_value_t new_element = ecma_builtin_json_internalize_property (reviver_p, object_p, prop_name);
754+
755+
if (ECMA_IS_VALUE_ERROR (new_element))
756+
{
757+
return new_element;
758+
}
759+
760+
/* ES11: 2.b.iii.2 / 2.c.ii.2 */
761+
if (ecma_is_value_undefined (new_element))
762+
{
763+
/* ES11: 2.b.iii.2.a / 2.c.ii.2.a */
764+
ecma_value_t delete_val = ecma_op_object_delete (object_p, prop_name, false);
765+
766+
#if ENABLED (JERRY_ESNEXT)
767+
if (ECMA_IS_VALUE_ERROR (delete_val))
768+
{
769+
return delete_val;
770+
}
771+
#endif /* ENABLED (JERRY_ESNEXT) */
772+
773+
JERRY_ASSERT (ecma_is_value_boolean (delete_val));
774+
}
775+
else
776+
{
777+
/* ES11: 2.b.iii.3.a / 2.c.ii.3.a */
778+
ecma_value_t def_value = ecma_builtin_helper_def_prop (object_p,
779+
prop_name,
780+
new_element,
781+
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE);
782+
ecma_free_value (new_element);
783+
784+
#if ENABLED (JERRY_ESNEXT)
785+
if (ECMA_IS_VALUE_ERROR (def_value))
786+
{
787+
return def_value;
788+
}
789+
#endif /* ENABLED (JERRY_ESNEXT) */
790+
791+
JERRY_ASSERT (ecma_is_value_boolean (def_value));
792+
}
793+
794+
return ECMA_VALUE_TRUE;
795+
} /* ecma_builtin_json_internalize_process_property */
796+
757797
/**
758798
* Function to set a string token from the given arguments, fills its fields and advances the string pointer.
759799
*
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Copyright JS Foundation and other contributors, http://js.foundation
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/* Test JSON parse [[delete]] with Array */
16+
var badDeleteArray = new Proxy([0], {
17+
deleteProperty: function() {
18+
throw "My Super Error A";
19+
}
20+
});
21+
22+
try {
23+
JSON.parse('[0,0]', function() { this[1] = badDeleteArray; });
24+
} catch (ex) {
25+
assert(ex === "My Super Error A");
26+
}
27+
28+
/* Test JSON parse [[delete]] with Objects */
29+
var badDeleteObj = new Proxy([0], {
30+
deleteProperty: function() {
31+
throw "My Super Error B";
32+
}
33+
});
34+
35+
try {
36+
JSON.parse('[0,0]', function() { this[1] = badDeleteObj; });
37+
} catch (ex) {
38+
assert(ex === "My Super Error B");
39+
}
40+
41+
/* Test JSON parse property define with Array */
42+
var badDefineArray = new Proxy([null], {
43+
defineProperty: function(_, name) {
44+
throw "My Super Error C";
45+
}
46+
});
47+
48+
try {
49+
JSON.parse('["first", null]', function(_, value) {
50+
if (value === 'first') {
51+
this[1] = badDefineArray;
52+
}
53+
return value;
54+
});
55+
} catch (ex) {
56+
assert(ex === "My Super Error C");
57+
}
58+
59+
/* Test JSON parse property define with Object */
60+
var badDefineObj = new Proxy({0: null}, {
61+
defineProperty: function(_, name) {
62+
throw "My Super Error D";
63+
}
64+
});
65+
66+
try {
67+
JSON.parse('["first", null]', function(_, value) {
68+
if (value === 'first') {
69+
this[1] = badDefineObj;
70+
}
71+
return value;
72+
});
73+
} catch (ex) {
74+
assert(ex === "My Super Error D");
75+
}
76+
77+
/* Test JSON parse keys call */
78+
var badKeys = new Proxy({}, {
79+
ownKeys: function() {
80+
throw "My Super Error E";
81+
}
82+
});
83+
84+
try {
85+
JSON.parse('[0,0]', function() { this[1] = badKeys; });
86+
} catch (ex) {
87+
assert(ex === "My Super Error E");
88+
}

tests/test262-esnext-excludelist.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -979,11 +979,6 @@
979979
<test id="built-ins/GeneratorFunction/proto-from-ctor-realm-prototype.js"><reason></reason></test>
980980
<test id="built-ins/GeneratorFunction/proto-from-ctor-realm.js"><reason></reason></test>
981981
<test id="built-ins/GeneratorPrototype/return/from-state-completed.js"><reason></reason></test>
982-
<test id="built-ins/JSON/parse/reviver-array-define-prop-err.js"><reason></reason></test>
983-
<test id="built-ins/JSON/parse/reviver-array-delete-err.js"><reason></reason></test>
984-
<test id="built-ins/JSON/parse/reviver-object-define-prop-err.js"><reason></reason></test>
985-
<test id="built-ins/JSON/parse/reviver-object-delete-err.js"><reason></reason></test>
986-
<test id="built-ins/JSON/parse/reviver-object-own-keys-err.js"><reason></reason></test>
987982
<test id="built-ins/JSON/stringify/replacer-array-proxy-revoked-realm.js"><reason></reason></test>
988983
<test id="built-ins/JSON/stringify/value-bigint-cross-realm.js"><reason></reason></test>
989984
<test id="built-ins/JSON/stringify/value-bigint-order.js"><reason></reason></test>

0 commit comments

Comments
 (0)