mirror of https://github.com/svaarala/duktape.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
603 lines
26 KiB
603 lines
26 KiB
/*
|
|
* 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_RUNNING (1 << 0) /* mark-and-sweep is currently running */
|
|
#define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED (1 << 1) /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */
|
|
#define DUK_HEAP_FLAG_REFZERO_FREE_RUNNING (1 << 2) /* refcount code is processing refzero list */
|
|
#define DUK_HEAP_FLAG_ERRHANDLER_RUNNING (1 << 3) /* an error handler (user callback to augment/replace error) is running */
|
|
#define DUK_HEAP_FLAG_INTERRUPT_RUNNING (1 << 4) /* executor interrupt running (used to avoid nested interrupts) */
|
|
#define DUK_HEAP_FLAG_FINALIZER_NORESCUE (1 << 5) /* heap destruction ongoing, finalizer rescue no longer possible */
|
|
#define DUK_HEAP_FLAG_DEBUGGER_PAUSED (1 << 6) /* 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_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING)
|
|
#define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
|
|
#define DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING)
|
|
#define DUK_HEAP_HAS_ERRHANDLER_RUNNING(heap) DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
|
|
#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_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING)
|
|
#define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
|
|
#define DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING)
|
|
#define DUK_HEAP_SET_ERRHANDLER_RUNNING(heap) DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
|
|
#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_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING)
|
|
#define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
|
|
#define DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING)
|
|
#define DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
|
|
#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.
|
|
*/
|
|
|
|
#define DUK_MS_FLAG_EMERGENCY (1 << 0) /* emergency mode: try extra hard */
|
|
#define DUK_MS_FLAG_NO_STRINGTABLE_RESIZE (1 << 1) /* don't resize stringtable (but may sweep it); needed during stringtable resize */
|
|
#define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1 << 2) /* don't compact objects; needed during object property allocation resize */
|
|
#define DUK_MS_FLAG_NO_FINALIZERS (1 << 3) /* don't run finalizers; leave finalizable objects in finalize_list for next round */
|
|
#define DUK_MS_FLAG_SKIP_FINALIZERS (1 << 4) /* don't run finalizers; queue finalizable objects back to heap_allocated */
|
|
|
|
/*
|
|
* 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
|
|
|
|
/*
|
|
* 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
|
|
|
|
/* 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 */
|
|
|
|
/* helper to insert a (non-string) heap object into heap allocated list */
|
|
#define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr) duk_heap_insert_into_heap_allocated((heap),(hdr))
|
|
|
|
/*
|
|
* Stringtable
|
|
*/
|
|
|
|
/* initial stringtable size, must be prime and higher than DUK_UTIL_MIN_HASH_PRIME */
|
|
#define DUK_STRTAB_INITIAL_SIZE 17
|
|
|
|
/* indicates a deleted string; any fixed non-NULL, non-hstring pointer works */
|
|
#define DUK_STRTAB_DELETED_MARKER(heap) ((duk_hstring *) heap)
|
|
|
|
/* resizing parameters */
|
|
#define DUK_STRTAB_MIN_FREE_DIVISOR 4 /* load factor max 75% */
|
|
#define DUK_STRTAB_MIN_USED_DIVISOR 4 /* load factor min 25% */
|
|
#define DUK_STRTAB_GROW_ST_SIZE(n) ((n) + (n)) /* used entries + approx 100% -> reset load to 50% */
|
|
|
|
#define DUK_STRTAB_U32_MAX_STRLEN 10 /* 4'294'967'295 */
|
|
#define DUK_STRTAB_HIGHEST_32BIT_PRIME 0xfffffffbUL
|
|
|
|
/* probe sequence (open addressing) */
|
|
#define DUK_STRTAB_HASH_INITIAL(hash,h_size) ((hash) % (h_size))
|
|
#define DUK_STRTAB_HASH_PROBE_STEP(hash) DUK_UTIL_GET_HASH_PROBE_STEP((hash))
|
|
|
|
/* fixed top level hashtable size (separate chaining) */
|
|
#define DUK_STRTAB_CHAIN_SIZE DUK_USE_STRTAB_CHAIN_SIZE
|
|
|
|
/*
|
|
* 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))
|
|
|
|
/*
|
|
* Memory constants
|
|
*/
|
|
|
|
#define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT 5 /* 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
|
|
|
|
/* Step types */
|
|
#define DUK_STEP_TYPE_NONE 0
|
|
#define DUK_STEP_TYPE_INTO 1
|
|
#define DUK_STEP_TYPE_OVER 2
|
|
#define DUK_STEP_TYPE_OUT 3
|
|
|
|
struct duk_breakpoint {
|
|
duk_hstring *filename;
|
|
duk_uint32_t line;
|
|
};
|
|
|
|
#if defined(DUK_USE_DEBUGGER_SUPPORT)
|
|
#define DUK_HEAP_IS_DEBUGGER_ATTACHED(heap) ((heap)->dbg_read_cb != NULL)
|
|
#define DUK_HEAP_CLEAR_STEP_STATE(heap) do { \
|
|
(heap)->dbg_step_type = DUK_STEP_TYPE_NONE; \
|
|
(heap)->dbg_step_thread = NULL; \
|
|
(heap)->dbg_step_csindex = 0; \
|
|
(heap)->dbg_step_startline = 0; \
|
|
} while (0)
|
|
#define DUK_HEAP_SET_PAUSED(heap) do { \
|
|
DUK_HEAP_SET_DEBUGGER_PAUSED(heap); \
|
|
(heap)->dbg_state_dirty = 1; \
|
|
DUK_HEAP_CLEAR_STEP_STATE((heap)); \
|
|
} while (0)
|
|
#define DUK_HEAP_CLEAR_PAUSED(heap) do { \
|
|
DUK_HEAP_CLEAR_DEBUGGER_PAUSED(heap); \
|
|
(heap)->dbg_state_dirty = 1; \
|
|
DUK_HEAP_CLEAR_STEP_STATE((heap)); \
|
|
} while (0)
|
|
#define DUK_HEAP_IS_PAUSED(heap) (DUK_HEAP_HAS_DEBUGGER_PAUSED((heap)))
|
|
#endif /* DUK_USE_DEBUGGER_SUPPORT */
|
|
|
|
/*
|
|
* 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 {
|
|
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) */
|
|
};
|
|
|
|
/*
|
|
* Stringtable entry for fixed size stringtable
|
|
*/
|
|
|
|
struct duk_strtab_entry {
|
|
#if defined(DUK_USE_HEAPPTR16)
|
|
/* A 16-bit listlen makes sense with 16-bit heap pointers: there
|
|
* won't be space for 64k strings anyway.
|
|
*/
|
|
duk_uint16_t listlen; /* if 0, 'str16' used, if > 0, 'strlist16' used */
|
|
union {
|
|
duk_uint16_t strlist16;
|
|
duk_uint16_t str16;
|
|
} u;
|
|
#else
|
|
duk_size_t listlen; /* if 0, 'str' used, if > 0, 'strlist' used */
|
|
union {
|
|
duk_hstring **strlist;
|
|
duk_hstring *str;
|
|
} u;
|
|
#endif
|
|
};
|
|
|
|
/*
|
|
* Main heap structure
|
|
*/
|
|
|
|
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;
|
|
|
|
/* Precomputed pointers when using 16-bit heap pointer packing. */
|
|
#if defined(DUK_USE_HEAPPTR16)
|
|
duk_uint16_t heapptr_null16;
|
|
duk_uint16_t heapptr_deleted16;
|
|
#endif
|
|
|
|
/* 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;
|
|
|
|
/* allocated heap objects */
|
|
duk_heaphdr *heap_allocated;
|
|
|
|
/* work list for objects whose refcounts are zero but which have not been
|
|
* "finalized"; avoids recursive C calls when refcounts go to zero in a
|
|
* chain of objects.
|
|
*/
|
|
#if defined(DUK_USE_REFERENCE_COUNTING)
|
|
duk_heaphdr *refzero_list;
|
|
duk_heaphdr *refzero_list_tail;
|
|
#endif
|
|
|
|
/* mark-and-sweep control */
|
|
#if defined(DUK_USE_VOLUNTARY_GC)
|
|
duk_int_t mark_and_sweep_trigger_counter;
|
|
#endif
|
|
duk_int_t mark_and_sweep_recursion_depth;
|
|
|
|
/* mark-and-sweep flags automatically active (used for critical sections) */
|
|
duk_small_uint_t mark_and_sweep_base_flags;
|
|
|
|
/* work list for objects to be finalized (by mark-and-sweep) */
|
|
duk_heaphdr *finalize_list;
|
|
|
|
/* longjmp state */
|
|
duk_ljstate lj;
|
|
|
|
/* marker for detecting internal "double faults", see duk_error_throw.c */
|
|
duk_bool_t handling_error;
|
|
|
|
/* heap thread, used internally and for finalization */
|
|
duk_hthread *heap_thread;
|
|
|
|
/* current thread */
|
|
duk_hthread *curr_thread; /* currently running 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;
|
|
|
|
/* rnd_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
|
|
|
|
/* 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 */
|
|
|
|
#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;
|
|
|
|
/* debugger state, only relevant when 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_step_type; /* step type: none, step into, step over, step out */
|
|
duk_hthread *dbg_step_thread; /* borrowed; NULL if no step state (NULLed in unwind) */
|
|
duk_size_t dbg_step_csindex; /* callstack index */
|
|
duk_uint32_t dbg_step_startline; /* starting line number */
|
|
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_uint32_t dbg_exec_counter; /* cumulative opcode execution count (overflows are OK) */
|
|
duk_uint32_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
|
|
|
|
/* string intern table (weak refs) */
|
|
#if defined(DUK_USE_STRTAB_PROBE)
|
|
#if defined(DUK_USE_HEAPPTR16)
|
|
duk_uint16_t *strtable16;
|
|
#else
|
|
duk_hstring **strtable;
|
|
#endif
|
|
duk_uint32_t st_size; /* alloc size in elements */
|
|
duk_uint32_t st_used; /* used elements (includes DELETED) */
|
|
#endif
|
|
|
|
/* XXX: static alloc is OK until separate chaining stringtable
|
|
* resizing is implemented.
|
|
*/
|
|
#if defined(DUK_USE_STRTAB_CHAIN)
|
|
duk_strtab_entry strtable[DUK_STRTAB_CHAIN_SIZE];
|
|
#endif
|
|
|
|
/* string access cache (codepoint offset -> byte offset) for fast string
|
|
* character looping; 'weak' reference which needs special handling in GC.
|
|
*/
|
|
duk_strcache strcache[DUK_HEAP_STRCACHE_SIZE];
|
|
|
|
/* 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
|
|
};
|
|
|
|
/*
|
|
* 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_DOUBLE_LINKED_HEAP) && defined(DUK_USE_REFERENCE_COUNTING)
|
|
DUK_INTERNAL_DECL void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
|
|
#endif
|
|
#if defined(DUK_USE_INTERRUPT_COUNTER)
|
|
DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr);
|
|
#endif
|
|
|
|
#if 0 /*unused*/
|
|
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen);
|
|
#endif
|
|
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen);
|
|
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len);
|
|
#if 0 /*unused*/
|
|
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val);
|
|
#endif
|
|
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val);
|
|
DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val);
|
|
#if defined(DUK_USE_REFERENCE_COUNTING)
|
|
DUK_INTERNAL_DECL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h);
|
|
#endif
|
|
#if defined(DUK_USE_MS_STRINGTABLE_RESIZE)
|
|
DUK_INTERNAL_DECL void duk_heap_force_strtab_resize(duk_heap *heap);
|
|
#endif
|
|
DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap);
|
|
#if defined(DUK_USE_DEBUG)
|
|
DUK_INTERNAL void duk_heap_dump_strtab(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_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);
|
|
|
|
#if defined(DUK_USE_REFERENCE_COUNTING)
|
|
DUK_INTERNAL_DECL void duk_refzero_free_pending(duk_hthread *thr);
|
|
DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr);
|
|
#if 0 /* Not needed: fast path handles inline; slow path uses duk_heaphdr_decref() which is needed anyway. */
|
|
DUK_INTERNAL_DECL void duk_hstring_decref(duk_hthread *thr, duk_hstring *h);
|
|
DUK_INTERNAL_DECL void duk_hstring_decref_norz(duk_hthread *thr, duk_hstring *h);
|
|
DUK_INTERNAL_DECL void duk_hbuffer_decref(duk_hthread *thr, duk_hbuffer *h);
|
|
DUK_INTERNAL_DECL void duk_hbuffer_decref_norz(duk_hthread *thr, duk_hbuffer *h);
|
|
DUK_INTERNAL_DECL void duk_hobject_decref(duk_hthread *thr, duk_hobject *h);
|
|
DUK_INTERNAL_DECL void duk_hobject_decref_norz(duk_hthread *thr, duk_hobject *h);
|
|
#endif
|
|
DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h);
|
|
DUK_INTERNAL_DECL void duk_heaphdr_refzero_norz(duk_hthread *thr, duk_heaphdr *h);
|
|
#if defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
|
|
DUK_INTERNAL_DECL void duk_hstring_refzero(duk_hthread *thr, duk_hstring *h); /* no 'norz' variant */
|
|
DUK_INTERNAL_DECL void duk_hbuffer_refzero(duk_hthread *thr, duk_hbuffer *h); /* no 'norz' variant */
|
|
DUK_INTERNAL_DECL void duk_hobject_refzero(duk_hthread *thr, duk_hobject *h);
|
|
DUK_INTERNAL_DECL void duk_hobject_refzero_norz(duk_hthread *thr, duk_hobject *h);
|
|
#else
|
|
DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv);
|
|
DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv);
|
|
DUK_INTERNAL_DECL void duk_tval_decref_norz(duk_hthread *thr, duk_tval *tv);
|
|
DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h);
|
|
DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h);
|
|
DUK_INTERNAL_DECL void duk_heaphdr_decref_norz(duk_hthread *thr, duk_heaphdr *h);
|
|
#endif
|
|
#else /* DUK_USE_REFERENCE_COUNTING */
|
|
/* no refcounting */
|
|
#endif /* DUK_USE_REFERENCE_COUNTING */
|
|
|
|
DUK_INTERNAL_DECL duk_bool_t 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 */
|
|
|