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 pgal.u-szeged@partner.samsung.com
This commit is contained in:
+46
-3
@@ -2370,7 +2370,16 @@ jerry_set_internal_property (const jerry_value_t obj_val, /**< object value */
|
||||
ECMA_PROPERTY_CONFIGURABLE_ENUMERABLE_WRITABLE,
|
||||
NULL);
|
||||
|
||||
internal_object_p = ecma_create_object (NULL, 0, ECMA_OBJECT_TYPE_GENERAL);
|
||||
internal_object_p = ecma_create_object (NULL,
|
||||
sizeof (ecma_extended_object_t),
|
||||
ECMA_OBJECT_TYPE_CLASS);
|
||||
{
|
||||
ecma_extended_object_t *container_p = (ecma_extended_object_t *) internal_object_p;
|
||||
container_p->u.class_prop.class_id = LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT;
|
||||
container_p->u.class_prop.extra_info = 0;
|
||||
container_p->u.class_prop.u.length = 0;
|
||||
}
|
||||
|
||||
value_p->value = ecma_make_object_value (internal_object_p);
|
||||
ecma_deref_object (internal_object_p);
|
||||
}
|
||||
@@ -2833,6 +2842,40 @@ jerry_set_prototype (const jerry_value_t obj_val, /**< object value */
|
||||
return ECMA_VALUE_TRUE;
|
||||
} /* jerry_set_prototype */
|
||||
|
||||
/**
|
||||
* Utility to check if a given object can be used for the foreach api calls.
|
||||
*
|
||||
* Some objects/classes uses extra internal objects to correctly store data.
|
||||
* These extre object should never be exposed externally to the API user.
|
||||
*
|
||||
* @returns true - if the user can access the object in the callback.
|
||||
* false - if the object is an internal object which should no be accessed by the user.
|
||||
*/
|
||||
static
|
||||
bool jerry_object_is_valid_foreach (ecma_object_t *object_p) /**< object to test */
|
||||
{
|
||||
if (ecma_is_lexical_environment (object_p))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ecma_object_type_t object_type = ecma_get_object_type (object_p);
|
||||
|
||||
if (object_type == ECMA_OBJECT_TYPE_CLASS)
|
||||
{
|
||||
ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p;
|
||||
switch (ext_object_p->u.class_prop.class_id)
|
||||
{
|
||||
/* An object's internal property object should not be iterable by foreach. */
|
||||
case LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT:
|
||||
/* Containers are internal data, do not iterate on them. */
|
||||
case LIT_INTERNAL_MAGIC_STRING_CONTAINER: return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} /* jerry_object_is_valid_foreach */
|
||||
|
||||
/**
|
||||
* Traverse objects.
|
||||
*
|
||||
@@ -2853,7 +2896,7 @@ jerry_objects_foreach (jerry_objects_foreach_t foreach_p, /**< function pointer
|
||||
{
|
||||
ecma_object_t *iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, iter_cp);
|
||||
|
||||
if (!ecma_is_lexical_environment (iter_p)
|
||||
if (jerry_object_is_valid_foreach (iter_p)
|
||||
&& !foreach_p (ecma_make_object_value (iter_p), user_data_p))
|
||||
{
|
||||
return true;
|
||||
@@ -2891,7 +2934,7 @@ jerry_objects_foreach_by_native_info (const jerry_object_native_info_t *native_i
|
||||
{
|
||||
ecma_object_t *iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, iter_cp);
|
||||
|
||||
if (!ecma_is_lexical_environment (iter_p))
|
||||
if (jerry_object_is_valid_foreach (iter_p))
|
||||
{
|
||||
native_pointer_p = ecma_get_native_pointer_value (iter_p, (void *) native_info_p);
|
||||
if (native_pointer_p
|
||||
|
||||
@@ -1142,7 +1142,8 @@ ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */
|
||||
JERRY_ASSERT (ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_UNDEFINED
|
||||
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_ARGUMENTS_UL
|
||||
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_BOOLEAN_UL
|
||||
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_ERROR_UL);
|
||||
|| ext_object_p->u.class_prop.class_id == LIT_MAGIC_STRING_ERROR_UL
|
||||
|| ext_object_p->u.class_prop.class_id == LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ typedef enum
|
||||
LIT_INTERNAL_MAGIC_STRING_CLASS_THIS_BINDING, /**< the this binding of the class constructor */
|
||||
LIT_INTERNAL_MAGIC_STRING_WEAK_REFS, /**< Weak references to the current object */
|
||||
LIT_INTERNAL_MAGIC_STRING_CONTAINER, /**< Literal ID for internal container objects */
|
||||
LIT_INTERNAL_MAGIC_STRING_INTERNAL_OBJECT, /**< Internal object ID for internal properties */
|
||||
LIT_MAGIC_STRING__COUNT /**< number of magic strings */
|
||||
} lit_magic_string_id_t;
|
||||
|
||||
|
||||
@@ -16,6 +16,105 @@
|
||||
#include "jerryscript.h"
|
||||
#include "test-common.h"
|
||||
|
||||
static bool
|
||||
count_objects (jerry_value_t object, void *user_arg)
|
||||
{
|
||||
(void) object;
|
||||
TEST_ASSERT (user_arg != NULL);
|
||||
|
||||
int *counter = (int *) user_arg;
|
||||
|
||||
(*counter)++;
|
||||
return true;
|
||||
} /* count_objects */
|
||||
|
||||
static void
|
||||
test_container (void)
|
||||
{
|
||||
jerry_value_t global = jerry_get_global_object ();
|
||||
jerry_value_t map_str = jerry_create_string ((const jerry_char_t *) "Map");
|
||||
jerry_value_t map_result = jerry_get_property (global, map_str);
|
||||
jerry_type_t type = jerry_value_get_type (map_result);
|
||||
|
||||
jerry_release_value (map_result);
|
||||
jerry_release_value (map_str);
|
||||
jerry_release_value (global);
|
||||
|
||||
/* If there is no Map function this is not an es2015 profile build, skip this test case. */
|
||||
if (type != JERRY_TYPE_FUNCTION)
|
||||
{
|
||||
jerry_port_log (JERRY_LOG_LEVEL_ERROR, "Container based test is disabled!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *eval_str = "new Map ([[1, 2], [3, 4]])";
|
||||
{
|
||||
/* Make sure that the Map and it's prototype object/function is initialized. */
|
||||
jerry_value_t result = jerry_eval ((const jerry_char_t *) eval_str, sizeof (eval_str) - 1, 0);
|
||||
jerry_release_value (result);
|
||||
}
|
||||
|
||||
/* Get the number of iterable objects. */
|
||||
int start_count = 0;
|
||||
jerry_objects_foreach (count_objects, &start_count);
|
||||
|
||||
/* Create another map. */
|
||||
jerry_value_t result = jerry_eval ((const jerry_char_t *) eval_str, sizeof (eval_str) - 1, 0);
|
||||
|
||||
/* Get the current number of objects. */
|
||||
int end_count = 0;
|
||||
jerry_objects_foreach (count_objects, &end_count);
|
||||
|
||||
/* As only one Map was created the number of iterable objects should be incremented only by one. */
|
||||
TEST_ASSERT (end_count > start_count);
|
||||
TEST_ASSERT ((end_count - start_count) == 1);
|
||||
|
||||
jerry_release_value (result);
|
||||
} /* test_container */
|
||||
|
||||
static void
|
||||
test_internal_prop (void)
|
||||
{
|
||||
/* Make sure that the object is initialized in the engine. */
|
||||
{
|
||||
jerry_value_t object = jerry_create_object ();
|
||||
jerry_release_value (object);
|
||||
}
|
||||
|
||||
/* Get the number of iterable objects. */
|
||||
int before_object_count = 0;
|
||||
jerry_objects_foreach (count_objects, &before_object_count);
|
||||
|
||||
jerry_value_t object = jerry_create_object ();
|
||||
|
||||
/* After creating the object, the number of objects is incremented by one. */
|
||||
int after_object_count = 0;
|
||||
{
|
||||
jerry_objects_foreach (count_objects, &after_object_count);
|
||||
|
||||
TEST_ASSERT (after_object_count > before_object_count);
|
||||
TEST_ASSERT ((after_object_count - before_object_count) == 1);
|
||||
}
|
||||
|
||||
jerry_value_t internal_prop_name = jerry_create_string ((const jerry_char_t *) "hidden_foo");
|
||||
jerry_value_t internal_prop_object = jerry_create_object ();
|
||||
bool internal_result = jerry_set_internal_property (object, internal_prop_name, internal_prop_object);
|
||||
TEST_ASSERT (internal_result == true);
|
||||
jerry_release_value (internal_prop_name);
|
||||
jerry_release_value (internal_prop_object);
|
||||
|
||||
/* After adding an internal property object, the number of object is incremented by one. */
|
||||
{
|
||||
int after_internal_count = 0;
|
||||
jerry_objects_foreach (count_objects, &after_internal_count);
|
||||
|
||||
TEST_ASSERT (after_internal_count > after_object_count);
|
||||
TEST_ASSERT ((after_internal_count - after_object_count) == 1);
|
||||
}
|
||||
|
||||
jerry_release_value (object);
|
||||
} /* test_internal_prop */
|
||||
|
||||
static int test_data = 1;
|
||||
|
||||
static void free_test_data (void *data_p)
|
||||
@@ -133,5 +232,11 @@ main (void)
|
||||
jerry_release_value (property_name);
|
||||
jerry_release_value (undefined);
|
||||
jerry_release_value (strict_equal);
|
||||
|
||||
test_container ();
|
||||
test_internal_prop ();
|
||||
|
||||
jerry_cleanup ();
|
||||
|
||||
return 0;
|
||||
} /* main */
|
||||
|
||||
Reference in New Issue
Block a user