/** * Send memory statistics to the debugger client. */ void jerry_debugger_send_memstats (void) { JERRY_ASSERT (JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED); JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_memstats_t, memstats_p); memstats_p->type = JERRY_DEBUGGER_MEMSTATS_RECEIVE; #ifdef JMEM_STATS /* if jmem_stats are defined */ jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); uint32_t allocated_bytes = (uint32_t) heap_stats->allocated_bytes; memcpy (memstats_p->allocated_bytes, &allocated_bytes, sizeof (uint32_t)); uint32_t byte_code_bytes = (uint32_t) heap_stats->byte_code_bytes; memcpy (memstats_p->byte_code_bytes, &byte_code_bytes, sizeof (uint32_t)); uint32_t string_bytes = (uint32_t) heap_stats->string_bytes; memcpy (memstats_p->string_bytes, &string_bytes, sizeof (uint32_t)); uint32_t object_bytes = (uint32_t) heap_stats->object_bytes; memcpy (memstats_p->object_bytes, &object_bytes, sizeof (uint32_t)); uint32_t property_bytes = (uint32_t) heap_stats->property_bytes; memcpy (memstats_p->property_bytes, &property_bytes, sizeof (uint32_t)); #else /* if not, just put zeros */ memset (memstats_p->allocated_bytes, 0, sizeof (uint32_t)); memset (memstats_p->byte_code_bytes, 0, sizeof (uint32_t)); memset (memstats_p->string_bytes, 0, sizeof (uint32_t)); memset (memstats_p->object_bytes, 0, sizeof (uint32_t)); memset (memstats_p->property_bytes, 0, sizeof (uint32_t)); #endif jerry_debugger_send (sizeof (jerry_debugger_send_memstats_t)); } /* jerry_debugger_send_memstats */
/** * Check whether currently executed code is strict mode code * * @return true - current code is executed in strict mode, * false - otherwise */ bool vm_is_strict_mode (void) { JERRY_ASSERT (JERRY_CONTEXT (vm_top_context_p) != NULL); return JERRY_CONTEXT (vm_top_context_p)->bytecode_header_p->status_flags & CBC_CODE_FLAGS_STRICT_MODE; } /* vm_is_strict_mode */
/** * Collect empty pool chunks */ void jmem_pools_collect_empty () { jmem_pools_chunk_t *chunk_p = JERRY_CONTEXT (jmem_free_8_byte_chunk_p); JERRY_CONTEXT (jmem_free_8_byte_chunk_p) = NULL; while (chunk_p) { VALGRIND_DEFINED_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); jmem_pools_chunk_t *const next_p = chunk_p->next_p; VALGRIND_NOACCESS_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); jmem_heap_free_block (chunk_p, 8); JMEM_POOLS_STAT_DEALLOC (); chunk_p = next_p; } #ifdef JERRY_CPOINTER_32_BIT chunk_p = JERRY_CONTEXT (jmem_free_16_byte_chunk_p); JERRY_CONTEXT (jmem_free_16_byte_chunk_p) = NULL; while (chunk_p) { VALGRIND_DEFINED_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); jmem_pools_chunk_t *const next_p = chunk_p->next_p; VALGRIND_NOACCESS_SPACE (chunk_p, sizeof (jmem_pools_chunk_t)); jmem_heap_free_block (chunk_p, 16); JMEM_POOLS_STAT_DEALLOC (); chunk_p = next_p; } #endif /* JERRY_CPOINTER_32_BIT */ } /* jmem_pools_collect_empty */
/** * Free the chunk */ inline void __attr_hot___ __attr_always_inline___ jmem_pools_free (void *chunk_p, /**< pointer to the chunk */ size_t size) /**< size of the chunk */ { JERRY_ASSERT (chunk_p != NULL); jmem_pools_chunk_t *const chunk_to_free_p = (jmem_pools_chunk_t *) chunk_p; VALGRIND_DEFINED_SPACE (chunk_to_free_p, size); if (size <= 8) { chunk_to_free_p->next_p = JERRY_CONTEXT (jmem_free_8_byte_chunk_p); JERRY_CONTEXT (jmem_free_8_byte_chunk_p) = chunk_to_free_p; } else { #ifdef JERRY_CPOINTER_32_BIT JERRY_ASSERT (size <= 16); chunk_to_free_p->next_p = JERRY_CONTEXT (jmem_free_16_byte_chunk_p); JERRY_CONTEXT (jmem_free_16_byte_chunk_p) = chunk_to_free_p; #else /* !JERRY_CPOINTER_32_BIT */ JERRY_UNREACHABLE (); #endif /* JERRY_CPOINTER_32_BIT */ } VALGRIND_NOACCESS_SPACE (chunk_to_free_p, size); JMEM_POOLS_STAT_FREE_POOL (); } /* jmem_pools_free */
/** * Allocate a chunk of specified size * * @return pointer to allocated chunk, if allocation was successful, * or NULL - if not enough memory. */ inline void * __attribute__((hot)) __attr_always_inline___ jmem_pools_alloc (void) { #ifdef JMEM_GC_BEFORE_EACH_ALLOC jmem_run_free_unused_memory_callbacks (JMEM_FREE_UNUSED_MEMORY_SEVERITY_HIGH); #endif /* JMEM_GC_BEFORE_EACH_ALLOC */ if (JERRY_CONTEXT (jmem_free_chunk_p) != NULL) { const jmem_pools_chunk_t *const chunk_p = JERRY_CONTEXT (jmem_free_chunk_p); JMEM_POOLS_STAT_REUSE (); VALGRIND_DEFINED_SPACE (chunk_p, JMEM_POOL_CHUNK_SIZE); JERRY_CONTEXT (jmem_free_chunk_p) = chunk_p->next_p; VALGRIND_UNDEFINED_SPACE (chunk_p, JMEM_POOL_CHUNK_SIZE); return (void *) chunk_p; } else { JMEM_POOLS_STAT_NEW_ALLOC (); return (void *) jmem_heap_alloc_block (JMEM_POOL_CHUNK_SIZE); } } /* jmem_pools_alloc */
/** * Startup initialization of heap */ void jmem_heap_init (void) { #ifndef JERRY_CPOINTER_32_BIT JERRY_STATIC_ASSERT (((UINT16_MAX + 1) << JMEM_ALIGNMENT_LOG) >= JMEM_HEAP_SIZE, maximum_heap_size_for_16_bit_compressed_pointers_is_512K); #endif /* !JERRY_CPOINTER_32_BIT */ JERRY_ASSERT ((uintptr_t) JERRY_HEAP_CONTEXT (area) % JMEM_ALIGNMENT == 0); JERRY_CONTEXT (jmem_heap_limit) = CONFIG_MEM_HEAP_DESIRED_LIMIT; jmem_heap_free_t *const region_p = (jmem_heap_free_t *) JERRY_HEAP_CONTEXT (area); region_p->size = JMEM_HEAP_AREA_SIZE; region_p->next_offset = JMEM_HEAP_END_OF_LIST; JERRY_HEAP_CONTEXT (first).size = 0; JERRY_HEAP_CONTEXT (first).next_offset = JMEM_HEAP_GET_OFFSET_FROM_ADDR (region_p); JERRY_CONTEXT (jmem_heap_list_skip_p) = &JERRY_HEAP_CONTEXT (first); VALGRIND_NOACCESS_SPACE (JERRY_HEAP_CONTEXT (area), JMEM_HEAP_AREA_SIZE); JMEM_HEAP_STAT_INIT (); } /* jmem_heap_init */
/** * Unregister specified 'try to give memory back' callback routine */ void jmem_unregister_free_unused_memory_callback (jmem_free_unused_memory_callback_t callback) /**< callback routine */ { /* Currently only one callback is supported */ JERRY_ASSERT (JERRY_CONTEXT (jmem_free_unused_memory_callback) == callback); JERRY_CONTEXT (jmem_free_unused_memory_callback) = NULL; } /* jmem_unregister_free_unused_memory_callback */
/** * Finalize pool manager */ void jmem_pools_finalize (void) { jmem_pools_collect_empty (); JERRY_ASSERT (JERRY_CONTEXT (jmem_free_8_byte_chunk_p) == NULL); #ifdef JERRY_CPOINTER_32_BIT JERRY_ASSERT (JERRY_CONTEXT (jmem_free_16_byte_chunk_p) == NULL); #endif /* JERRY_CPOINTER_32_BIT */ } /* jmem_pools_finalize */
/** * Run 'try to give memory back' callbacks with specified severity */ void jmem_run_free_unused_memory_callbacks (jmem_free_unused_memory_severity_t severity) /**< severity of the request */ { if (JERRY_CONTEXT (jmem_free_unused_memory_callback) != NULL) { JERRY_CONTEXT (jmem_free_unused_memory_callback) (severity); } jmem_pools_collect_empty (); } /* jmem_run_free_unused_memory_callbacks */
/** * Continue execution. */ void jerry_debugger_continue (void) { #ifdef JERRY_DEBUGGER if ((JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED) && !(JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_BREAKPOINT_MODE)) { JERRY_DEBUGGER_CLEAR_FLAGS (JERRY_DEBUGGER_VM_STOP); JERRY_CONTEXT (debugger_stop_context) = NULL; } #endif /* JERRY_DEBUGGER */ } /* jerry_debugger_continue */
/** * Allocation of memory block, running 'try to give memory back' callbacks, if there is not enough memory. * * Note: * if there is still not enough memory after running the callbacks * - NULL value will be returned if parmeter 'ret_null_on_error' is true * - the engine will terminate with ERR_OUT_OF_MEMORY if 'ret_null_on_error' is false * * @return NULL, if the required memory size is 0 * also NULL, if 'ret_null_on_error' is true and the allocation fails because of there is not enough memory */ static void * jmem_heap_gc_and_alloc_block (const size_t size, /**< required memory size */ bool ret_null_on_error) /**< indicates whether return null or terminate with ERR_OUT_OF_MEMORY on out of memory */ { if (unlikely (size == 0)) { return NULL; } VALGRIND_FREYA_CHECK_MEMPOOL_REQUEST; #ifdef JMEM_GC_BEFORE_EACH_ALLOC jmem_run_free_unused_memory_callbacks (JMEM_FREE_UNUSED_MEMORY_SEVERITY_HIGH); #endif /* JMEM_GC_BEFORE_EACH_ALLOC */ if (JERRY_CONTEXT (jmem_heap_allocated_size) + size >= JERRY_CONTEXT (jmem_heap_limit)) { jmem_run_free_unused_memory_callbacks (JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW); } void *data_space_p = jmem_heap_alloc_block_internal (size); if (likely (data_space_p != NULL)) { VALGRIND_FREYA_MALLOCLIKE_SPACE (data_space_p, size); return data_space_p; } for (jmem_free_unused_memory_severity_t severity = JMEM_FREE_UNUSED_MEMORY_SEVERITY_LOW; severity <= JMEM_FREE_UNUSED_MEMORY_SEVERITY_HIGH; severity = (jmem_free_unused_memory_severity_t) (severity + 1)) { jmem_run_free_unused_memory_callbacks (severity); data_space_p = jmem_heap_alloc_block_internal (size); if (likely (data_space_p != NULL)) { VALGRIND_FREYA_MALLOCLIKE_SPACE (data_space_p, size); return data_space_p; } } JERRY_ASSERT (data_space_p == NULL); if (!ret_null_on_error) { jerry_fatal (ERR_OUT_OF_MEMORY); } return data_space_p; } /* jmem_heap_gc_and_alloc_block */
jmem_pools_free (void *chunk_p) /**< pointer to the chunk */ { jmem_pools_chunk_t *const chunk_to_free_p = (jmem_pools_chunk_t *) chunk_p; VALGRIND_DEFINED_SPACE (chunk_to_free_p, JMEM_POOL_CHUNK_SIZE); chunk_to_free_p->next_p = JERRY_CONTEXT (jmem_free_chunk_p); JERRY_CONTEXT (jmem_free_chunk_p) = chunk_to_free_p; VALGRIND_NOACCESS_SPACE (chunk_to_free_p, JMEM_POOL_CHUNK_SIZE); JMEM_POOLS_STAT_FREE_POOL (); } /* jmem_pools_free */
/** * Finalize ECMA built-in objects */ void ecma_finalize_builtins (void) { for (ecma_builtin_id_t id = (ecma_builtin_id_t) 0; id < ECMA_BUILTIN_ID__COUNT; id = (ecma_builtin_id_t) (id + 1)) { if (JERRY_CONTEXT (ecma_builtin_objects)[id] != NULL) { ecma_deref_object (JERRY_CONTEXT (ecma_builtin_objects)[id]); JERRY_CONTEXT (ecma_builtin_objects)[id] = NULL; } } } /* ecma_finalize_builtins */
/** * Get reference to specified built-in object * * @return pointer to the object's instance */ ecma_object_t * ecma_builtin_get (ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); if (unlikely (JERRY_CONTEXT (ecma_builtin_objects)[builtin_id] == NULL)) { ecma_instantiate_builtin (builtin_id); } ecma_ref_object (JERRY_CONTEXT (ecma_builtin_objects)[builtin_id]); return JERRY_CONTEXT (ecma_builtin_objects)[builtin_id]; } /* ecma_builtin_get */
/** * Send backtrace. */ static void jerry_debugger_send_backtrace (uint8_t *recv_buffer_p) /**< pointer to the received data */ { JERRY_DEBUGGER_RECEIVE_BUFFER_AS (jerry_debugger_receive_get_backtrace_t, get_backtrace_p); uint32_t max_depth; memcpy (&max_depth, get_backtrace_p->max_depth, sizeof (uint32_t)); if (max_depth == 0) { max_depth = UINT32_MAX; } JERRY_DEBUGGER_SEND_BUFFER_AS (jerry_debugger_send_backtrace_t, backtrace_p); backtrace_p->type = JERRY_DEBUGGER_BACKTRACE; vm_frame_ctx_t *frame_ctx_p = JERRY_CONTEXT (vm_top_context_p); size_t current_frame = 0; const size_t max_frame_count = JERRY_DEBUGGER_SEND_MAX (jerry_debugger_frame_t); const size_t max_message_size = JERRY_DEBUGGER_SEND_SIZE (max_frame_count, jerry_debugger_frame_t); while (frame_ctx_p != NULL && max_depth > 0) { if (frame_ctx_p->bytecode_header_p->status_flags & CBC_CODE_FLAGS_DEBUGGER_IGNORE) { frame_ctx_p = frame_ctx_p->prev_context_p; continue; } if (current_frame >= max_frame_count) { if (!jerry_debugger_send (max_message_size)) { return; } current_frame = 0; } jerry_debugger_frame_t *frame_p = backtrace_p->frames + current_frame; jmem_cpointer_t byte_code_cp; JMEM_CP_SET_NON_NULL_POINTER (byte_code_cp, frame_ctx_p->bytecode_header_p); memcpy (frame_p->byte_code_cp, &byte_code_cp, sizeof (jmem_cpointer_t)); uint32_t offset = (uint32_t) (frame_ctx_p->byte_code_p - (uint8_t *) frame_ctx_p->bytecode_header_p); memcpy (frame_p->offset, &offset, sizeof (uint32_t)); frame_ctx_p = frame_ctx_p->prev_context_p; current_frame++; max_depth--; } size_t message_size = current_frame * sizeof (jerry_debugger_frame_t); backtrace_p->type = JERRY_DEBUGGER_BACKTRACE_END; jerry_debugger_send (sizeof (jerry_debugger_send_type_t) + message_size); } /* jerry_debugger_send_backtrace */
/** * Account allocation */ static void jmem_heap_stat_alloc (size_t size) /**< Size of allocated block */ { const size_t aligned_size = (size + JMEM_ALIGNMENT - 1) / JMEM_ALIGNMENT * JMEM_ALIGNMENT; const size_t waste_bytes = aligned_size - size; jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); heap_stats->allocated_bytes += aligned_size; heap_stats->waste_bytes += waste_bytes; heap_stats->alloc_count++; if (heap_stats->allocated_bytes > heap_stats->peak_allocated_bytes) { heap_stats->peak_allocated_bytes = heap_stats->allocated_bytes; } if (heap_stats->allocated_bytes > heap_stats->global_peak_allocated_bytes) { heap_stats->global_peak_allocated_bytes = heap_stats->allocated_bytes; } if (heap_stats->waste_bytes > heap_stats->peak_waste_bytes) { heap_stats->peak_waste_bytes = heap_stats->waste_bytes; } if (heap_stats->waste_bytes > heap_stats->global_peak_waste_bytes) { heap_stats->global_peak_waste_bytes = heap_stats->waste_bytes; } } /* jmem_heap_stat_alloc */
/** * Print heap memory usage statistics */ void jmem_heap_stats_print (void) { jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); JERRY_DEBUG_MSG ("Heap stats:\n" " Heap size = %zu bytes\n" " Allocated = %zu bytes\n" " Waste = %zu bytes\n" " Peak allocated = %zu bytes\n" " Peak waste = %zu bytes\n" " Skip-ahead ratio = %zu.%04zu\n" " Average alloc iteration = %zu.%04zu\n" " Average free iteration = %zu.%04zu\n" "\n", heap_stats->size, heap_stats->allocated_bytes, heap_stats->waste_bytes, heap_stats->peak_allocated_bytes, heap_stats->peak_waste_bytes, heap_stats->skip_count / heap_stats->nonskip_count, heap_stats->skip_count % heap_stats->nonskip_count * 10000 / heap_stats->nonskip_count, heap_stats->alloc_iter_count / heap_stats->alloc_count, heap_stats->alloc_iter_count % heap_stats->alloc_count * 10000 / heap_stats->alloc_count, heap_stats->free_iter_count / heap_stats->free_count, heap_stats->free_iter_count % heap_stats->free_count * 10000 / heap_stats->free_count); } /* jmem_heap_stats_print */
/** * Reject the promise if the value is error. * * See also: * ES2015 25.4.1.1.1 * * @return ecma value of the new promise. * Returned value must be freed with ecma_free_value. */ inline static ecma_value_t ecma_builtin_promise_reject_abrupt (ecma_value_t capability) /**< reject description */ { ecma_value_t reason = JERRY_CONTEXT (error_value); ecma_string_t *str_reject = ecma_new_ecma_string_from_uint32 (ECMA_PROMISE_PROPERTY_REJECT); ecma_value_t reject = ecma_op_object_get (ecma_get_object_from_value (capability), str_reject); ecma_deref_ecma_string (str_reject); ecma_value_t call_ret = ecma_op_function_call (ecma_get_object_from_value (reject), ecma_make_simple_value (ECMA_SIMPLE_VALUE_UNDEFINED), &reason, 1); ecma_free_value (reject); if (ECMA_IS_VALUE_ERROR (call_ret)) { return call_ret; } ecma_free_value (call_ret); ecma_string_t *str_promise = ecma_new_ecma_string_from_uint32 (ECMA_PROMISE_PROPERTY_PROMISE); ecma_value_t promise_new = ecma_op_object_get (ecma_get_object_from_value (capability), str_promise); ecma_deref_ecma_string (str_promise); return promise_new; } /* ecma_builtin_promise_reject_abrupt */
/** * Get pools memory usage statistics */ void jmem_pools_get_stats (jmem_pools_stats_t *out_pools_stats_p) /**< [out] pools' stats */ { JERRY_ASSERT (out_pools_stats_p != NULL); *out_pools_stats_p = JERRY_CONTEXT (jmem_pools_stats); } /* jmem_pools_get_stats */
/** * Finalize pool manager */ void jmem_pools_finalize (void) { jmem_pools_collect_empty (); JERRY_ASSERT (JERRY_CONTEXT (jmem_free_chunk_p) == NULL); } /* jmem_pools_finalize */
/** * Get heap memory usage statistics */ void jmem_heap_get_stats (jmem_heap_stats_t *out_heap_stats_p) /**< [out] heap stats */ { JERRY_ASSERT (out_heap_stats_p != NULL); *out_heap_stats_p = JERRY_CONTEXT (jmem_heap_stats); } /* jmem_heap_get_stats */
/** * Register byte code free. */ void jmem_stats_free_byte_code_bytes (size_t byte_code_size) { jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); JERRY_ASSERT (heap_stats->byte_code_bytes >= byte_code_size); heap_stats->byte_code_bytes -= byte_code_size; } /* jmem_stats_free_byte_code_bytes */
/** * Register string free. */ void jmem_stats_free_string_bytes (size_t string_size) { jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); JERRY_ASSERT (heap_stats->string_bytes >= string_size); heap_stats->string_bytes -= string_size; } /* jmem_stats_free_string_bytes */
/** * Check if passed object is the instance of specified built-in. */ bool ecma_builtin_is (ecma_object_t *obj_p, /**< pointer to an object */ ecma_builtin_id_t builtin_id) /**< id of built-in to check on */ { JERRY_ASSERT (obj_p != NULL && !ecma_is_lexical_environment (obj_p)); JERRY_ASSERT (builtin_id < ECMA_BUILTIN_ID__COUNT); if (JERRY_CONTEXT (ecma_builtin_objects)[builtin_id] == NULL) { /* If a built-in object is not instantiated, * the specified object cannot be the built-in object */ return false; } else { return (obj_p == JERRY_CONTEXT (ecma_builtin_objects)[builtin_id]); } } /* ecma_builtin_is */
/** * Initialize Global environment */ void ecma_init_global_lex_env (void) { ecma_object_t *glob_obj_p = ecma_builtin_get (ECMA_BUILTIN_ID_GLOBAL); JERRY_CONTEXT (ecma_global_lex_env_p) = ecma_create_object_lex_env (NULL, glob_obj_p, ECMA_LEXICAL_ENVIRONMENT_THIS_OBJECT_BOUND); } /* ecma_init_global_lex_env */
/** * Register property free. */ void jmem_stats_free_property_bytes (size_t property_size) { jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); JERRY_ASSERT (heap_stats->property_bytes >= property_size); heap_stats->property_bytes -= property_size; } /* jmem_stats_free_property_bytes */
/** * Register object free. */ void jmem_stats_free_object_bytes (size_t object_size) { jmem_heap_stats_t *heap_stats = &JERRY_CONTEXT (jmem_heap_stats); JERRY_ASSERT (heap_stats->object_bytes >= object_size); heap_stats->object_bytes -= object_size; } /* jmem_stats_free_object_bytes */
/** * Get visited flag of the object. */ static inline bool ecma_gc_is_object_visited (ecma_object_t *object_p) /**< object */ { JERRY_ASSERT (object_p != NULL); bool flag_value = (object_p->type_flags_refs & ECMA_OBJECT_FLAG_GC_VISITED) != 0; return flag_value != JERRY_CONTEXT (ecma_gc_visited_flip_flag); } /* ecma_gc_is_object_visited */
/** * Checks whether the debugger is connected. * * @return true - if the debugger is connected * false - otherwise */ bool jerry_debugger_is_connected (void) { #ifdef JERRY_DEBUGGER return JERRY_CONTEXT (debugger_flags) & JERRY_DEBUGGER_CONNECTED; #else return false; #endif /* JERRY_DEBUGGER */ } /* jerry_debugger_is_connected */
/** * Debugger server initialization. Must be called after jerry_init. */ void jerry_debugger_init (uint16_t port) /**< server port number */ { #ifdef JERRY_DEBUGGER JERRY_CONTEXT (debugger_port) = port; jerry_debugger_accept_connection (); #else /* !JERRY_DEBUGGER */ JERRY_UNUSED (port); #endif /* JERRY_DEBUGGER */ } /* jerry_debugger_init */