diff --git a/jerry-core/api/jerry.c b/jerry-core/api/jerry.c index 8840e7502..813aa6c13 100644 --- a/jerry-core/api/jerry.c +++ b/jerry-core/api/jerry.c @@ -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 diff --git a/jerry-core/ecma/base/ecma-gc.c b/jerry-core/ecma/base/ecma-gc.c index e578494bc..c0997bc9b 100644 --- a/jerry-core/ecma/base/ecma-gc.c +++ b/jerry-core/ecma/base/ecma-gc.c @@ -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; } } diff --git a/jerry-core/lit/lit-magic-strings.h b/jerry-core/lit/lit-magic-strings.h index 304b3cdeb..01f02a065 100644 --- a/jerry-core/lit/lit-magic-strings.h +++ b/jerry-core/lit/lit-magic-strings.h @@ -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; diff --git a/tests/unit-core/test-objects-foreach.c b/tests/unit-core/test-objects-foreach.c index c291a2946..3e7f9a84f 100644 --- a/tests/unit-core/test-objects-foreach.c +++ b/tests/unit-core/test-objects-foreach.c @@ -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 */