/*
 *  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)

/* Voluntary mark-and-sweep: triggered periodically. */
#define DUK_MS_FLAG_VOLUNTARY                (1U << 1)

/* 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 << 2)

/* 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 << 3)

/*
 *  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 */
