Skip to content

Commit fa81944

Browse files
authored
Foreach API methods should not iterate over internal objects (#3537)
The `jerry_objects_foreach` and `jerry_objects_foreach_by_native_info` methods iterates over all objects. However in ES 2015 there are a few special objects which should only be used internally thus these objects should not be accessed by the API user. JerryScript-DCO-1.0-Signed-off-by: Peter Gal [email protected]
1 parent a78c8d4 commit fa81944

File tree

4 files changed

+154
-4
lines changed

4 files changed

+154
-4
lines changed

jerry-core/api/jerry.c

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2370,7 +2370,16 @@ jerry_set_internal_property (const jerry_value_t obj_val, /**< object value */
23702370
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
23712371
NULL);
23722372

2373-
internal_object_p = ecma_create_object (NULL, 0, ECMA_OBJECT_TYPE_GENERAL);
2373+
internal_object_p = ecma_create_object (NULL,
2374+
sizeof (ecma_extended_object_t),
2375+
ECMA_OBJECT_TYPE_CLASS);
2376+
{
2377+
ecma_extended_object_t *container_p = (ecma_extended_object_t *) internal_object_p;
2378+
container_p->u.class_prop.class_id = LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT;
2379+
container_p->u.class_prop.extra_info = 0;
2380+
container_p->u.class_prop.u.length = 0;
2381+
}
2382+
23742383
value_p->value = ecma_make_object_value (internal_object_p);
23752384
ecma_deref_object (internal_object_p);
23762385
}
@@ -2833,6 +2842,40 @@ jerry_set_prototype (const jerry_value_t obj_val, /**< object value */
28332842
return ECMA_VALUE_TRUE;
28342843
} /* jerry_set_prototype */
28352844

2845+
/**
2846+
* Utility to check if a given object can be used for the foreach api calls.
2847+
*
2848+
* Some objects/classes uses extra internal objects to correctly store data.
2849+
* These extre object should never be exposed externally to the API user.
2850+
*
2851+
* @returns true - if the user can access the object in the callback.
2852+
* false - if the object is an internal object which should no be accessed by the user.
2853+
*/
2854+
static
2855+
bool jerry_object_is_valid_foreach (ecma_object_t *object_p) /**< object to test */
2856+
{
2857+
if (ecma_is_lexical_environment (object_p))
2858+
{
2859+
return false;
2860+
}
2861+
2862+
ecma_object_type_t object_type = ecma_get_object_type (object_p);
2863+
2864+
if (object_type == ECMA_OBJECT_TYPE_CLASS)
2865+
{
2866+
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
2867+
switch (ext_object_p->u.class_prop.class_id)
2868+
{
2869+
/* An object's internal property object should not be iterable by foreach. */
2870+
case LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT:
2871+
/* Containers are internal data, do not iterate on them. */
2872+
case LIT_INTERNAL_MAGIC_STRING_CONTAINER: return false;
2873+
}
2874+
}
2875+
2876+
return true;
2877+
} /* jerry_object_is_valid_foreach */
2878+
28362879
/**
28372880
* Traverse objects.
28382881
*
@@ -2853,7 +2896,7 @@ jerry_objects_foreach (jerry_objects_foreach_t foreach_p, /**< function pointer
28532896
{
28542897
ecma_object_t *iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, iter_cp);
28552898

2856-
if (!ecma_is_lexical_environment (iter_p)
2899+
if (jerry_object_is_valid_foreach (iter_p)
28572900
&& !foreach_p (ecma_make_object_value (iter_p), user_data_p))
28582901
{
28592902
return true;
@@ -2891,7 +2934,7 @@ jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_i
28912934
{
28922935
ecma_object_t *iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, iter_cp);
28932936

2894-
if (!ecma_is_lexical_environment (iter_p))
2937+
if (jerry_object_is_valid_foreach (iter_p))
28952938
{
28962939
native_pointer_p = ecma_get_native_pointer_value (iter_p, (void *) native_info_p);
28972940
if (native_pointer_p

jerry-core/ecma/base/ecma-gc.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1142,7 +1142,8 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
11421142
JERRY_ASSERT (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_UNDEFINED
11431143
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_ARGUMENTS_UL
11441144
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL
1145-
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_ERROR_UL);
1145+
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_ERROR_UL
1146+
|| ext_object_p->u.class_prop.class_id == LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT);
11461147
break;
11471148
}
11481149
}

jerry-core/lit/lit-magic-strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ typedef enum
7373
LIT_INTERNAL_MAGIC_STRING_CLASS_THIS_BINDING, /**< the this binding of the class constructor */
7474
LIT_INTERNAL_MAGIC_STRING_WEAK_REFS, /**< Weak references to the current object */
7575
LIT_INTERNAL_MAGIC_STRING_CONTAINER, /**< Literal ID for internal container objects */
76+
LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT, /**< Internal object ID for internal properties */
7677
LIT_MAGIC_STRING__COUNT /**< number of magic strings */
7778
} lit_magic_string_id_t;
7879

tests/unit-core/test-objects-foreach.c

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,105 @@
1616
#include "jerryscript.h"
1717
#include "test-common.h"
1818

19+
static bool
20+
count_objects (jerry_value_t object, void *user_arg)
21+
{
22+
(void) object;
23+
TEST_ASSERT (user_arg != NULL);
24+
25+
int *counter = (int *) user_arg;
26+
27+
(*counter)++;
28+
return true;
29+
} /* count_objects */
30+
31+
static void
32+
test_container (void)
33+
{
34+
jerry_value_t global = jerry_get_global_object ();
35+
jerry_value_t map_str = jerry_create_string ((const jerry_char_t *) "Map");
36+
jerry_value_t map_result = jerry_get_property (global, map_str);
37+
jerry_type_t type = jerry_value_get_type (map_result);
38+
39+
jerry_release_value (map_result);
40+
jerry_release_value (map_str);
41+
jerry_release_value (global);
42+
43+
/* If there is no Map function this is not an es2015 profile build, skip this test case. */
44+
if (type != JERRY_TYPE_FUNCTION)
45+
{
46+
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Container based test is disabled!\n");
47+
return;
48+
}
49+
50+
const char *eval_str = "new Map ([[1, 2], [3, 4]])";
51+
{
52+
/* Make sure that the Map and it's prototype object/function is initialized. */
53+
jerry_value_t result = jerry_eval ((const jerry_char_t *) eval_str, sizeof (eval_str) - 1, 0);
54+
jerry_release_value (result);
55+
}
56+
57+
/* Get the number of iterable objects. */
58+
int start_count = 0;
59+
jerry_objects_foreach (count_objects, &start_count);
60+
61+
/* Create another map. */
62+
jerry_value_t result = jerry_eval ((const jerry_char_t *) eval_str, sizeof (eval_str) - 1, 0);
63+
64+
/* Get the current number of objects. */
65+
int end_count = 0;
66+
jerry_objects_foreach (count_objects, &end_count);
67+
68+
/* As only one Map was created the number of iterable objects should be incremented only by one. */
69+
TEST_ASSERT (end_count > start_count);
70+
TEST_ASSERT ((end_count - start_count) == 1);
71+
72+
jerry_release_value (result);
73+
} /* test_container */
74+
75+
static void
76+
test_internal_prop (void)
77+
{
78+
/* Make sure that the object is initialized in the engine. */
79+
{
80+
jerry_value_t object = jerry_create_object ();
81+
jerry_release_value (object);
82+
}
83+
84+
/* Get the number of iterable objects. */
85+
int before_object_count = 0;
86+
jerry_objects_foreach (count_objects, &before_object_count);
87+
88+
jerry_value_t object = jerry_create_object ();
89+
90+
/* After creating the object, the number of objects is incremented by one. */
91+
int after_object_count = 0;
92+
{
93+
jerry_objects_foreach (count_objects, &after_object_count);
94+
95+
TEST_ASSERT (after_object_count > before_object_count);
96+
TEST_ASSERT ((after_object_count - before_object_count) == 1);
97+
}
98+
99+
jerry_value_t internal_prop_name = jerry_create_string ((const jerry_char_t *) "hidden_foo");
100+
jerry_value_t internal_prop_object = jerry_create_object ();
101+
bool internal_result = jerry_set_internal_property (object, internal_prop_name, internal_prop_object);
102+
TEST_ASSERT (internal_result == true);
103+
jerry_release_value (internal_prop_name);
104+
jerry_release_value (internal_prop_object);
105+
106+
/* After adding an internal property object, the number of object is incremented by one. */
107+
{
108+
int after_internal_count = 0;
109+
jerry_objects_foreach (count_objects, &after_internal_count);
110+
111+
TEST_ASSERT (after_internal_count > after_object_count);
112+
TEST_ASSERT ((after_internal_count - after_object_count) == 1);
113+
}
114+
115+
jerry_release_value (object);
116+
} /* test_internal_prop */
117+
19118
static int test_data = 1;
20119

21120
static void free_test_data (void *data_p)
@@ -133,5 +232,11 @@ main (void)
133232
jerry_release_value (property_name);
134233
jerry_release_value (undefined);
135234
jerry_release_value (strict_equal);
235+
236+
test_container ();
237+
test_internal_prop ();
238+
136239
jerry_cleanup ();
240+
241+
return 0;
137242
} /* main */

0 commit comments

Comments
 (0)