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.
410 lines
17 KiB
410 lines
17 KiB
/*
|
|
* Heap structure.
|
|
*
|
|
* Heap contains allocated heap objects, interned strings, and built-in
|
|
* strings for one or more threads.
|
|
*/
|
|
|
|
#ifndef DUK_HEAP_H_INCLUDED
|
|
#define DUK_HEAP_H_INCLUDED
|
|
|
|
#include "duk_bittypes.h"
|
|
#include "duk_forwdecl.h"
|
|
#include "duk_strings.h" /* autogenerated */
|
|
|
|
/* alloc function typedefs in duk_api.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_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_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_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)
|
|
|
|
/*
|
|
* Longjmp types, also double as identifying continuation type for a rethrow (in 'finally')
|
|
*/
|
|
|
|
#define DUK_LJ_TYPE_UNKNOWN 0 /* unused */
|
|
#define DUK_LJ_TYPE_RETURN 1 /* value1 -> return value */
|
|
#define DUK_LJ_TYPE_THROW 2 /* value1 -> error object */
|
|
#define DUK_LJ_TYPE_BREAK 3 /* value1 -> label number */
|
|
#define DUK_LJ_TYPE_CONTINUE 4 /* value1 -> label number */
|
|
#define DUK_LJ_TYPE_YIELD 5 /* value1 -> yield value, iserror -> error / normal */
|
|
#define DUK_LJ_TYPE_RESUME 6 /* value1 -> resume value, value2 -> resumee thread, iserror -> error/normal */
|
|
#define DUK_LJ_TYPE_NORMAL 7 /* pseudo-type to indicate a normal continuation (for 'finally' rethrowing) */
|
|
|
|
/* dummy non-zero value to be used as an argument for longjmp(), see man longjmp */
|
|
#define DUK_LONGJMP_DUMMY_VALUE 1
|
|
|
|
/*
|
|
* 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_FINALIZERS (1 << 2) /* don't run finalizers (which may have arbitrary side effects) */
|
|
#define DUK_MS_FLAG_NO_OBJECT_COMPACTION (1 << 3) /* don't compact objects; needed during object property allocation resize */
|
|
|
|
/*
|
|
* Other heap related defines
|
|
*/
|
|
|
|
/* Maximum duk_handle_call / duk_handle_safe_call depth. Note that this
|
|
* does not limit bytecode executor internal call depth at all (e.g.
|
|
* for Ecmascript-to-Ecmascript calls, thread yields/resumes, etc).
|
|
* There is a separate callstack depth limit for threads.
|
|
*/
|
|
|
|
#define DUK_HEAP_DEFAULT_CALL_RECURSION_LIMIT 60 /* assuming 0.5 kB between calls, about 30kB of stack */
|
|
|
|
/* mark-and-sweep C recursion depth for marking phase; if reached,
|
|
* mark object as a TEMPROOT and use multi-pass marking.
|
|
*/
|
|
#ifdef DUK_USE_MARK_AND_SWEEP
|
|
#ifdef DUK_USE_GC_TORTURE
|
|
#define DUK_HEAP_DEFAULT_MARK_AND_SWEEP_RECURSION_LIMIT 3
|
|
#else
|
|
#define DUK_HEAP_DEFAULT_MARK_AND_SWEEP_RECURSION_LIMIT 32
|
|
#endif
|
|
#endif
|
|
|
|
/* mark-and-sweep interval can be much lower with reference counting */
|
|
#ifdef DUK_USE_MARK_AND_SWEEP
|
|
#ifdef DUK_USE_REFERENCE_COUNTING
|
|
#define DUK_HEAP_DEFAULT_MARK_AND_SWEEP_TRIGGER_LIMIT 10000
|
|
#else
|
|
#define DUK_HEAP_DEFAULT_MARK_AND_SWEEP_TRIGGER_LIMIT 1000
|
|
#endif
|
|
#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 0xfffffffbU
|
|
|
|
/* probe sequence */
|
|
#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))
|
|
|
|
/*
|
|
* Built-in strings
|
|
*/
|
|
|
|
/* heap string indices are autogenerated in duk_strings.h */
|
|
#define DUK_HEAP_GET_STRING(heap,idx) ((heap)->strs[(idx)])
|
|
|
|
/*
|
|
* Raw memory calls: relative to heap, but no GC interaction
|
|
*/
|
|
|
|
#define DUK_ALLOC_RAW(heap,size) \
|
|
((heap)->alloc_func((heap)->alloc_udata, (size)))
|
|
|
|
#define DUK_REALLOC_RAW(heap,ptr,newsize) \
|
|
((heap)->realloc_func((heap)->alloc_udata, (ptr), (newsize)))
|
|
|
|
#define DUK_FREE_RAW(heap,ptr) \
|
|
((heap)->free_func((heap)->alloc_udata, (ptr)))
|
|
|
|
/*
|
|
* Memory calls: relative to heap, GC interaction, but no error throwing.
|
|
*
|
|
* FIXME: 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 (and checked variant) 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 knows the storage
|
|
* location of the pointer being reallocated and looks it up on every
|
|
* attempt; the storage location must of course be stable, which is
|
|
* always the case for heap objects now.
|
|
*
|
|
* Note: the pointer in the storage location ('iptr') is read but is
|
|
* NOT updated; caller must do that.
|
|
*/
|
|
|
|
#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,iptr,newsize) duk_heap_mem_realloc_indirect((heap), (iptr), (newsize))
|
|
#define DUK_FREE(heap,ptr) duk_heap_mem_free((heap), (ptr))
|
|
|
|
/*
|
|
* Memory calls: relative to a thread, GC interaction, throw error on alloc failure
|
|
*/
|
|
|
|
/* XXX: add __func__ */
|
|
|
|
#ifdef DUK_USE_VERBOSE_ERRORS
|
|
#define DUK_ALLOC_CHECKED(thr,size) duk_heap_mem_alloc_checked((thr), (size), __FILE__, __LINE__)
|
|
#define DUK_ALLOC_CHECKED_ZEROED(thr,size) duk_heap_mem_alloc_checked_zeroed((thr), (size), __FILE__, __LINE__)
|
|
#define DUK_REALLOC_CHECKED(thr,ptr,newsize) duk_heap_mem_realloc_checked((thr), (ptr), (newsize), __FILE__, __LINE__)
|
|
#define DUK_REALLOC_INDIRECT_CHECKED(thr,iptr,newsize) duk_heap_mem_realloc_indirect_checked((thr), (iptr), (newsize), __FILE__, __LINE__)
|
|
#define DUK_FREE_CHECKED(thr,ptr) duk_heap_mem_free((thr)->heap, (ptr)) /* must not fail */
|
|
#else
|
|
#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_REALLOC_CHECKED(thr,ptr,newsize) duk_heap_mem_realloc_checked((thr), (ptr), (newsize))
|
|
#define DUK_REALLOC_INDIRECT_CHECKED(thr,iptr,newsize) duk_heap_mem_realloc_indirect_checked((thr), (iptr), (newsize))
|
|
#define DUK_FREE_CHECKED(thr,ptr) duk_heap_mem_free((thr)->heap, (ptr)) /* must not fail */
|
|
#endif
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
/*
|
|
* 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_u32 bidx;
|
|
duk_u32 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_hobject *errhandler; /* function to invoke for errors before unwinding; may be NULL, -borrowed reference- (must be in valstack) */
|
|
int type; /* longjmp type */
|
|
duk_tval value1; /* 1st related value (type specific) */
|
|
duk_tval value2; /* 2nd related value (type specific) */
|
|
int iserror; /* isError flag for yield */
|
|
};
|
|
|
|
/*
|
|
* Main heap structure
|
|
*/
|
|
|
|
struct duk_heap {
|
|
int flags;
|
|
|
|
/* allocator functions */
|
|
duk_alloc_function alloc_func;
|
|
duk_realloc_function realloc_func;
|
|
duk_free_function free_func;
|
|
void *alloc_udata;
|
|
|
|
/* 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.
|
|
*/
|
|
#ifdef DUK_USE_REFERENCE_COUNTING
|
|
duk_heaphdr *refzero_list;
|
|
duk_heaphdr *refzero_list_tail;
|
|
#endif
|
|
|
|
#ifdef DUK_USE_MARK_AND_SWEEP
|
|
/* mark-and-sweep control */
|
|
int mark_and_sweep_trigger_counter;
|
|
int mark_and_sweep_trigger_limit;
|
|
int mark_and_sweep_recursion_depth;
|
|
int mark_and_sweep_recursion_limit;
|
|
|
|
/* mark-and-sweep flags automatically active (used for critical sections) */
|
|
int mark_and_sweep_base_flags;
|
|
|
|
/* work list for objects to be finalized (by mark-and-sweep) */
|
|
duk_heaphdr *finalize_list;
|
|
#endif
|
|
|
|
/* fatal error handling, called e.g. when a longjmp() is needed but
|
|
* lj.jmpbuf_ptr is NULL. fatal_func must never return.
|
|
*/
|
|
duk_fatal_function fatal_func;
|
|
|
|
/* longjmp state */
|
|
duk_ljstate lj;
|
|
|
|
/* marker for detecting internal "double faults", see duk_error_throw.c */
|
|
int 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 */
|
|
int call_recursion_depth;
|
|
int call_recursion_limit;
|
|
|
|
/* mix-in value for computing string hashes; should be reasonably unpredictable */
|
|
duk_u32 hash_seed;
|
|
|
|
/* rnd_state for duk_util_tinyrandom.c */
|
|
duk_u32 rnd_state;
|
|
|
|
/* string intern table (weak refs) */
|
|
duk_hstring **st;
|
|
duk_u32 st_size; /* alloc size in elements */
|
|
duk_u32 st_used; /* used elements (includes DELETED) */
|
|
|
|
/* 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 */
|
|
duk_hstring *strs[DUK_HEAP_NUM_STRINGS];
|
|
};
|
|
|
|
/*
|
|
* Prototypes
|
|
*/
|
|
|
|
duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
|
|
duk_realloc_function realloc_func,
|
|
duk_free_function free_func,
|
|
void *alloc_udata,
|
|
duk_fatal_function fatal_func);
|
|
duk_heap *duk_heap_alloc_default(void);
|
|
void duk_heap_free(duk_heap *heap);
|
|
void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr);
|
|
|
|
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)
|
|
void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
|
|
#endif
|
|
|
|
duk_hstring *duk_heap_string_lookup(duk_heap *heap, duk_u8 *str, duk_u32 blen);
|
|
duk_hstring *duk_heap_string_intern(duk_heap *heap, duk_u8 *str, duk_u32 blen);
|
|
duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, duk_u8 *str, duk_u32 len);
|
|
duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_u32 val);
|
|
duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_u32 val);
|
|
duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_u32 val);
|
|
void duk_heap_string_remove(duk_heap *heap, duk_hstring *h);
|
|
void duk_heap_force_stringtable_resize(duk_heap *heap);
|
|
|
|
void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h);
|
|
duk_u32 duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_u32 char_offset);
|
|
|
|
#ifdef DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS
|
|
void *duk_default_alloc_function(void *udata, size_t size);
|
|
void *duk_default_realloc_function(void *udata, void *ptr, size_t newsize);
|
|
void duk_default_free_function(void *udata, void *ptr);
|
|
#endif
|
|
|
|
void *duk_heap_mem_alloc(duk_heap *heap, size_t size);
|
|
void *duk_heap_mem_alloc_zeroed(duk_heap *heap, size_t size);
|
|
void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, size_t newsize);
|
|
void *duk_heap_mem_realloc_indirect(duk_heap *heap, void **iptr, size_t newsize);
|
|
void duk_heap_mem_free(duk_heap *heap, void *ptr);
|
|
|
|
#ifdef DUK_USE_VERBOSE_ERRORS
|
|
void *duk_heap_mem_alloc_checked(duk_hthread *thr, size_t size, const char *filename, int line);
|
|
void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, size_t size, const char *filename, int line);
|
|
void *duk_heap_mem_realloc_checked(duk_hthread *thr, void *ptr, size_t newsize, const char *filename, int line);
|
|
void *duk_heap_mem_realloc_indirect_checked(duk_hthread *thr, void **iptr, size_t newsize, const char *filename, int line);
|
|
#else
|
|
void *duk_heap_mem_alloc_checked(duk_hthread *thr, size_t size);
|
|
void *duk_heap_mem_alloc_checked_zeroed(duk_hthread *thr, size_t size);
|
|
void *duk_heap_mem_realloc_checked(duk_hthread *thr, void *ptr, size_t newsize);
|
|
void *duk_heap_mem_realloc_indirect_checked(duk_hthread *thr, void **iptr, size_t newsize);
|
|
#endif
|
|
|
|
#ifdef DUK_USE_REFERENCE_COUNTING
|
|
void duk_heap_tval_incref(duk_tval *tv);
|
|
void duk_heap_tval_decref(duk_hthread *thr, duk_tval *tv);
|
|
void duk_heap_heaphdr_incref(duk_heaphdr *h);
|
|
void duk_heap_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h);
|
|
void duk_heap_refcount_finalize_heaphdr(duk_hthread *thr, duk_heaphdr *hdr);
|
|
#else
|
|
/* no refcounting */
|
|
#endif
|
|
|
|
#ifdef DUK_USE_MARK_AND_SWEEP
|
|
int duk_heap_mark_and_sweep(duk_heap *heap, int flags);
|
|
#endif
|
|
|
|
duk_u32 duk_heap_hashstring(duk_heap *heap, duk_u8 *str, duk_u32 len);
|
|
|
|
#endif /* DUK_HEAP_H_INCLUDED */
|
|
|
|
|