/* Copyright JS Foundation and other contributors, http://js.foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Garbage collector implementation */ #include "ecma-gc.h" #include "ecma-alloc.h" #include "ecma-array-object.h" #include "ecma-arraybuffer-object.h" #include "ecma-builtin-handlers.h" #include "ecma-container-object.h" #include "ecma-function-object.h" #include "ecma-globals.h" #include "ecma-helpers.h" #include "ecma-lcache.h" #include "ecma-objects.h" #include "ecma-property-hashmap.h" #include "ecma-proxy-object.h" #include "jcontext.h" #include "jrt-bit-fields.h" #include "jrt-libc-includes.h" #include "jrt.h" #include "re-compiler.h" #include "vm-defines.h" #include "vm-stack.h" #if JERRY_BUILTIN_TYPEDARRAY #include "ecma-typedarray-object.h" #endif /* JERRY_BUILTIN_TYPEDARRAY */ #include "ecma-promise-object.h" /* TODO: Extract GC to a separate component */ /** \addtogroup ecma ECMA * @{ * * \addtogroup ecmagc Garbage collector * @{ */ /* * The garbage collector uses the reference counter * of object: it increases the counter by one when * the object is marked at the first time. */ /** * Get visited flag of the object. * * @return true - if visited * false - otherwise */ static inline bool JERRY_ATTR_ALWAYS_INLINE ecma_gc_is_object_visited (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); return (object_p->type_flags_refs < ECMA_OBJECT_NON_VISITED); } /* ecma_gc_is_object_visited */ /** * Mark objects as visited starting from specified object as root */ static void ecma_gc_mark (ecma_object_t *object_p); /** * Set visited flag of the object. */ static void ecma_gc_set_object_visited (ecma_object_t *object_p) /**< object */ { if (object_p->type_flags_refs >= ECMA_OBJECT_NON_VISITED) { #if (JERRY_GC_MARK_LIMIT != 0) if (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) != 0) { JERRY_CONTEXT (ecma_gc_mark_recursion_limit)--; /* Set the reference count of gray object to 0 */ object_p->type_flags_refs &= (ecma_object_descriptor_t) (ECMA_OBJECT_REF_ONE - 1); ecma_gc_mark (object_p); JERRY_CONTEXT (ecma_gc_mark_recursion_limit)++; } else { /* Set the reference count of the non-marked gray object to 1 */ object_p->type_flags_refs &= (ecma_object_descriptor_t) ((ECMA_OBJECT_REF_ONE << 1) - 1); JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE); } #else /* (JERRY_GC_MARK_LIMIT == 0) */ /* Set the reference count of gray object to 0 */ object_p->type_flags_refs &= (ecma_object_descriptor_t) (ECMA_OBJECT_REF_ONE - 1); #endif /* (JERRY_GC_MARK_LIMIT != 0) */ } } /* ecma_gc_set_object_visited */ /** * Initialize GC information for the object */ extern inline void ecma_init_gc_info (ecma_object_t *object_p) /**< object */ { JERRY_CONTEXT (ecma_gc_objects_number)++; JERRY_CONTEXT (ecma_gc_new_objects)++; JERRY_ASSERT (object_p->type_flags_refs < ECMA_OBJECT_REF_ONE); object_p->type_flags_refs |= ECMA_OBJECT_REF_ONE; object_p->gc_next_cp = JERRY_CONTEXT (ecma_gc_objects_cp); ECMA_SET_NON_NULL_POINTER (JERRY_CONTEXT (ecma_gc_objects_cp), object_p); } /* ecma_init_gc_info */ /** * Increase reference counter of an object */ extern inline void JERRY_ATTR_ALWAYS_INLINE ecma_ref_object_inline (ecma_object_t *object_p) /**< object */ { if (JERRY_LIKELY (object_p->type_flags_refs < ECMA_OBJECT_MAX_REF)) { object_p->type_flags_refs = (ecma_object_descriptor_t) (object_p->type_flags_refs + ECMA_OBJECT_REF_ONE); } else { jerry_fatal (JERRY_FATAL_REF_COUNT_LIMIT); } } /* ecma_ref_object_inline */ /** * Increase reference counter of an object */ void ecma_ref_object (ecma_object_t *object_p) /**< object */ { ecma_ref_object_inline (object_p); } /* ecma_ref_object */ /** * Decrease reference counter of an object */ extern inline void JERRY_ATTR_ALWAYS_INLINE ecma_deref_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE); object_p->type_flags_refs = (ecma_object_descriptor_t) (object_p->type_flags_refs - ECMA_OBJECT_REF_ONE); } /* ecma_deref_object */ /** * Mark objects referenced by global object */ static void ecma_gc_mark_global_object (ecma_global_object_t *global_object_p) /**< global object */ { JERRY_ASSERT (global_object_p->extended_object.u.built_in.routine_id == 0); ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, global_object_p->global_env_cp)); #if JERRY_BUILTIN_REALMS ecma_gc_set_object_visited (ecma_get_object_from_value (global_object_p->this_binding)); #endif /* JERRY_BUILTIN_REALMS */ if (global_object_p->global_scope_cp != global_object_p->global_env_cp) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, global_object_p->global_scope_cp)); } jmem_cpointer_t *builtin_objects_p = global_object_p->builtin_objects; for (int i = 0; i < ECMA_BUILTIN_OBJECTS_COUNT; i++) { if (builtin_objects_p[i] != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, builtin_objects_p[i])); } } } /* ecma_gc_mark_global_object */ /** * Mark objects referenced by arguments object */ static void ecma_gc_mark_arguments_object (ecma_extended_object_t *ext_object_p) /**< arguments object */ { JERRY_ASSERT (ecma_get_object_type ((ecma_object_t *) ext_object_p) == ECMA_OBJECT_TYPE_CLASS); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) ext_object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (arguments_p->callee)); ecma_value_t *argv_p = (ecma_value_t *) (arguments_p + 1); if (ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; argv_p = (ecma_value_t *) (mapped_arguments_p + 1); ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, mapped_arguments_p->lex_env)); } uint32_t arguments_number = arguments_p->header.u.cls.u3.arguments_number; for (uint32_t i = 0; i < arguments_number; i++) { if (ecma_is_value_object (argv_p[i])) { ecma_gc_set_object_visited (ecma_get_object_from_value (argv_p[i])); } } } /* ecma_gc_mark_arguments_object */ /** * Mark referenced object from property */ static void ecma_gc_mark_properties (ecma_object_t *object_p, /**< object */ bool mark_references) /**< mark references */ { JERRY_UNUSED (mark_references); #if !JERRY_MODULE_SYSTEM JERRY_ASSERT (!mark_references); #endif /* !JERRY_MODULE_SYSTEM */ jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { prop_iter_cp = prop_iter_p->next_property_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *property_pair_p = (ecma_property_pair_t *) prop_iter_p; for (uint32_t index = 0; index < ECMA_PROPERTY_PAIR_ITEM_COUNT; index++) { uint8_t property = property_pair_p->header.types[index]; if (JERRY_LIKELY (ECMA_PROPERTY_IS_RAW (property))) { if (property & ECMA_PROPERTY_FLAG_DATA) { ecma_value_t value = property_pair_p->values[index].value; if (ecma_is_value_object (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (value)); } continue; } #if JERRY_MODULE_SYSTEM if (mark_references) { continue; } #endif /* JERRY_MODULE_SYSTEM */ ecma_property_value_t *accessor_objs_p = property_pair_p->values + index; ecma_getter_setter_pointers_t *get_set_pair_p = ecma_get_named_accessor_property (accessor_objs_p); if (get_set_pair_p->getter_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->getter_cp)); } if (get_set_pair_p->setter_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, get_set_pair_p->setter_cp)); } continue; } if (!ECMA_PROPERTY_IS_INTERNAL (property)) { JERRY_ASSERT (property == ECMA_PROPERTY_TYPE_DELETED || property == ECMA_PROPERTY_TYPE_HASHMAP); continue; } JERRY_ASSERT (property_pair_p->names_cp[index] >= LIT_INTERNAL_MAGIC_STRING_FIRST_DATA && property_pair_p->names_cp[index] < LIT_MAGIC_STRING__COUNT); switch (property_pair_p->names_cp[index]) { case LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD: { ecma_environment_record_t *environment_record_p; environment_record_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t, property_pair_p->values[index].value); if (environment_record_p->this_binding != ECMA_VALUE_UNINITIALIZED) { JERRY_ASSERT (ecma_is_value_object (environment_record_p->this_binding)); ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->this_binding)); } JERRY_ASSERT (ecma_is_value_object (environment_record_p->function_object)); ecma_gc_set_object_visited (ecma_get_object_from_value (environment_record_p->function_object)); break; } case LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS: { ecma_value_t *compact_collection_p; compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, property_pair_p->values[index].value); ecma_value_t *end_p = ecma_compact_collection_end (compact_collection_p); ecma_value_t *current_p = compact_collection_p + 1; while (end_p - current_p >= ECMA_PRIVATE_ELEMENT_LIST_SIZE) { current_p++; /* skip the type */ current_p++; /* skip the name */ ecma_value_t value = *current_p++; if (!ecma_is_value_undefined (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (value)); } } break; } #if JERRY_BUILTIN_CONTAINER case LIT_INTERNAL_MAGIC_STRING_WEAK_REFS: { ecma_value_t key_arg = ecma_make_object_value (object_p); ecma_collection_t *refs_p; refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, property_pair_p->values[index].value); for (uint32_t j = 0; j < refs_p->item_count; j++) { const ecma_value_t reference_value = refs_p->buffer_p[j]; if (ecma_is_value_empty (reference_value)) { continue; } ecma_object_t *reference_object_p = ecma_get_object_from_value (reference_value); JERRY_ASSERT (ecma_get_object_type (reference_object_p) == ECMA_OBJECT_TYPE_CLASS); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) reference_object_p; if (map_object_p->u.cls.type != ECMA_OBJECT_CLASS_CONTAINER || map_object_p->u.cls.u2.container_id != LIT_MAGIC_STRING_WEAKMAP_UL || !ecma_gc_is_object_visited (reference_object_p)) { continue; } ecma_value_t value = ecma_op_container_find_weak_value (reference_object_p, key_arg); if (ecma_is_value_object (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (value)); } } break; } #endif /* JERRY_BUILTIN_CONTAINER */ case LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES: { jerry_value_t value = property_pair_p->values[index].value; if (value == JMEM_CP_NULL) { JERRY_ASSERT (!(property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)); break; } ecma_native_pointer_t *item_p; item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value); do { jerry_object_native_info_t *native_info_p = item_p->native_info_p; JERRY_ASSERT (native_info_p != NULL && native_info_p->number_of_references > 0); uint8_t *start_p = ((uint8_t *) item_p->native_p) + native_info_p->offset_of_references; ecma_value_t *value_p = (ecma_value_t *) start_p; ecma_value_t *end_p = value_p + native_info_p->number_of_references; do { if (ecma_is_value_object (*value_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*value_p)); } } while (++value_p < end_p); if (property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL) { break; } item_p = &(((ecma_native_pointer_chain_t *) item_p)->next_p->data); } while (item_p != NULL); break; } } } prop_iter_cp = prop_iter_p->next_property_cp; } } /* ecma_gc_mark_properties */ /** * Mark compiled code. */ static void ecma_gc_mark_compiled_code (ecma_value_t script_value) /**< script value */ { cbc_script_t *script_p = ECMA_GET_INTERNAL_VALUE_POINTER (cbc_script_t, script_value); if (script_p->refs_and_type & CBC_SCRIPT_USER_VALUE_IS_OBJECT) { ecma_value_t user_value = CBC_SCRIPT_GET_USER_VALUE (script_p); JERRY_ASSERT (ecma_is_value_object (user_value)); ecma_gc_set_object_visited (ecma_get_object_from_value (user_value)); } #if JERRY_MODULE_SYSTEM if (script_p->refs_and_type & CBC_SCRIPT_HAS_IMPORT_META) { ecma_value_t import_meta = CBC_SCRIPT_GET_IMPORT_META (script_p, script_p->refs_and_type); JERRY_ASSERT (ecma_is_value_object (import_meta)); ecma_gc_set_object_visited (ecma_get_object_from_value (import_meta)); } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_REALMS ecma_gc_set_object_visited (script_p->realm_p); #endif /* JERRY_BUILTIN_REALMS */ } /* ecma_gc_mark_compiled_code */ /** * Mark objects referenced by bound function object. */ static void JERRY_ATTR_NOINLINE ecma_gc_mark_bound_function_object (ecma_object_t *object_p) /**< bound function object */ { JERRY_ASSERT (ecma_get_object_type (object_p) == ECMA_OBJECT_TYPE_BOUND_FUNCTION); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; ecma_object_t *target_func_p; target_func_p = ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, bound_func_p->header.u.bound_function.target_function); ecma_gc_set_object_visited (target_func_p); ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this; if (!ecma_is_value_integer_number (args_len_or_this)) { if (ecma_is_value_object (args_len_or_this)) { ecma_gc_set_object_visited (ecma_get_object_from_value (args_len_or_this)); } return; } ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this); ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1); JERRY_ASSERT (args_length > 0); for (ecma_integer_value_t i = 0; i < args_length; i++) { if (ecma_is_value_object (args_p[i])) { ecma_gc_set_object_visited (ecma_get_object_from_value (args_p[i])); } } } /* ecma_gc_mark_bound_function_object */ /** * Mark objects referenced by Promise built-in. */ static void ecma_gc_mark_promise_object (ecma_extended_object_t *ext_object_p) /**< extended object */ { /* Mark promise result. */ ecma_value_t result = ext_object_p->u.cls.u3.value; if (ecma_is_value_object (result)) { ecma_gc_set_object_visited (ecma_get_object_from_value (result)); } /* Mark all reactions. */ ecma_promise_object_t *promise_object_p = (ecma_promise_object_t *) ext_object_p; ecma_collection_t *collection_p = promise_object_p->reactions; if (collection_p != NULL) { ecma_value_t *buffer_p = collection_p->buffer_p; ecma_value_t *buffer_end_p = buffer_p + collection_p->item_count; while (buffer_p < buffer_end_p) { ecma_value_t value = *buffer_p++; ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, value)); if (JMEM_CP_GET_FIRST_BIT_FROM_POINTER_TAG (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*buffer_p++)); } if (JMEM_CP_GET_SECOND_BIT_FROM_POINTER_TAG (value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*buffer_p++)); } } } } /* ecma_gc_mark_promise_object */ #if JERRY_BUILTIN_CONTAINER /** * Mark objects referenced by Map built-in. */ static void ecma_gc_mark_map_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) { ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); if (ecma_is_value_empty (entry_p->key)) { continue; } if (ecma_is_value_object (entry_p->key)) { ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->key)); } if (ecma_is_value_object (entry_p->value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->value)); } } } /* ecma_gc_mark_map_object */ /** * Mark objects referenced by WeakMap built-in. */ static void ecma_gc_mark_weakmap_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_PAIR_SIZE) { ecma_container_pair_t *entry_p = (ecma_container_pair_t *) (start_p + i); if (ecma_is_value_empty (entry_p->key)) { continue; } JERRY_ASSERT (ecma_is_value_object (entry_p->key)); if (ecma_is_value_object (entry_p->value) && ecma_gc_is_object_visited (ecma_get_object_from_value (entry_p->key))) { ecma_gc_set_object_visited (ecma_get_object_from_value (entry_p->value)); } } } /* ecma_gc_mark_weakmap_object */ /** * Mark objects referenced by Set built-in. */ static void ecma_gc_mark_set_object (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_value_t *start_p = ECMA_CONTAINER_START (container_p); uint32_t entry_count = ECMA_CONTAINER_ENTRY_COUNT (container_p); for (uint32_t i = 0; i < entry_count; i += ECMA_CONTAINER_VALUE_SIZE) { ecma_value_t *entry_p = start_p + i; if (ecma_is_value_empty (*entry_p)) { continue; } if (ecma_is_value_object (*entry_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*entry_p)); } } } /* ecma_gc_mark_set_object */ #endif /* JERRY_BUILTIN_CONTAINER */ /** * Mark objects referenced by inactive generator functions, async functions, etc. */ static void ecma_gc_mark_executable_object (ecma_object_t *object_p) /**< object */ { vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; if (executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_ASYNC_GENERATOR_CALLED) { ecma_value_t task = executable_object_p->extended_object.u.cls.u3.head; while (!ECMA_IS_INTERNAL_VALUE_NULL (task)) { ecma_async_generator_task_t *task_p; task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, task); JERRY_ASSERT (ecma_is_value_object (task_p->promise)); ecma_gc_set_object_visited (ecma_get_object_from_value (task_p->promise)); if (ecma_is_value_object (task_p->operation_value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (task_p->operation_value)); } task = task_p->next; } } ecma_gc_set_object_visited (executable_object_p->frame_ctx.lex_env_p); ecma_gc_set_object_visited (executable_object_p->shared.function_object_p); if (!ECMA_EXECUTABLE_OBJECT_IS_SUSPENDED (executable_object_p) && !(executable_object_p->extended_object.u.cls.u2.executable_obj_flags & ECMA_EXECUTABLE_OBJECT_DO_AWAIT_OR_YIELD)) { /* All objects referenced by running executable objects are strong roots, * and a finished executable object cannot refer to other values. */ return; } if (ecma_is_value_object (executable_object_p->frame_ctx.this_binding)) { ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->frame_ctx.this_binding)); } const ecma_compiled_code_t *bytecode_header_p = executable_object_p->shared.bytecode_header_p; size_t register_end; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end = args_p->register_end; } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end = args_p->register_end; } ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); ecma_value_t *register_end_p = register_p + register_end; while (register_p < register_end_p) { if (ecma_is_value_object (*register_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p)); } register_p++; } if (executable_object_p->frame_ctx.context_depth > 0) { ecma_value_t *context_end_p = register_p; register_p += executable_object_p->frame_ctx.context_depth; ecma_value_t *context_top_p = register_p; do { if (VM_CONTEXT_IS_VARIABLE_LENGTH (VM_GET_CONTEXT_TYPE (context_top_p[-1]))) { ecma_value_t *last_item_p = context_top_p - VM_GET_CONTEXT_END (context_top_p[-1]); JERRY_ASSERT (last_item_p >= context_end_p); context_top_p--; do { ecma_gc_set_object_visited (ecma_get_object_from_value (*(--context_top_p))); } while (context_top_p > last_item_p); continue; } uint32_t offsets = vm_get_context_value_offsets (context_top_p); while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) { int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); if (ecma_is_value_object (context_top_p[offset])) { ecma_gc_set_object_visited (ecma_get_object_from_value (context_top_p[offset])); } offsets >>= VM_CONTEXT_OFFSET_SHIFT; } JERRY_ASSERT (context_top_p >= context_end_p + offsets); context_top_p -= offsets; } while (context_top_p > context_end_p); } register_end_p = executable_object_p->frame_ctx.stack_top_p; while (register_p < register_end_p) { if (ecma_is_value_object (*register_p)) { ecma_gc_set_object_visited (ecma_get_object_from_value (*register_p)); } register_p++; } if (ecma_is_value_object (executable_object_p->iterator)) { ecma_gc_set_object_visited (ecma_get_object_from_value (executable_object_p->iterator)); } } /* ecma_gc_mark_executable_object */ #if JERRY_BUILTIN_PROXY /** * Mark the objects referenced by a proxy object */ static void ecma_gc_mark_proxy_object (ecma_object_t *object_p) /**< proxy object */ { JERRY_ASSERT (ECMA_OBJECT_IS_PROXY (object_p)); ecma_proxy_object_t *proxy_p = (ecma_proxy_object_t *) object_p; if (!ecma_is_value_null (proxy_p->target)) { ecma_gc_set_object_visited (ecma_get_object_from_value (proxy_p->target)); } if (!ecma_is_value_null (proxy_p->handler)) { ecma_gc_set_object_visited (ecma_get_object_from_value (proxy_p->handler)); } } /* ecma_gc_mark_proxy_object */ #endif /* JERRY_BUILTIN_PROXY */ /** * Mark objects as visited starting from specified object as root */ static void ecma_gc_mark (ecma_object_t *object_p) /**< object to mark from */ { JERRY_ASSERT (object_p != NULL); JERRY_ASSERT (ecma_gc_is_object_visited (object_p)); if (ecma_is_lexical_environment (object_p)) { jmem_cpointer_t outer_lex_env_cp = object_p->u2.outer_reference_cp; if (outer_lex_env_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, outer_lex_env_cp)); } switch (ecma_get_lex_env_type (object_p)) { case ECMA_LEXICAL_ENVIRONMENT_CLASS: { #if JERRY_MODULE_SYSTEM if (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA) { if (ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) { ecma_gc_mark_properties (object_p, true); } ecma_gc_set_object_visited (((ecma_lexical_environment_class_t *) object_p)->object_p); return; } #endif /* JERRY_MODULE_SYSTEM */ /* FALLTHRU */ } case ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND: { ecma_object_t *binding_object_p = ecma_get_lex_env_binding_object (object_p); ecma_gc_set_object_visited (binding_object_p); return; } default: { break; } } } else { /** * Have the object's prototype here so the object could set it to JMEM_CP_NULL * if the prototype should be ignored (like in case of PROXY). */ jmem_cpointer_t proto_cp = object_p->u2.prototype_cp; switch (ecma_get_object_type (object_p)) { case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: { ecma_extended_object_t *extended_object_p = (ecma_extended_object_t *) object_p; if (extended_object_p->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) { ecma_gc_mark_global_object ((ecma_global_object_t *) object_p); } #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = extended_object_p->u.built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ break; } case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: { #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = ((ecma_extended_built_in_object_t *) object_p)->built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ /* FALLTHRU */ } case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_ARGUMENTS: { ecma_gc_mark_arguments_object (ext_object_p); break; } #if JERRY_PARSER case ECMA_OBJECT_CLASS_SCRIPT: { const ecma_compiled_code_t *compiled_code_p; compiled_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); JERRY_ASSERT (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); ecma_gc_mark_compiled_code (((cbc_uint8_arguments_t *) compiled_code_p)->script_value); break; } #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_TYPEDARRAY: { ecma_gc_set_object_visited (ecma_typedarray_get_arraybuffer (object_p)); break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { JERRY_ASSERT (proto_cp == JMEM_CP_NULL); ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, ext_object_p->u.cls.u3.value)); ecma_gc_mark_properties (object_p, true); return; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE: { ecma_module_t *module_p = ((ecma_module_t *) ext_object_p); if (module_p->scope_p != NULL) { ecma_gc_set_object_visited (((ecma_module_t *) ext_object_p)->scope_p); } if (module_p->namespace_object_p != NULL) { ecma_gc_set_object_visited (((ecma_module_t *) ext_object_p)->namespace_object_p); } if (!(module_p->header.u.cls.u2.module_flags & ECMA_MODULE_IS_NATIVE) && module_p->u.compiled_code_p != NULL) { const ecma_compiled_code_t *compiled_code_p = module_p->u.compiled_code_p; JERRY_ASSERT (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); ecma_gc_mark_compiled_code (((cbc_uint8_arguments_t *) compiled_code_p)->script_value); } ecma_module_node_t *node_p = module_p->imports_p; while (node_p != NULL) { if (ecma_is_value_object (node_p->u.path_or_module)) { ecma_gc_set_object_visited (ecma_get_object_from_value (node_p->u.path_or_module)); } node_p = node_p->next_p; } break; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_BUILTIN_DATAVIEW case ECMA_OBJECT_CLASS_DATAVIEW: { ecma_dataview_object_t *dataview_p = (ecma_dataview_object_t *) object_p; ecma_gc_set_object_visited (dataview_p->buffer_p); break; } #endif /* JERRY_BUILTIN_DATAVIEW */ #if JERRY_BUILTIN_CONTAINER case ECMA_OBJECT_CLASS_CONTAINER: { if (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_MAP_UL) { ecma_gc_mark_map_object (object_p); break; } if (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKMAP_UL) { ecma_gc_mark_weakmap_object (object_p); break; } if (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_SET_UL) { ecma_gc_mark_set_object (object_p); break; } JERRY_ASSERT (ext_object_p->u.cls.u2.container_id == LIT_MAGIC_STRING_WEAKSET_UL); break; } #endif /* JERRY_BUILTIN_CONTAINER */ case ECMA_OBJECT_CLASS_GENERATOR: case ECMA_OBJECT_CLASS_ASYNC_GENERATOR: { ecma_gc_mark_executable_object (object_p); break; } case ECMA_OBJECT_CLASS_PROMISE: { ecma_gc_mark_promise_object (ext_object_p); break; } case ECMA_OBJECT_CLASS_PROMISE_CAPABILITY: { ecma_promise_capabality_t *capability_p = (ecma_promise_capabality_t *) object_p; if (ecma_is_value_object (capability_p->header.u.cls.u3.promise)) { ecma_gc_set_object_visited (ecma_get_object_from_value (capability_p->header.u.cls.u3.promise)); } if (ecma_is_value_object (capability_p->resolve)) { ecma_gc_set_object_visited (ecma_get_object_from_value (capability_p->resolve)); } if (ecma_is_value_object (capability_p->reject)) { ecma_gc_set_object_visited (ecma_get_object_from_value (capability_p->reject)); } break; } case ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR: { ecma_async_from_sync_iterator_object_t *iter_p = (ecma_async_from_sync_iterator_object_t *) ext_object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (iter_p->header.u.cls.u3.sync_iterator)); if (!ecma_is_value_undefined (iter_p->sync_next_method)) { ecma_gc_set_object_visited (ecma_get_object_from_value (iter_p->sync_next_method)); } break; } case ECMA_OBJECT_CLASS_ARRAY_ITERATOR: case ECMA_OBJECT_CLASS_SET_ITERATOR: case ECMA_OBJECT_CLASS_MAP_ITERATOR: { ecma_value_t iterated_value = ext_object_p->u.cls.u3.iterated_value; if (!ecma_is_value_empty (iterated_value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (iterated_value)); } break; } #if JERRY_BUILTIN_REGEXP case ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR: { ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) object_p; ecma_value_t regexp = regexp_string_iterator_obj->iterating_regexp; ecma_gc_set_object_visited (ecma_get_object_from_value (regexp)); break; } #endif /* JERRY_BUILTIN_REGEXP */ default: { /* The ECMA_OBJECT_CLASS__MAX type represents an uninitialized class. */ JERRY_ASSERT (ext_object_p->u.cls.type <= ECMA_OBJECT_CLASS__MAX); break; } } break; } case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = ((ecma_extended_built_in_object_t *) object_p)->built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ /* FALLTHRU */ } case ECMA_OBJECT_TYPE_ARRAY: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; if (JERRY_UNLIKELY (ext_object_p->u.array.length_prop_and_hole_count & ECMA_ARRAY_TEMPLATE_LITERAL)) { /* Template objects are never marked. */ JERRY_ASSERT (object_p->type_flags_refs >= ECMA_OBJECT_REF_ONE); return; } if (ecma_op_array_is_fast_array (ext_object_p)) { if (object_p->u1.property_list_cp != JMEM_CP_NULL) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); for (uint32_t i = 0; i < ext_object_p->u.array.length; i++) { if (ecma_is_value_object (values_p[i])) { ecma_gc_set_object_visited (ecma_get_object_from_value (values_p[i])); } } } if (proto_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); } return; } break; } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { ecma_gc_mark_proxy_object (object_p); /* Prototype of proxy object is a bit set. */ proto_cp = JMEM_CP_NULL; break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_FUNCTION: { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; ecma_gc_set_object_visited ( ECMA_GET_NON_NULL_POINTER_FROM_POINTER_TAG (ecma_object_t, ext_func_p->u.function.scope_cp)); const ecma_compiled_code_t *compiled_code_p = ecma_op_function_get_compiled_code (ext_func_p); if (CBC_FUNCTION_IS_ARROW (compiled_code_p->status_flags)) { ecma_arrow_function_t *arrow_func_p = (ecma_arrow_function_t *) object_p; if (ecma_is_value_object (arrow_func_p->this_binding)) { ecma_gc_set_object_visited (ecma_get_object_from_value (arrow_func_p->this_binding)); } if (ecma_is_value_object (arrow_func_p->new_target)) { ecma_gc_set_object_visited (ecma_get_object_from_value (arrow_func_p->new_target)); } } #if JERRY_SNAPSHOT_EXEC if (JERRY_UNLIKELY (compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)) { /* Static snapshot functions have a global realm */ break; } #endif /* JERRY_SNAPSHOT_EXEC */ JERRY_ASSERT (!(compiled_code_p->status_flags & CBC_CODE_FLAGS_STATIC_FUNCTION)); ecma_gc_mark_compiled_code (((cbc_uint8_arguments_t *) compiled_code_p)->script_value); break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ecma_gc_mark_bound_function_object (object_p); break; } case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; #if JERRY_BUILTIN_REALMS ecma_value_t realm_value = ext_func_p->u.built_in.realm_value; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, realm_value)); #endif /* JERRY_BUILTIN_REALMS */ if (ext_func_p->u.built_in.id == ECMA_BUILTIN_ID_HANDLER) { switch (ext_func_p->u.built_in.routine_id) { case ECMA_NATIVE_HANDLER_PROMISE_RESOLVE: case ECMA_NATIVE_HANDLER_PROMISE_REJECT: { ecma_promise_resolver_t *resolver_obj_p = (ecma_promise_resolver_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (resolver_obj_p->promise)); break; } case ECMA_NATIVE_HANDLER_PROMISE_THEN_FINALLY: case ECMA_NATIVE_HANDLER_PROMISE_CATCH_FINALLY: { ecma_promise_finally_function_t *finally_obj_p = (ecma_promise_finally_function_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (finally_obj_p->constructor)); ecma_gc_set_object_visited (ecma_get_object_from_value (finally_obj_p->on_finally)); break; } case ECMA_NATIVE_HANDLER_PROMISE_CAPABILITY_EXECUTOR: { ecma_promise_capability_executor_t *executor_p = (ecma_promise_capability_executor_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->capability)); break; } case ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER: { ecma_promise_all_executor_t *executor_p = (ecma_promise_all_executor_t *) object_p; ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->capability)); ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->values)); ecma_gc_set_object_visited (ecma_get_object_from_value (executor_p->remaining_elements)); break; } #if JERRY_BUILTIN_PROXY case ECMA_NATIVE_HANDLER_PROXY_REVOKE: { ecma_revocable_proxy_object_t *rev_proxy_p = (ecma_revocable_proxy_object_t *) object_p; if (!ecma_is_value_null (rev_proxy_p->proxy)) { ecma_gc_set_object_visited (ecma_get_object_from_value (rev_proxy_p->proxy)); } break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_NATIVE_HANDLER_VALUE_THUNK: case ECMA_NATIVE_HANDLER_VALUE_THROWER: { ecma_promise_value_thunk_t *thunk_obj_p = (ecma_promise_value_thunk_t *) object_p; if (ecma_is_value_object (thunk_obj_p->value)) { ecma_gc_set_object_visited (ecma_get_object_from_value (thunk_obj_p->value)); } break; } case ECMA_NATIVE_HANDLER_ASYNC_FROM_SYNC_ITERATOR_UNWRAP: { break; } default: { JERRY_UNREACHABLE (); } } } break; } case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { ecma_gc_mark_compiled_code (((ecma_extended_object_t *) object_p)->u.constructor_function.script_value); break; } #if JERRY_BUILTIN_REALMS case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { ecma_native_function_t *native_function_p = (ecma_native_function_t *) object_p; ecma_gc_set_object_visited (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_object_t, native_function_p->realm_value)); break; } #endif /* JERRY_BUILTIN_REALMS */ default: { break; } } if (proto_cp != JMEM_CP_NULL) { ecma_gc_set_object_visited (ECMA_GET_NON_NULL_POINTER (ecma_object_t, proto_cp)); } } ecma_gc_mark_properties (object_p, false); } /* ecma_gc_mark */ /** * Free the native handle/pointer by calling its free callback. */ static void ecma_gc_free_native_pointer (ecma_property_t property, /**< property descriptor */ ecma_value_t value) /**< property value */ { if (JERRY_LIKELY (property & ECMA_PROPERTY_FLAG_SINGLE_EXTERNAL)) { JERRY_ASSERT (value != JMEM_CP_NULL); ecma_native_pointer_t *native_pointer_p; native_pointer_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_t, value); if (native_pointer_p->native_info_p != NULL) { jerry_object_native_free_cb_t free_cb = native_pointer_p->native_info_p->free_cb; if (free_cb != NULL) { free_cb (native_pointer_p->native_p, native_pointer_p->native_info_p); } } jmem_heap_free_block (native_pointer_p, sizeof (ecma_native_pointer_t)); return; } if (value == JMEM_CP_NULL) { return; } ecma_native_pointer_chain_t *item_p; item_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_native_pointer_chain_t, value); do { if (item_p->data.native_info_p != NULL) { jerry_object_native_free_cb_t free_cb = item_p->data.native_info_p->free_cb; if (free_cb != NULL) { free_cb (item_p->data.native_p, item_p->data.native_info_p); } } ecma_native_pointer_chain_t *next_p = item_p->next_p; jmem_heap_free_block (item_p, sizeof (ecma_native_pointer_chain_t)); item_p = next_p; } while (item_p != NULL); } /* ecma_gc_free_native_pointer */ /** * Free specified arguments object. * * @return allocated object's size */ static size_t ecma_free_arguments_object (ecma_extended_object_t *ext_object_p) /**< arguments object */ { JERRY_ASSERT (ecma_get_object_type ((ecma_object_t *) ext_object_p) == ECMA_OBJECT_TYPE_CLASS); size_t object_size = sizeof (ecma_unmapped_arguments_t); if (ext_object_p->u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_MAPPED) { ecma_mapped_arguments_t *mapped_arguments_p = (ecma_mapped_arguments_t *) ext_object_p; object_size = sizeof (ecma_mapped_arguments_t); #if JERRY_SNAPSHOT_EXEC if (!(mapped_arguments_p->unmapped.header.u.cls.u1.arguments_flags & ECMA_ARGUMENTS_OBJECT_STATIC_BYTECODE)) #endif /* JERRY_SNAPSHOT_EXEC */ { ecma_compiled_code_t *byte_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, mapped_arguments_p->u.byte_code); ecma_bytecode_deref (byte_code_p); } } ecma_value_t *argv_p = (ecma_value_t *) (((uint8_t *) ext_object_p) + object_size); ecma_unmapped_arguments_t *arguments_p = (ecma_unmapped_arguments_t *) ext_object_p; uint32_t arguments_number = arguments_p->header.u.cls.u3.arguments_number; for (uint32_t i = 0; i < arguments_number; i++) { ecma_free_value_if_not_object (argv_p[i]); } uint32_t saved_argument_count = JERRY_MAX (arguments_number, arguments_p->header.u.cls.u2.formal_params_number); return object_size + (saved_argument_count * sizeof (ecma_value_t)); } /* ecma_free_arguments_object */ /** * Free specified fast access mode array object. */ static void ecma_free_fast_access_array (ecma_object_t *object_p) /**< fast access mode array object to free */ { JERRY_ASSERT (ecma_op_object_is_fast_array (object_p)); ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; const uint32_t aligned_length = ECMA_FAST_ARRAY_ALIGN_LENGTH (ext_object_p->u.array.length); if (object_p->u1.property_list_cp != JMEM_CP_NULL) { ecma_value_t *values_p = ECMA_GET_NON_NULL_POINTER (ecma_value_t, object_p->u1.property_list_cp); for (uint32_t i = 0; i < aligned_length; i++) { ecma_free_value_if_not_object (values_p[i]); } jmem_heap_free_block (values_p, aligned_length * sizeof (ecma_value_t)); } ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); } /* ecma_free_fast_access_array */ /** * Free non-objects referenced by inactive generator functions, async functions, etc. * * @return total object size */ static size_t ecma_gc_free_executable_object (ecma_object_t *object_p) /**< object */ { vm_executable_object_t *executable_object_p = (vm_executable_object_t *) object_p; const ecma_compiled_code_t *bytecode_header_p = executable_object_p->shared.bytecode_header_p; size_t size, register_end; if (bytecode_header_p->status_flags & CBC_CODE_FLAGS_UINT16_ARGUMENTS) { cbc_uint16_arguments_t *args_p = (cbc_uint16_arguments_t *) bytecode_header_p; register_end = args_p->register_end; size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } else { cbc_uint8_arguments_t *args_p = (cbc_uint8_arguments_t *) bytecode_header_p; register_end = args_p->register_end; size = (register_end + (size_t) args_p->stack_limit) * sizeof (ecma_value_t); } size = JERRY_ALIGNUP (sizeof (vm_executable_object_t) + size, sizeof (uintptr_t)); ecma_bytecode_deref ((ecma_compiled_code_t *) bytecode_header_p); uint16_t executable_obj_flags = executable_object_p->extended_object.u.cls.u2.executable_obj_flags; JERRY_ASSERT (!(executable_obj_flags & ECMA_EXECUTABLE_OBJECT_RUNNING)); if (executable_obj_flags & ECMA_ASYNC_GENERATOR_CALLED) { ecma_value_t task = executable_object_p->extended_object.u.cls.u3.head; while (!ECMA_IS_INTERNAL_VALUE_NULL (task)) { ecma_async_generator_task_t *task_p; task_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_async_generator_task_t, task); JERRY_ASSERT (ecma_is_value_object (task_p->promise)); ecma_free_value_if_not_object (task_p->operation_value); task = task_p->next; jmem_heap_free_block (task_p, sizeof (ecma_async_generator_task_t)); } } if (executable_obj_flags & ECMA_EXECUTABLE_OBJECT_COMPLETED) { return size; } ecma_free_value_if_not_object (executable_object_p->frame_ctx.this_binding); ecma_value_t *register_p = VM_GET_REGISTERS (&executable_object_p->frame_ctx); ecma_value_t *register_end_p = register_p + register_end; while (register_p < register_end_p) { ecma_free_value_if_not_object (*register_p++); } if (executable_object_p->frame_ctx.context_depth > 0) { ecma_value_t *context_end_p = register_p; register_p += executable_object_p->frame_ctx.context_depth; ecma_value_t *context_top_p = register_p; do { context_top_p[-1] &= (uint32_t) ~VM_CONTEXT_HAS_LEX_ENV; if (VM_CONTEXT_IS_VARIABLE_LENGTH (VM_GET_CONTEXT_TYPE (context_top_p[-1]))) { ecma_value_t *last_item_p = context_top_p - VM_GET_CONTEXT_END (context_top_p[-1]); JERRY_ASSERT (last_item_p >= context_end_p); context_top_p--; do { ecma_free_value_if_not_object (*(--context_top_p)); } while (context_top_p > last_item_p); continue; } uint32_t offsets = vm_get_context_value_offsets (context_top_p); while (VM_CONTEXT_HAS_NEXT_OFFSET (offsets)) { int32_t offset = VM_CONTEXT_GET_NEXT_OFFSET (offsets); if (ecma_is_value_object (context_top_p[offset])) { context_top_p[offset] = ECMA_VALUE_UNDEFINED; } offsets >>= VM_CONTEXT_OFFSET_SHIFT; } context_top_p = vm_stack_context_abort (&executable_object_p->frame_ctx, context_top_p); } while (context_top_p > context_end_p); } register_end_p = executable_object_p->frame_ctx.stack_top_p; while (register_p < register_end_p) { ecma_free_value_if_not_object (*register_p++); } return size; } /* ecma_gc_free_executable_object */ JERRY_STATIC_ASSERT (!ECMA_PROPERTY_IS_RAW (ECMA_PROPERTY_TYPE_DELETED), ecma_property_type_deleted_must_not_be_raw_property); JERRY_STATIC_ASSERT ((ECMA_PROPERTY_TYPE_DELETED & ECMA_PROPERTY_FLAG_LCACHED) == 0, ecma_property_type_deleted_must_not_have_lcached_flag); JERRY_STATIC_ASSERT (ECMA_GC_FREE_SECOND_PROPERTY == 1, ecma_gc_free_second_must_be_one); /** * Free property of an object */ void ecma_gc_free_property (ecma_object_t *object_p, /**< object */ ecma_property_pair_t *prop_pair_p, /**< property pair */ uint32_t options) /**< option bits including property index */ { /* Both cannot be deleted. */ JERRY_ASSERT (prop_pair_p->header.types[0] != ECMA_PROPERTY_TYPE_DELETED || prop_pair_p->header.types[1] != ECMA_PROPERTY_TYPE_DELETED); JERRY_ASSERT (prop_pair_p->header.types[0] != ECMA_PROPERTY_TYPE_HASHMAP); uint32_t index = (options & ECMA_GC_FREE_SECOND_PROPERTY); jmem_cpointer_t name_cp = prop_pair_p->names_cp[index]; ecma_property_t *property_p = prop_pair_p->header.types + index; ecma_property_t property = *property_p; #if JERRY_LCACHE if ((property & ECMA_PROPERTY_FLAG_LCACHED) != 0) { ecma_lcache_invalidate (object_p, name_cp, property_p); } #endif /* JERRY_LCACHE */ if (ECMA_PROPERTY_IS_RAW (property)) { if (ECMA_PROPERTY_GET_NAME_TYPE (property) == ECMA_DIRECT_STRING_PTR) { ecma_string_t *prop_name_p = ECMA_GET_NON_NULL_POINTER (ecma_string_t, name_cp); ecma_deref_ecma_string (prop_name_p); } if (property & ECMA_PROPERTY_FLAG_DATA) { ecma_free_value_if_not_object (prop_pair_p->values[index].value); return; } if (JERRY_UNLIKELY (options & ECMA_GC_FREE_REFERENCES)) { return; } #if JERRY_CPOINTER_32_BIT ecma_getter_setter_pointers_t *getter_setter_pair_p; getter_setter_pair_p = ECMA_GET_NON_NULL_POINTER (ecma_getter_setter_pointers_t, prop_pair_p->values[index].getter_setter_pair_cp); jmem_pools_free (getter_setter_pair_p, sizeof (ecma_getter_setter_pointers_t)); #endif /* JERRY_CPOINTER_32_BIT */ return; } if (property == ECMA_PROPERTY_TYPE_DELETED) { return; } ecma_value_t value = prop_pair_p->values[index].value; switch (name_cp) { case LIT_INTERNAL_MAGIC_STRING_ENVIRONMENT_RECORD: { ecma_environment_record_t *environment_record_p; environment_record_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_environment_record_t, value); jmem_heap_free_block (environment_record_p, sizeof (ecma_environment_record_t)); break; } case LIT_INTERNAL_MAGIC_STRING_CLASS_FIELD_COMPUTED: { ecma_value_t *compact_collection_p; compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); ecma_compact_collection_free (compact_collection_p); break; } case LIT_INTERNAL_MAGIC_STRING_CLASS_PRIVATE_ELEMENTS: { ecma_value_t *compact_collection_p; compact_collection_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_value_t, value); ecma_value_t *end_p = ecma_compact_collection_end (compact_collection_p); ecma_value_t *current_p = compact_collection_p + 1; JERRY_ASSERT ((end_p - current_p) % ECMA_PRIVATE_ELEMENT_LIST_SIZE == 0); while (current_p < end_p) { current_p++; /* skip the type */ ecma_deref_ecma_string (ecma_get_prop_name_from_value (*current_p++)); current_p++; /* skip the value */ } ecma_compact_collection_destroy (compact_collection_p); break; } #if JERRY_BUILTIN_WEAKREF || JERRY_BUILTIN_CONTAINER case LIT_INTERNAL_MAGIC_STRING_WEAK_REFS: { ecma_collection_t *refs_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, value); for (uint32_t j = 0; j < refs_p->item_count; j++) { const ecma_value_t reference_value = refs_p->buffer_p[j]; if (!ecma_is_value_empty (reference_value)) { ecma_object_t *obj_p = ecma_get_object_from_value (reference_value); if (ecma_object_class_is (obj_p, ECMA_OBJECT_CLASS_WEAKREF)) { ecma_extended_object_t *ext_obj_p = (ecma_extended_object_t *) obj_p; ext_obj_p->u.cls.u3.target = ECMA_VALUE_UNDEFINED; continue; } ecma_op_container_remove_weak_entry (obj_p, ecma_make_object_value (object_p)); } } ecma_collection_destroy (refs_p); break; } #endif /* JERRY_BUILTIN_CONTAINER */ default: { JERRY_ASSERT (name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER || name_cp == LIT_INTERNAL_MAGIC_STRING_NATIVE_POINTER_WITH_REFERENCES); ecma_gc_free_native_pointer (property, value); break; } } } /* ecma_gc_free_property */ /** * Free properties of an object */ void ecma_gc_free_properties (ecma_object_t *object_p, /**< object */ uint32_t options) /**< option bits */ { jmem_cpointer_t prop_iter_cp = object_p->u1.property_list_cp; #if JERRY_PROPERTY_HASHMAP if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { ecma_property_hashmap_free (object_p); prop_iter_cp = object_p->u1.property_list_cp; } } #endif /* JERRY_PROPERTY_HASHMAP */ while (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); JERRY_ASSERT (ECMA_PROPERTY_IS_PROPERTY_PAIR (prop_iter_p)); ecma_property_pair_t *prop_pair_p = (ecma_property_pair_t *) prop_iter_p; for (uint32_t i = 0; i < ECMA_PROPERTY_PAIR_ITEM_COUNT; i++) { ecma_gc_free_property (object_p, prop_pair_p, i | options); } prop_iter_cp = prop_iter_p->next_property_cp; ecma_dealloc_property_pair (prop_pair_p); } } /* ecma_gc_free_properties */ /** * Free specified object. */ static void ecma_gc_free_object (ecma_object_t *object_p) /**< object to free */ { JERRY_ASSERT (object_p != NULL && !ecma_gc_is_object_visited (object_p)); JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_objects_number) > 0); JERRY_CONTEXT (ecma_gc_objects_number)--; if (ecma_is_lexical_environment (object_p)) { #if JERRY_MODULE_SYSTEM if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_CLASS && (object_p->type_flags_refs & ECMA_OBJECT_FLAG_LEXICAL_ENV_HAS_DATA)) { if (ECMA_LEX_ENV_CLASS_IS_MODULE (object_p)) { ecma_gc_free_properties (object_p, ECMA_GC_FREE_REFERENCES); } ecma_dealloc_extended_object (object_p, sizeof (ecma_lexical_environment_class_t)); return; } #endif /* JERRY_MODULE_SYSTEM */ if (ecma_get_lex_env_type (object_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { ecma_gc_free_properties (object_p, ECMA_GC_FREE_NO_OPTIONS); } ecma_dealloc_object (object_p); return; } ecma_object_type_t object_type = ecma_get_object_type (object_p); size_t ext_object_size = sizeof (ecma_extended_object_t); switch (object_type) { case ECMA_OBJECT_TYPE_GENERAL: { ecma_gc_free_properties (object_p, ECMA_GC_FREE_NO_OPTIONS); ecma_dealloc_object (object_p); return; } case ECMA_OBJECT_TYPE_BUILT_IN_GENERAL: { if (((ecma_extended_object_t *) object_p)->u.built_in.id == ECMA_BUILTIN_ID_GLOBAL) { ext_object_size = sizeof (ecma_global_object_t); break; } uint8_t bitset_size = ((ecma_extended_object_t *) object_p)->u.built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); break; } case ECMA_OBJECT_TYPE_BUILT_IN_CLASS: { ext_object_size = sizeof (ecma_extended_built_in_object_t); uint8_t bitset_size = ((ecma_extended_built_in_object_t *) object_p)->built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); /* FALLTHRU */ } case ECMA_OBJECT_TYPE_CLASS: { ecma_extended_object_t *ext_object_p = (ecma_extended_object_t *) object_p; switch (ext_object_p->u.cls.type) { case ECMA_OBJECT_CLASS_STRING: case ECMA_OBJECT_CLASS_NUMBER: case ECMA_OBJECT_CLASS_SYMBOL: #if JERRY_BUILTIN_BIGINT case ECMA_OBJECT_CLASS_BIGINT: #endif /* JERRY_BUILTIN_BIGINT */ { ecma_free_value (ext_object_p->u.cls.u3.value); break; } case ECMA_OBJECT_CLASS_ARGUMENTS: { ext_object_size = ecma_free_arguments_object (ext_object_p); break; } #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_TYPEDARRAY: { if (ext_object_p->u.cls.u2.typedarray_flags & ECMA_TYPEDARRAY_IS_EXTENDED) { ext_object_size = sizeof (ecma_extended_typedarray_object_t); } break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE_NAMESPACE: { ecma_gc_free_properties (object_p, ECMA_GC_FREE_REFERENCES); ecma_dealloc_extended_object (object_p, sizeof (ecma_extended_object_t)); return; } #endif /* JERRY_MODULE_SYSTEM */ #if JERRY_PARSER case ECMA_OBJECT_CLASS_SCRIPT: { ecma_compiled_code_t *compiled_code_p; compiled_code_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); ecma_bytecode_deref (compiled_code_p); break; } #endif /* JERRY_PARSER */ #if JERRY_BUILTIN_DATE case ECMA_OBJECT_CLASS_DATE: { ext_object_size = sizeof (ecma_date_object_t); break; } #endif /* JERRY_BUILTIN_DATE */ #if JERRY_BUILTIN_REGEXP case ECMA_OBJECT_CLASS_REGEXP: { ecma_compiled_code_t *bytecode_p = ECMA_GET_INTERNAL_VALUE_ANY_POINTER (ecma_compiled_code_t, ext_object_p->u.cls.u3.value); ecma_bytecode_deref (bytecode_p); break; } #endif /* JERRY_BUILTIN_REGEXP */ case ECMA_OBJECT_CLASS_STRING_ITERATOR: { ecma_value_t iterated_value = ext_object_p->u.cls.u3.iterated_value; if (!ecma_is_value_empty (iterated_value)) { ecma_deref_ecma_string (ecma_get_string_from_value (iterated_value)); } break; } #if JERRY_BUILTIN_REGEXP case ECMA_OBJECT_CLASS_REGEXP_STRING_ITERATOR: { ecma_regexp_string_iterator_t *regexp_string_iterator_obj = (ecma_regexp_string_iterator_t *) object_p; ecma_value_t iterated_string = regexp_string_iterator_obj->iterated_string; if (!ecma_is_value_empty (iterated_string)) { ecma_deref_ecma_string (ecma_get_string_from_value (iterated_string)); } ext_object_size = sizeof (ecma_regexp_string_iterator_t); break; } #endif /* JERRY_BUILTIN_REGEXP */ #if JERRY_BUILTIN_TYPEDARRAY case ECMA_OBJECT_CLASS_ARRAY_BUFFER: #if JERRY_BUILTIN_SHAREDARRAYBUFFER case ECMA_OBJECT_CLASS_SHARED_ARRAY_BUFFER: #endif /* JERRY_BUILTIN_SHAREDARRAYBUFFER */ { if (!(ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_HAS_POINTER)) { ext_object_size += ext_object_p->u.cls.u3.length; break; } ext_object_size = sizeof (ecma_arraybuffer_pointer_t); if (ECMA_ARRAYBUFFER_GET_FLAGS (ext_object_p) & ECMA_ARRAYBUFFER_ALLOCATED) { ecma_arraybuffer_release_buffer (object_p); } break; } #endif /* JERRY_BUILTIN_TYPEDARRAY */ #if JERRY_BUILTIN_WEAKREF case ECMA_OBJECT_CLASS_WEAKREF: { ecma_value_t target = ext_object_p->u.cls.u3.target; if (!ecma_is_value_undefined (target)) { ecma_op_object_unref_weak (ecma_get_object_from_value (target), ecma_make_object_value (object_p)); } break; } #endif /* JERRY_BUILTIN_WEAKREF */ #if JERRY_BUILTIN_CONTAINER case ECMA_OBJECT_CLASS_CONTAINER: { ecma_extended_object_t *map_object_p = (ecma_extended_object_t *) object_p; ecma_collection_t *container_p = ECMA_GET_INTERNAL_VALUE_POINTER (ecma_collection_t, map_object_p->u.cls.u3.value); ecma_op_container_free_entries (object_p); ecma_collection_destroy (container_p); break; } #endif /* JERRY_BUILTIN_CONTAINER */ #if JERRY_BUILTIN_DATAVIEW case ECMA_OBJECT_CLASS_DATAVIEW: { ext_object_size = sizeof (ecma_dataview_object_t); break; } #endif /* JERRY_BUILTIN_DATAVIEW */ case ECMA_OBJECT_CLASS_GENERATOR: case ECMA_OBJECT_CLASS_ASYNC_GENERATOR: { ext_object_size = ecma_gc_free_executable_object (object_p); break; } case ECMA_OBJECT_CLASS_PROMISE: { ecma_free_value_if_not_object (ext_object_p->u.cls.u3.value); /* Reactions only contains objects. */ ecma_collection_destroy (((ecma_promise_object_t *) object_p)->reactions); ext_object_size = sizeof (ecma_promise_object_t); break; } case ECMA_OBJECT_CLASS_PROMISE_CAPABILITY: { ext_object_size = sizeof (ecma_promise_capabality_t); break; } case ECMA_OBJECT_CLASS_ASYNC_FROM_SYNC_ITERATOR: { ext_object_size = sizeof (ecma_async_from_sync_iterator_object_t); break; } #if JERRY_MODULE_SYSTEM case ECMA_OBJECT_CLASS_MODULE: { ecma_module_release_module ((ecma_module_t *) ext_object_p); ext_object_size = sizeof (ecma_module_t); break; } #endif /* JERRY_MODULE_SYSTEM */ default: { /* The ECMA_OBJECT_CLASS__MAX type represents an uninitialized class. */ JERRY_ASSERT (ext_object_p->u.cls.type <= ECMA_OBJECT_CLASS__MAX); break; } } break; } case ECMA_OBJECT_TYPE_BUILT_IN_ARRAY: { ext_object_size = sizeof (ecma_extended_built_in_object_t); uint8_t bitset_size = ((ecma_extended_built_in_object_t *) object_p)->built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); /* FALLTHRU */ } case ECMA_OBJECT_TYPE_ARRAY: { if (ecma_op_array_is_fast_array ((ecma_extended_object_t *) object_p)) { ecma_free_fast_access_array (object_p); return; } break; } #if JERRY_BUILTIN_PROXY case ECMA_OBJECT_TYPE_PROXY: { ext_object_size = sizeof (ecma_proxy_object_t); break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_OBJECT_TYPE_FUNCTION: { /* Function with byte-code (not a built-in function). */ ecma_extended_object_t *ext_func_p = (ecma_extended_object_t *) object_p; #if JERRY_SNAPSHOT_EXEC if (ext_func_p->u.function.bytecode_cp != ECMA_NULL_POINTER) { #endif /* JERRY_SNAPSHOT_EXEC */ ecma_compiled_code_t *byte_code_p = (ECMA_GET_INTERNAL_VALUE_POINTER (ecma_compiled_code_t, ext_func_p->u.function.bytecode_cp)); if (CBC_FUNCTION_IS_ARROW (byte_code_p->status_flags)) { ecma_free_value_if_not_object (((ecma_arrow_function_t *) object_p)->this_binding); ecma_free_value_if_not_object (((ecma_arrow_function_t *) object_p)->new_target); ext_object_size = sizeof (ecma_arrow_function_t); } ecma_bytecode_deref (byte_code_p); #if JERRY_SNAPSHOT_EXEC } else { ext_object_size = sizeof (ecma_static_function_t); } #endif /* JERRY_SNAPSHOT_EXEC */ break; } case ECMA_OBJECT_TYPE_BUILT_IN_FUNCTION: { ecma_extended_object_t *extended_func_p = (ecma_extended_object_t *) object_p; if (!ecma_builtin_function_is_routine (object_p)) { uint8_t bitset_size = extended_func_p->u.built_in.u.length_and_bitset_size; ext_object_size += sizeof (uint64_t) * (bitset_size >> ECMA_BUILT_IN_BITSET_SHIFT); break; } if (extended_func_p->u.built_in.id != ECMA_BUILTIN_ID_HANDLER) { break; } switch (extended_func_p->u.built_in.routine_id) { case ECMA_NATIVE_HANDLER_PROMISE_RESOLVE: case ECMA_NATIVE_HANDLER_PROMISE_REJECT: { ext_object_size = sizeof (ecma_promise_resolver_t); break; } case ECMA_NATIVE_HANDLER_PROMISE_THEN_FINALLY: case ECMA_NATIVE_HANDLER_PROMISE_CATCH_FINALLY: { ext_object_size = sizeof (ecma_promise_finally_function_t); break; } case ECMA_NATIVE_HANDLER_PROMISE_CAPABILITY_EXECUTOR: { ext_object_size = sizeof (ecma_promise_capability_executor_t); break; } case ECMA_NATIVE_HANDLER_PROMISE_ALL_HELPER: { ext_object_size = sizeof (ecma_promise_all_executor_t); break; } #if JERRY_BUILTIN_PROXY case ECMA_NATIVE_HANDLER_PROXY_REVOKE: { ext_object_size = sizeof (ecma_revocable_proxy_object_t); break; } #endif /* JERRY_BUILTIN_PROXY */ case ECMA_NATIVE_HANDLER_VALUE_THUNK: case ECMA_NATIVE_HANDLER_VALUE_THROWER: { ecma_free_value_if_not_object (((ecma_promise_value_thunk_t *) object_p)->value); ext_object_size = sizeof (ecma_promise_value_thunk_t); break; } case ECMA_NATIVE_HANDLER_ASYNC_FROM_SYNC_ITERATOR_UNWRAP: { break; } default: { JERRY_UNREACHABLE (); } } break; } case ECMA_OBJECT_TYPE_BOUND_FUNCTION: { ext_object_size = sizeof (ecma_bound_function_t); ecma_bound_function_t *bound_func_p = (ecma_bound_function_t *) object_p; ecma_value_t args_len_or_this = bound_func_p->header.u.bound_function.args_len_or_this; ecma_free_value (bound_func_p->target_length); if (!ecma_is_value_integer_number (args_len_or_this)) { ecma_free_value_if_not_object (args_len_or_this); break; } ecma_integer_value_t args_length = ecma_get_integer_from_value (args_len_or_this); ecma_value_t *args_p = (ecma_value_t *) (bound_func_p + 1); for (ecma_integer_value_t i = 0; i < args_length; i++) { ecma_free_value_if_not_object (args_p[i]); } size_t args_size = ((size_t) args_length) * sizeof (ecma_value_t); ext_object_size += args_size; break; } case ECMA_OBJECT_TYPE_CONSTRUCTOR_FUNCTION: { ecma_script_deref (((ecma_extended_object_t *) object_p)->u.constructor_function.script_value); break; } case ECMA_OBJECT_TYPE_NATIVE_FUNCTION: { ext_object_size = sizeof (ecma_native_function_t); break; } default: { JERRY_UNREACHABLE (); } } ecma_gc_free_properties (object_p, ECMA_GC_FREE_NO_OPTIONS); ecma_dealloc_extended_object (object_p, ext_object_size); } /* ecma_gc_free_object */ /** * Run garbage collection, freeing objects that are no longer referenced. */ void ecma_gc_run (void) { #if (JERRY_GC_MARK_LIMIT != 0) JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) == JERRY_GC_MARK_LIMIT); #endif /* (JERRY_GC_MARK_LIMIT != 0) */ JERRY_CONTEXT (ecma_gc_new_objects) = 0; ecma_object_t black_list_head; black_list_head.gc_next_cp = JMEM_CP_NULL; ecma_object_t *black_end_p = &black_list_head; ecma_object_t white_gray_list_head; white_gray_list_head.gc_next_cp = JERRY_CONTEXT (ecma_gc_objects_cp); ecma_object_t *obj_prev_p = &white_gray_list_head; jmem_cpointer_t obj_iter_cp = obj_prev_p->gc_next_cp; ecma_object_t *obj_iter_p; /* Move root objects (i.e. they have global or stack references) to the black list. */ while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); const jmem_cpointer_t obj_next_cp = obj_iter_p->gc_next_cp; JERRY_ASSERT (obj_prev_p == NULL || ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_prev_p->gc_next_cp) == obj_iter_p); if (obj_iter_p->type_flags_refs >= ECMA_OBJECT_REF_ONE) { /* Moving the object to list of marked objects. */ obj_prev_p->gc_next_cp = obj_next_cp; black_end_p->gc_next_cp = obj_iter_cp; black_end_p = obj_iter_p; } else { obj_iter_p->type_flags_refs |= ECMA_OBJECT_NON_VISITED; obj_prev_p = obj_iter_p; } obj_iter_cp = obj_next_cp; } black_end_p->gc_next_cp = JMEM_CP_NULL; /* Mark root objects. */ obj_iter_cp = black_list_head.gc_next_cp; while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); ecma_gc_mark (obj_iter_p); obj_iter_cp = obj_iter_p->gc_next_cp; } /* Mark non-root objects. */ bool marked_anything_during_current_iteration; do { #if (JERRY_GC_MARK_LIMIT != 0) JERRY_ASSERT (JERRY_CONTEXT (ecma_gc_mark_recursion_limit) == JERRY_GC_MARK_LIMIT); #endif /* (JERRY_GC_MARK_LIMIT != 0) */ marked_anything_during_current_iteration = false; obj_prev_p = &white_gray_list_head; obj_iter_cp = obj_prev_p->gc_next_cp; while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); const jmem_cpointer_t obj_next_cp = obj_iter_p->gc_next_cp; JERRY_ASSERT (obj_prev_p == NULL || ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_prev_p->gc_next_cp) == obj_iter_p); if (ecma_gc_is_object_visited (obj_iter_p)) { /* Moving the object to list of marked objects */ obj_prev_p->gc_next_cp = obj_next_cp; black_end_p->gc_next_cp = obj_iter_cp; black_end_p = obj_iter_p; #if (JERRY_GC_MARK_LIMIT != 0) if (obj_iter_p->type_flags_refs >= ECMA_OBJECT_REF_ONE) { /* Set the reference count of non-marked gray object to 0 */ obj_iter_p->type_flags_refs &= (ecma_object_descriptor_t) (ECMA_OBJECT_REF_ONE - 1); ecma_gc_mark (obj_iter_p); marked_anything_during_current_iteration = true; } #else /* (JERRY_GC_MARK_LIMIT == 0) */ marked_anything_during_current_iteration = true; #endif /* (JERRY_GC_MARK_LIMIT != 0) */ } else { obj_prev_p = obj_iter_p; } obj_iter_cp = obj_next_cp; } } while (marked_anything_during_current_iteration); black_end_p->gc_next_cp = JMEM_CP_NULL; JERRY_CONTEXT (ecma_gc_objects_cp) = black_list_head.gc_next_cp; /* Sweep objects that are currently unmarked. */ obj_iter_cp = white_gray_list_head.gc_next_cp; while (obj_iter_cp != JMEM_CP_NULL) { obj_iter_p = JMEM_CP_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); const jmem_cpointer_t obj_next_cp = obj_iter_p->gc_next_cp; JERRY_ASSERT (!ecma_gc_is_object_visited (obj_iter_p)); ecma_gc_free_object (obj_iter_p); obj_iter_cp = obj_next_cp; } #if JERRY_BUILTIN_REGEXP /* Free RegExp bytecodes stored in cache */ re_cache_gc (); #endif /* JERRY_BUILTIN_REGEXP */ } /* ecma_gc_run */ /** * Try to free some memory (depending on memory pressure). * * When called with JMEM_PRESSURE_FULL, the engine will be terminated with JERRY_FATAL_OUT_OF_MEMORY. */ void ecma_free_unused_memory (jmem_pressure_t pressure) /**< current pressure */ { #if JERRY_DEBUGGER while ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) && JERRY_CONTEXT (debugger_byte_code_free_tail) != ECMA_NULL_POINTER) { /* Wait until all byte code is freed or the connection is aborted. */ jerry_debugger_receive (NULL); } #endif /* JERRY_DEBUGGER */ if (JERRY_LIKELY (pressure == JMEM_PRESSURE_LOW)) { #if JERRY_PROPERTY_HASHMAP if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) > ECMA_PROP_HASHMAP_ALLOC_ON) { --JERRY_CONTEXT (ecma_prop_hashmap_alloc_state); } JERRY_CONTEXT (status_flags) &= (uint32_t) ~ECMA_STATUS_HIGH_PRESSURE_GC; #endif /* JERRY_PROPERTY_HASHMAP */ /* * If there is enough newly allocated objects since last GC, probably it is worthwhile to start GC now. * Otherwise, probability to free sufficient space is considered to be low. */ size_t new_objects_fraction = CONFIG_ECMA_GC_NEW_OBJECTS_FRACTION; if (JERRY_CONTEXT (ecma_gc_new_objects) * new_objects_fraction > JERRY_CONTEXT (ecma_gc_objects_number)) { ecma_gc_run (); } return; } else if (pressure == JMEM_PRESSURE_HIGH) { /* Freeing as much memory as we currently can */ #if JERRY_PROPERTY_HASHMAP if (JERRY_CONTEXT (status_flags) & ECMA_STATUS_HIGH_PRESSURE_GC) { JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) = ECMA_PROP_HASHMAP_ALLOC_MAX; } else if (JERRY_CONTEXT (ecma_prop_hashmap_alloc_state) < ECMA_PROP_HASHMAP_ALLOC_MAX) { ++JERRY_CONTEXT (ecma_prop_hashmap_alloc_state); JERRY_CONTEXT (status_flags) |= ECMA_STATUS_HIGH_PRESSURE_GC; } #endif /* JERRY_PROPERTY_HASHMAP */ ecma_gc_run (); #if JERRY_PROPERTY_HASHMAP /* Free hashmaps of remaining objects. */ jmem_cpointer_t obj_iter_cp = JERRY_CONTEXT (ecma_gc_objects_cp); while (obj_iter_cp != JMEM_CP_NULL) { ecma_object_t *obj_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_object_t, obj_iter_cp); if (!ecma_is_lexical_environment (obj_iter_p) || ecma_get_lex_env_type (obj_iter_p) == ECMA_LEXICAL_ENVIRONMENT_DECLARATIVE) { if (!ecma_is_lexical_environment (obj_iter_p) && ecma_op_object_is_fast_array (obj_iter_p)) { obj_iter_cp = obj_iter_p->gc_next_cp; continue; } jmem_cpointer_t prop_iter_cp = obj_iter_p->u1.property_list_cp; if (prop_iter_cp != JMEM_CP_NULL) { ecma_property_header_t *prop_iter_p = ECMA_GET_NON_NULL_POINTER (ecma_property_header_t, prop_iter_cp); if (prop_iter_p->types[0] == ECMA_PROPERTY_TYPE_HASHMAP) { ecma_property_hashmap_free (obj_iter_p); } } } obj_iter_cp = obj_iter_p->gc_next_cp; } #endif /* JERRY_PROPERTY_HASHMAP */ jmem_pools_collect_empty (); return; } else if (JERRY_UNLIKELY (pressure == JMEM_PRESSURE_FULL)) { jerry_fatal (JERRY_FATAL_OUT_OF_MEMORY); } else { JERRY_ASSERT (pressure == JMEM_PRESSURE_NONE); JERRY_UNREACHABLE (); } } /* ecma_free_unused_memory */ /** * @} * @} */