/* * Heap structure. * * Heap contains allocated heap objects, interned strings, and built-in * strings for one or more threads. */ #if !defined(DUK_HEAP_H_INCLUDED) #define DUK_HEAP_H_INCLUDED /* alloc function typedefs in duktape.h */ /* * Heap flags */ #define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED (1U << 0) /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */ #define DUK_HEAP_FLAG_INTERRUPT_RUNNING (1U << 1) /* executor interrupt running (used to avoid nested interrupts) */ #define DUK_HEAP_FLAG_FINALIZER_NORESCUE (1U << 2) /* heap destruction ongoing, finalizer rescue no longer possible */ #define DUK_HEAP_FLAG_DEBUGGER_PAUSED (1U << 3) /* debugger is paused: talk with debug client until step/resume */ #define DUK__HEAP_HAS_FLAGS(heap,bits) ((heap)->flags & (bits)) #define DUK__HEAP_SET_FLAGS(heap,bits) do { \ (heap)->flags |= (bits); \ } while (0) #define DUK__HEAP_CLEAR_FLAGS(heap,bits) do { \ (heap)->flags &= ~(bits); \ } while (0) #define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) #define DUK_HEAP_HAS_INTERRUPT_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) #define DUK_HEAP_HAS_FINALIZER_NORESCUE(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) #define DUK_HEAP_HAS_DEBUGGER_PAUSED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) #define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) #define DUK_HEAP_SET_INTERRUPT_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) #define DUK_HEAP_SET_FINALIZER_NORESCUE(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) #define DUK_HEAP_SET_DEBUGGER_PAUSED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) #define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED) #define DUK_HEAP_CLEAR_INTERRUPT_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING) #define DUK_HEAP_CLEAR_FINALIZER_NORESCUE(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE) #define DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_DEBUGGER_PAUSED) /* * Longjmp types, also double as identifying continuation type for a rethrow (in 'finally') */ #define DUK_LJ_TYPE_UNKNOWN 0 /* unused */ #define DUK_LJ_TYPE_THROW 1 /* value1 -> error object */ #define DUK_LJ_TYPE_YIELD 2 /* value1 -> yield value, iserror -> error / normal */ #define DUK_LJ_TYPE_RESUME 3 /* value1 -> resume value, value2 -> resumee thread, iserror -> error/normal */ #define DUK_LJ_TYPE_BREAK 4 /* value1 -> label number, pseudo-type to indicate a break continuation (for ENDFIN) */ #define DUK_LJ_TYPE_CONTINUE 5 /* value1 -> label number, pseudo-type to indicate a continue continuation (for ENDFIN) */ #define DUK_LJ_TYPE_RETURN 6 /* value1 -> return value, pseudo-type to indicate a return continuation (for ENDFIN) */ #define DUK_LJ_TYPE_NORMAL 7 /* no value, pseudo-type to indicate a normal continuation (for ENDFIN) */ /* * Mark-and-sweep flags * * These are separate from heap level flags now but could be merged. * The heap structure only contains a 'base mark-and-sweep flags' * field and the GC caller can impose further flags. */ /* Emergency mark-and-sweep: try extra hard, even at the cost of * performance. */ #define DUK_MS_FLAG_EMERGENCY (1U << 0) /* Postpone rescue decisions for reachable objects with FINALIZED set. * Used during finalize_list processing to avoid incorrect rescue * decisions due to finalize_list being a reachability root. */ #define DUK_MS_FLAG_POSTPONE_RESCUE (1U << 1) /* Don't compact objects; needed during object property table resize * to prevent a recursive resize. It would suffice to protect only the * current object being resized, but this is not yet implemented. */ #define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1U << 2) /* * Thread switching * * To switch heap->curr_thread, use the macro below so that interrupt counters * get updated correctly. The macro allows a NULL target thread because that * happens e.g. in call handling. */ #if defined(DUK_USE_INTERRUPT_COUNTER) #define DUK_HEAP_SWITCH_THREAD(heap,newthr) duk_heap_switch_thread((heap), (newthr)) #else #define DUK_HEAP_SWITCH_THREAD(heap,newthr) do { \ (heap)->curr_thread = (newthr); \ } while (0) #endif /* * Stats */ #if defined(DUK_USE_DEBUG) #define DUK_STATS_INC(heap,fieldname) do { \ (heap)->fieldname += 1; \ } while (0) #else #define DUK_STATS_INC(heap,fieldname) do {} while (0) #endif /* * Other heap related defines */ /* Mark-and-sweep interval is relative to combined count of objects and * strings kept in the heap during the latest mark-and-sweep pass. * Fixed point .8 multiplier and .0 adder. Trigger count (interval) is * decreased by each (re)allocation attempt (regardless of size), and each * refzero processed object. * * 'SKIP' indicates how many (re)allocations to wait until a retry if * GC is skipped because there is no thread do it with yet (happens * only during init phases). */ #if defined(DUK_USE_REFERENCE_COUNTING) #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT 12800L /* 50x heap size */ #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD 1024L #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP 256L #else #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT 256L /* 1x heap size */ #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD 1024L #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP 256L #endif /* GC torture. */ #if defined(DUK_USE_GC_TORTURE) #define DUK_GC_TORTURE(heap) do { duk_heap_mark_and_sweep((heap), 0); } while (0) #else #define DUK_GC_TORTURE(heap) do { } while (0) #endif /* Stringcache is used for speeding up char-offset-to-byte-offset * translations for non-ASCII strings. */ #define DUK_HEAP_STRCACHE_SIZE 4 #define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT 16 /* strings up to the this length are not cached */ /* Some list management macros. */ #define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap), (hdr)) #if defined(DUK_USE_REFERENCE_COUNTING) #define DUK_HEAP_REMOVE_FROM_HEAP_ALLOCATED(heap,hdr) duk_heap_remove_from_heap_allocated((heap), (hdr)) #endif #if defined(DUK_USE_FINALIZER_SUPPORT) #define DUK_HEAP_INSERT_INTO_FINALIZE_LIST(heap,hdr) duk_heap_insert_into_finalize_list((heap), (hdr)) #define DUK_HEAP_REMOVE_FROM_FINALIZE_LIST(heap,hdr) duk_heap_remove_from_finalize_list((heap), (hdr)) #endif /* * Built-in strings */ /* heap string indices are autogenerated in duk_strings.h */ #if defined(DUK_USE_ROM_STRINGS) #define DUK_HEAP_GET_STRING(heap,idx) \ ((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)])) #else /* DUK_USE_ROM_STRINGS */ #if defined(DUK_USE_HEAPPTR16) #define DUK_HEAP_GET_STRING(heap,idx) \ ((duk_hstring *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (heap)->strs16[(idx)])) #else #define DUK_HEAP_GET_STRING(heap,idx) \ ((heap)->strs[(idx)]) #endif #endif /* DUK_USE_ROM_STRINGS */ /* * Raw memory calls: relative to heap, but no GC interaction */ #define DUK_ALLOC_RAW(heap,size) \ ((heap)->alloc_func((heap)->heap_udata, (size))) #define DUK_REALLOC_RAW(heap,ptr,newsize) \ ((heap)->realloc_func((heap)->heap_udata, (void *) (ptr), (newsize))) #define DUK_FREE_RAW(heap,ptr) \ ((heap)->free_func((heap)->heap_udata, (void *) (ptr))) /* * Memory calls: relative to heap, GC interaction, but no error throwing. * * XXX: Currently a mark-and-sweep triggered by memory allocation will run * using the heap->heap_thread. This thread is also used for running * mark-and-sweep finalization; this is not ideal because it breaks the * isolation between multiple global environments. * * Notes: * * - DUK_FREE() is required to ignore NULL and any other possible return * value of a zero-sized alloc/realloc (same as ANSI C free()). * * - There is no DUK_REALLOC_ZEROED because we don't assume to know the * old size. Caller must zero the reallocated memory. * * - DUK_REALLOC_INDIRECT() must be used when a mark-and-sweep triggered * by an allocation failure might invalidate the original 'ptr', thus * causing a realloc retry to use an invalid pointer. Example: we're * reallocating the value stack and a finalizer resizes the same value * stack during mark-and-sweep. The indirect variant requests for the * current location of the pointer being reallocated using a callback * right before every realloc attempt; this circuitous approach is used * to avoid strict aliasing issues in a more straightforward indirect * pointer (void **) approach. Note: the pointer in the storage * location is read but is NOT updated; the caller must do that. */ /* callback for indirect reallocs, request for current pointer */ typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud); #define DUK_ALLOC(heap,size) duk_heap_mem_alloc((heap), (size)) #define DUK_ALLOC_ZEROED(heap,size) duk_heap_mem_alloc_zeroed((heap), (size)) #define DUK_REALLOC(heap,ptr,newsize) duk_heap_mem_realloc((heap), (ptr), (newsize)) #define DUK_REALLOC_INDIRECT(heap,cb,ud,newsize) duk_heap_mem_realloc_indirect((heap), (cb), (ud), (newsize)) #define DUK_FREE(heap,ptr) duk_heap_mem_free((heap), (ptr)) /* * Checked allocation, relative to a thread * * DUK_FREE_CHECKED() doesn't actually throw, but accepts a 'thr' argument * for convenience. */ #define DUK_ALLOC_CHECKED(thr,size) duk_heap_mem_alloc_checked((thr), (size)) #define DUK_ALLOC_CHECKED_ZEROED(thr,size) duk_heap_mem_alloc_checked_zeroed((thr), (size)) #define DUK_FREE_CHECKED(thr,ptr) duk_heap_mem_free((thr)->heap, (ptr)) /* * Memory constants */ #define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT 10 /* Retry allocation after mark-and-sweep for this * many times. A single mark-and-sweep round is * not guaranteed to free all unreferenced memory * because of finalization (in fact, ANY number of * rounds is strictly not enough). */ #define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT 3 /* Starting from this round, use emergency mode * for mark-and-sweep. */ /* * Debugger support */ /* Maximum number of breakpoints. Only breakpoints that are set are * consulted so increasing this has no performance impact. */ #define DUK_HEAP_MAX_BREAKPOINTS 16 /* Opcode interval for a Date-based status/peek rate limit check. Only * relevant when debugger is attached. Requesting a timestamp may be a * slow operation on some platforms so this shouldn't be too low. On the * other hand a high value makes Duktape react to a pause request slowly. */ #define DUK_HEAP_DBG_RATELIMIT_OPCODES 4000 /* Milliseconds between status notify and transport peeks. */ #define DUK_HEAP_DBG_RATELIMIT_MILLISECS 200 /* Debugger pause flags. */ #define DUK_PAUSE_FLAG_ONE_OPCODE (1U << 0) /* pause when a single opcode has been executed */ #define DUK_PAUSE_FLAG_ONE_OPCODE_ACTIVE (1U << 1) /* one opcode pause actually active; artifact of current implementation */ #define DUK_PAUSE_FLAG_LINE_CHANGE (1U << 2) /* pause when current line number changes */ #define DUK_PAUSE_FLAG_FUNC_ENTRY (1U << 3) /* pause when entering a function */ #define DUK_PAUSE_FLAG_FUNC_EXIT (1U << 4) /* pause when exiting current function */ #define DUK_PAUSE_FLAG_CAUGHT_ERROR (1U << 5) /* pause when about to throw an error that is caught */ #define DUK_PAUSE_FLAG_UNCAUGHT_ERROR (1U << 6) /* pause when about to throw an error that won't be caught */ struct duk_breakpoint { duk_hstring *filename; duk_uint32_t line; }; /* * String cache should ideally be at duk_hthread level, but that would * cause string finalization to slow down relative to the number of * threads; string finalization must check the string cache for "weak" * references to the string being finalized to avoid dead pointers. * * Thus, string caches are now at the heap level now. */ struct duk_strcache_entry { duk_hstring *h; duk_uint32_t bidx; duk_uint32_t cidx; }; /* * Longjmp state, contains the information needed to perform a longjmp. * Longjmp related values are written to value1, value2, and iserror. */ struct duk_ljstate { duk_jmpbuf *jmpbuf_ptr; /* current setjmp() catchpoint */ duk_small_uint_t type; /* longjmp type */ duk_bool_t iserror; /* isError flag for yield */ duk_tval value1; /* 1st related value (type specific) */ duk_tval value2; /* 2nd related value (type specific) */ }; #define DUK_ASSERT_LJSTATE_UNSET(heap) do { \ DUK_ASSERT(heap != NULL); \ DUK_ASSERT(heap->lj.type == DUK_LJ_TYPE_UNKNOWN); \ DUK_ASSERT(heap->lj.iserror == 0); \ DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value1)); \ DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(&heap->lj.value2)); \ } while (0) #define DUK_ASSERT_LJSTATE_SET(heap) do { \ DUK_ASSERT(heap != NULL); \ DUK_ASSERT(heap->lj.type != DUK_LJ_TYPE_UNKNOWN); \ } while (0) /* * Literal intern cache */ struct duk_litcache_entry { const duk_uint8_t *addr; duk_hstring *h; }; /* * Main heap structure */ #if defined(DUK_USE_ASSERTIONS) DUK_INTERNAL_DECL void duk_heap_assert_valid(duk_heap *heap); #define DUK_HEAP_ASSERT_VALID(heap) do { duk_heap_assert_valid((heap)); } while (0) #else #define DUK_HEAP_ASSERT_VALID(heap) do {} while (0) #endif struct duk_heap { duk_small_uint_t flags; /* Allocator functions. */ duk_alloc_function alloc_func; duk_realloc_function realloc_func; duk_free_function free_func; /* Heap udata, used for allocator functions but also for other heap * level callbacks like fatal function, pointer compression, etc. */ void *heap_udata; /* Fatal error handling, called e.g. when a longjmp() is needed but * lj.jmpbuf_ptr is NULL. fatal_func must never return; it's not * declared as "noreturn" because doing that for typedefs is a bit * challenging portability-wise. */ duk_fatal_function fatal_func; /* Main list of allocated heap objects. Objects are either here, * in finalize_list waiting for processing, or in refzero_list * temporarily while a DECREF refzero cascade finishes. */ duk_heaphdr *heap_allocated; /* Temporary work list for freeing a cascade of objects when a DECREF * (or DECREF_NORZ) encounters a zero refcount. Using a work list * allows fixed C stack size when refcounts go to zero for a chain of * objects. Outside of DECREF this is always a NULL because DECREF is * processed without side effects (only memory free calls). */ #if defined(DUK_USE_REFERENCE_COUNTING) duk_heaphdr *refzero_list; #endif #if defined(DUK_USE_FINALIZER_SUPPORT) /* Work list for objects to be finalized. */ duk_heaphdr *finalize_list; #if defined(DUK_USE_ASSERTIONS) /* Object whose finalizer is executing right now (no nesting). */ duk_heaphdr *currently_finalizing; #endif #endif /* Freelist for duk_activations and duk_catchers. */ #if defined(DUK_USE_CACHE_ACTIVATION) duk_activation *activation_free; #endif #if defined(DUK_USE_CACHE_CATCHER) duk_catcher *catcher_free; #endif /* Voluntary mark-and-sweep trigger counter. Intentionally signed * because we continue decreasing the value when voluntary GC cannot * run. */ #if defined(DUK_USE_VOLUNTARY_GC) duk_int_t ms_trigger_counter; #endif /* Mark-and-sweep recursion control: too deep recursion causes * multi-pass processing to avoid growing C stack without bound. */ duk_uint_t ms_recursion_depth; /* Mark-and-sweep flags automatically active (used for critical sections). */ duk_small_uint_t ms_base_flags; /* Mark-and-sweep running flag. Prevents re-entry, and also causes * refzero events to be ignored (= objects won't be queued to refzero_list). * * 0: mark-and-sweep not running * 1: mark-and-sweep is running * 2: heap destruction active or debugger active, prevent mark-and-sweep * and refzero processing (but mark-and-sweep not itself running) */ duk_uint_t ms_running; /* Mark-and-sweep prevent count, stacking. Used to avoid M&S side * effects (besides finalizers which are controlled separately) such * as compacting the string table or object property tables. This * is also bumped when ms_running is set to prevent recursive re-entry. * Can also be bumped when mark-and-sweep is not running. */ duk_uint_t ms_prevent_count; /* Finalizer processing prevent count, stacking. Bumped when finalizers * are processed to prevent recursive finalizer processing (first call site * processing finalizers handles all finalizers until the list is empty). * Can also be bumped explicitly to prevent finalizer execution. */ duk_uint_t pf_prevent_count; /* When processing finalize_list, don't actually run finalizers but * queue finalizable objects back to heap_allocated as is. This is * used during heap destruction to deal with finalizers that keep * on creating more finalizable garbage. */ duk_uint_t pf_skip_finalizers; #if defined(DUK_USE_ASSERTIONS) /* Set when we're in a critical path where an error throw would cause * e.g. sandboxing/protected call violations or state corruption. This * is just used for asserts. */ duk_bool_t error_not_allowed; #endif #if defined(DUK_USE_ASSERTIONS) /* Set when heap is still being initialized, helps with writing * some assertions. */ duk_bool_t heap_initializing; #endif /* Marker for detecting internal "double faults", errors thrown when * we're trying to create an error object, see duk_error_throw.c. */ duk_bool_t creating_error; /* Marker for indicating we're calling a user error augmentation * (errCreate/errThrow) function. Errors created/thrown during * such a call are not augmented. */ #if defined(DUK_USE_AUGMENT_ERROR_THROW) || defined(DUK_USE_AUGMENT_ERROR_CREATE) duk_bool_t augmenting_error; #endif /* Longjmp state. */ duk_ljstate lj; /* Heap thread, used internally and for finalization. */ duk_hthread *heap_thread; /* Current running thread. */ duk_hthread *curr_thread; /* Heap level "stash" object (e.g., various reachability roots). */ duk_hobject *heap_object; /* duk_handle_call / duk_handle_safe_call recursion depth limiting */ duk_int_t call_recursion_depth; duk_int_t call_recursion_limit; /* Mix-in value for computing string hashes; should be reasonably unpredictable. */ duk_uint32_t hash_seed; /* Random number state for duk_util_tinyrandom.c. */ #if !defined(DUK_USE_GET_RANDOM_DOUBLE) #if defined(DUK_USE_PREFER_SIZE) || !defined(DUK_USE_64BIT_OPS) duk_uint32_t rnd_state; /* State for Shamir's three-op algorithm */ #else duk_uint64_t rnd_state[2]; /* State for xoroshiro128+ */ #endif #endif /* Counter for unique local symbol creation. */ /* XXX: When 64-bit types are available, it would be more efficient to * use a duk_uint64_t at least for incrementing but maybe also for * string formatting in the Symbol constructor. */ duk_uint32_t sym_counter[2]; /* For manual debugging: instruction count based on executor and * interrupt counter book-keeping. Inspect debug logs to see how * they match up. */ #if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG) duk_int_t inst_count_exec; duk_int_t inst_count_interrupt; #endif /* Debugger state. */ #if defined(DUK_USE_DEBUGGER_SUPPORT) /* Callbacks and udata; dbg_read_cb != NULL is used to indicate attached state. */ duk_debug_read_function dbg_read_cb; /* required, NULL implies detached */ duk_debug_write_function dbg_write_cb; /* required */ duk_debug_peek_function dbg_peek_cb; duk_debug_read_flush_function dbg_read_flush_cb; duk_debug_write_flush_function dbg_write_flush_cb; duk_debug_request_function dbg_request_cb; duk_debug_detached_function dbg_detached_cb; void *dbg_udata; /* The following are only relevant when debugger is attached. */ duk_bool_t dbg_processing; /* currently processing messages or breakpoints: don't enter message processing recursively (e.g. no breakpoints when processing debugger eval) */ duk_bool_t dbg_state_dirty; /* resend state next time executor is about to run */ duk_bool_t dbg_force_restart; /* force executor restart to recheck breakpoints; used to handle function returns (see GH-303) */ duk_bool_t dbg_detaching; /* debugger detaching; used to avoid calling detach handler recursively */ duk_small_uint_t dbg_pause_flags; /* flags for automatic pause behavior */ duk_activation *dbg_pause_act; /* activation related to pause behavior (pause on line change, function entry/exit) */ duk_uint32_t dbg_pause_startline; /* starting line number for line change related pause behavior */ duk_breakpoint dbg_breakpoints[DUK_HEAP_MAX_BREAKPOINTS]; /* breakpoints: [0,breakpoint_count[ gc reachable */ duk_small_uint_t dbg_breakpoint_count; duk_breakpoint *dbg_breakpoints_active[DUK_HEAP_MAX_BREAKPOINTS + 1]; /* currently active breakpoints: NULL term, borrowed pointers */ /* XXX: make active breakpoints actual copies instead of pointers? */ /* These are for rate limiting Status notifications and transport peeking. */ duk_uint_t dbg_exec_counter; /* cumulative opcode execution count (overflows are OK) */ duk_uint_t dbg_last_counter; /* value of dbg_exec_counter when we last did a Date-based check */ duk_double_t dbg_last_time; /* time when status/peek was last done (Date-based rate limit) */ /* Used to support single-byte stream lookahead. */ duk_bool_t dbg_have_next_byte; duk_uint8_t dbg_next_byte; #endif /* DUK_USE_DEBUGGER_SUPPORT */ #if defined(DUK_USE_ASSERTIONS) duk_bool_t dbg_calling_transport; /* transport call in progress, calling into Duktape forbidden */ #endif /* String intern table (weak refs). */ #if defined(DUK_USE_STRTAB_PTRCOMP) duk_uint16_t *strtable16; #else duk_hstring **strtable; #endif duk_uint32_t st_mask; /* mask for lookup, st_size - 1 */ duk_uint32_t st_size; /* stringtable size */ #if (DUK_USE_STRTAB_MINSIZE != DUK_USE_STRTAB_MAXSIZE) duk_uint32_t st_count; /* string count for resize load factor checks */ #endif duk_bool_t st_resizing; /* string table is being resized; avoid recursive resize */ /* String access cache (codepoint offset -> byte offset) for fast string * character looping; 'weak' reference which needs special handling in GC. */ duk_strcache_entry strcache[DUK_HEAP_STRCACHE_SIZE]; #if defined(DUK_USE_LITCACHE_SIZE) /* Literal intern cache. When enabled, strings interned as literals * (e.g. duk_push_literal()) will be pinned and cached for the lifetime * of the heap. */ duk_litcache_entry litcache[DUK_USE_LITCACHE_SIZE]; #endif /* Built-in strings. */ #if defined(DUK_USE_ROM_STRINGS) /* No field needed when strings are in ROM. */ #else #if defined(DUK_USE_HEAPPTR16) duk_uint16_t strs16[DUK_HEAP_NUM_STRINGS]; #else duk_hstring *strs[DUK_HEAP_NUM_STRINGS]; #endif #endif /* Stats. */ #if defined(DUK_USE_DEBUG) duk_int_t stats_exec_opcodes; duk_int_t stats_exec_interrupt; duk_int_t stats_exec_throw; duk_int_t stats_call_all; duk_int_t stats_call_tailcall; duk_int_t stats_call_ecmatoecma; duk_int_t stats_safecall_all; duk_int_t stats_safecall_nothrow; duk_int_t stats_safecall_throw; duk_int_t stats_ms_try_count; duk_int_t stats_ms_skip_count; duk_int_t stats_ms_emergency_count; duk_int_t stats_strtab_intern_hit; duk_int_t stats_strtab_intern_miss; duk_int_t stats_strtab_resize_check; duk_int_t stats_strtab_resize_grow; duk_int_t stats_strtab_resize_shrink; duk_int_t stats_strtab_litcache_hit; duk_int_t stats_strtab_litcache_miss; duk_int_t stats_strtab_litcache_pin; duk_int_t stats_object_realloc_props; duk_int_t stats_object_abandon_array; duk_int_t stats_getownpropdesc_count; duk_int_t stats_getownpropdesc_hit; duk_int_t stats_getownpropdesc_miss; duk_int_t stats_getpropdesc_count; duk_int_t stats_getpropdesc_hit; duk_int_t stats_getpropdesc_miss; duk_int_t stats_getprop_all; duk_int_t stats_getprop_arrayidx; duk_int_t stats_getprop_bufobjidx; duk_int_t stats_getprop_bufferidx; duk_int_t stats_getprop_bufferlen; duk_int_t stats_getprop_stringidx; duk_int_t stats_getprop_stringlen; duk_int_t stats_getprop_proxy; duk_int_t stats_getprop_arguments; duk_int_t stats_putprop_all; duk_int_t stats_putprop_arrayidx; duk_int_t stats_putprop_bufobjidx; duk_int_t stats_putprop_bufferidx; duk_int_t stats_putprop_proxy; duk_int_t stats_getvar_all; duk_int_t stats_putvar_all; duk_int_t stats_envrec_delayedcreate; duk_int_t stats_envrec_create; duk_int_t stats_envrec_newenv; duk_int_t stats_envrec_oldenv; duk_int_t stats_envrec_pushclosure; #endif }; /* * Prototypes */ DUK_INTERNAL_DECL duk_heap *duk_heap_alloc(duk_alloc_function alloc_func, duk_realloc_function realloc_func, duk_free_function free_func, void *heap_udata, duk_fatal_function fatal_func); DUK_INTERNAL_DECL void duk_heap_free(duk_heap *heap); DUK_INTERNAL_DECL void duk_free_hobject(duk_heap *heap, duk_hobject *h); DUK_INTERNAL_DECL void duk_free_hbuffer(duk_heap *heap, duk_hbuffer *h); DUK_INTERNAL_DECL void duk_free_hstring(duk_heap *heap, duk_hstring *h); DUK_INTERNAL_DECL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr); DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); #if defined(DUK_USE_REFERENCE_COUNTING) DUK_INTERNAL_DECL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr); #endif #if defined(DUK_USE_FINALIZER_SUPPORT) DUK_INTERNAL_DECL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr); DUK_INTERNAL_DECL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr); #endif #if defined(DUK_USE_ASSERTIONS) DUK_INTERNAL_DECL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr); #endif #if defined(DUK_USE_INTERRUPT_COUNTER) DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr); #endif DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen); DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len); #if defined(DUK_USE_LITCACHE_SIZE) DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_literal_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t blen); #endif DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32(duk_heap *heap, duk_uint32_t val); DUK_INTERNAL_DECL duk_hstring *duk_heap_strtable_intern_u32_checked(duk_hthread *thr, duk_uint32_t val); #if defined(DUK_USE_REFERENCE_COUNTING) DUK_INTERNAL_DECL void duk_heap_strtable_unlink(duk_heap *heap, duk_hstring *h); #endif DUK_INTERNAL_DECL void duk_heap_strtable_unlink_prev(duk_heap *heap, duk_hstring *h, duk_hstring *prev); DUK_INTERNAL_DECL void duk_heap_strtable_force_resize(duk_heap *heap); DUK_INTERNAL void duk_heap_strtable_free(duk_heap *heap); #if defined(DUK_USE_DEBUG) DUK_INTERNAL void duk_heap_strtable_dump(duk_heap *heap); #endif DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h); DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset); #if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) DUK_INTERNAL_DECL void *duk_default_alloc_function(void *udata, duk_size_t size); DUK_INTERNAL_DECL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize); DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr); #endif DUK_INTERNAL_DECL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked(duk_hthread *thr, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, duk_size_t size); DUK_INTERNAL_DECL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize); DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize); DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr); DUK_INTERNAL_DECL void duk_heap_free_freelists(duk_heap *heap); #if defined(DUK_USE_FINALIZER_SUPPORT) DUK_INTERNAL_DECL void duk_heap_run_finalizer(duk_heap *heap, duk_hobject *obj); DUK_INTERNAL_DECL void duk_heap_process_finalize_list(duk_heap *heap); #endif /* DUK_USE_FINALIZER_SUPPORT */ DUK_INTERNAL_DECL void duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags); DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len); #endif /* DUK_HEAP_H_INCLUDED */