1 /*
2  *  Heap structure.
3  *
4  *  Heap contains allocated heap objects, interned strings, and built-in
5  *  strings for one or more threads.
6  */
7 
8 #ifndef DUK_HEAP_H_INCLUDED
9 #define DUK_HEAP_H_INCLUDED
10 
11 /* alloc function typedefs in duktape.h */
12 
13 /*
14  *  Heap flags
15  */
16 
17 #define DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING                     (1 << 0)  /* mark-and-sweep is currently running */
18 #define DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED            (1 << 1)  /* mark-and-sweep marking reached a recursion limit and must use multi-pass marking */
19 #define DUK_HEAP_FLAG_REFZERO_FREE_RUNNING                     (1 << 2)  /* refcount code is processing refzero list */
20 #define DUK_HEAP_FLAG_ERRHANDLER_RUNNING                       (1 << 3)  /* an error handler (user callback to augment/replace error) is running */
21 #define DUK_HEAP_FLAG_INTERRUPT_RUNNING                        (1 << 4)  /* executor interrupt running (used to avoid nested interrupts) */
22 #define DUK_HEAP_FLAG_FINALIZER_NORESCUE                       (1 << 5)  /* heap destruction ongoing, finalizer rescue no longer possible */
23 
24 #define DUK__HEAP_HAS_FLAGS(heap,bits)               ((heap)->flags & (bits))
25 #define DUK__HEAP_SET_FLAGS(heap,bits)  do { \
26 		(heap)->flags |= (bits); \
27 	} while (0)
28 #define DUK__HEAP_CLEAR_FLAGS(heap,bits)  do { \
29 		(heap)->flags &= ~(bits); \
30 	} while (0)
31 
32 #define DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap)            DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING)
33 #define DUK_HEAP_HAS_MARKANDSWEEP_RECLIMIT_REACHED(heap)   DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
34 #define DUK_HEAP_HAS_REFZERO_FREE_RUNNING(heap)            DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING)
35 #define DUK_HEAP_HAS_ERRHANDLER_RUNNING(heap)              DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
36 #define DUK_HEAP_HAS_INTERRUPT_RUNNING(heap)               DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
37 #define DUK_HEAP_HAS_FINALIZER_NORESCUE(heap)              DUK__HEAP_HAS_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
38 
39 #define DUK_HEAP_SET_MARKANDSWEEP_RUNNING(heap)            DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING)
40 #define DUK_HEAP_SET_MARKANDSWEEP_RECLIMIT_REACHED(heap)   DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
41 #define DUK_HEAP_SET_REFZERO_FREE_RUNNING(heap)            DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING)
42 #define DUK_HEAP_SET_ERRHANDLER_RUNNING(heap)              DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
43 #define DUK_HEAP_SET_INTERRUPT_RUNNING(heap)               DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
44 #define DUK_HEAP_SET_FINALIZER_NORESCUE(heap)              DUK__HEAP_SET_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
45 
46 #define DUK_HEAP_CLEAR_MARKANDSWEEP_RUNNING(heap)          DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RUNNING)
47 #define DUK_HEAP_CLEAR_MARKANDSWEEP_RECLIMIT_REACHED(heap) DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_MARKANDSWEEP_RECLIMIT_REACHED)
48 #define DUK_HEAP_CLEAR_REFZERO_FREE_RUNNING(heap)          DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_REFZERO_FREE_RUNNING)
49 #define DUK_HEAP_CLEAR_ERRHANDLER_RUNNING(heap)            DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_ERRHANDLER_RUNNING)
50 #define DUK_HEAP_CLEAR_INTERRUPT_RUNNING(heap)             DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_INTERRUPT_RUNNING)
51 #define DUK_HEAP_CLEAR_FINALIZER_NORESCUE(heap)            DUK__HEAP_CLEAR_FLAGS((heap), DUK_HEAP_FLAG_FINALIZER_NORESCUE)
52 
53 /*
54  *  Longjmp types, also double as identifying continuation type for a rethrow (in 'finally')
55  */
56 
57 #define DUK_LJ_TYPE_UNKNOWN      0    /* unused */
58 #define DUK_LJ_TYPE_THROW        1    /* value1 -> error object */
59 #define DUK_LJ_TYPE_YIELD        2    /* value1 -> yield value, iserror -> error / normal */
60 #define DUK_LJ_TYPE_RESUME       3    /* value1 -> resume value, value2 -> resumee thread, iserror -> error/normal */
61 #define DUK_LJ_TYPE_BREAK        4    /* value1 -> label number, pseudo-type to indicate a break continuation (for ENDFIN) */
62 #define DUK_LJ_TYPE_CONTINUE     5    /* value1 -> label number, pseudo-type to indicate a continue continuation (for ENDFIN) */
63 #define DUK_LJ_TYPE_RETURN       6    /* value1 -> return value, pseudo-type to indicate a return continuation (for ENDFIN) */
64 #define DUK_LJ_TYPE_NORMAL       7    /* no value, pseudo-type to indicate a normal continuation (for ENDFIN) */
65 
66 /*
67  *  Mark-and-sweep flags
68  *
69  *  These are separate from heap level flags now but could be merged.
70  *  The heap structure only contains a 'base mark-and-sweep flags'
71  *  field and the GC caller can impose further flags.
72  */
73 
74 #define DUK_MS_FLAG_EMERGENCY                (1 << 0)   /* emergency mode: try extra hard */
75 #define DUK_MS_FLAG_NO_STRINGTABLE_RESIZE    (1 << 1)   /* don't resize stringtable (but may sweep it); needed during stringtable resize */
76 #define DUK_MS_FLAG_NO_OBJECT_COMPACTION     (1 << 2)   /* don't compact objects; needed during object property allocation resize */
77 #define DUK_MS_FLAG_NO_FINALIZERS            (1 << 3)   /* don't run finalizers; leave finalizable objects in finalize_list for next round */
78 #define DUK_MS_FLAG_SKIP_FINALIZERS          (1 << 4)   /* don't run finalizers; queue finalizable objects back to heap_allocated */
79 
80 /*
81  *  Thread switching
82  *
83  *  To switch heap->curr_thread, use the macro below so that interrupt counters
84  *  get updated correctly.  The macro allows a NULL target thread because that
85  *  happens e.g. in call handling.
86  */
87 
88 #if defined(DUK_USE_INTERRUPT_COUNTER)
89 #define DUK_HEAP_SWITCH_THREAD(heap,newthr)  duk_heap_switch_thread((heap), (newthr))
90 #else
91 #define DUK_HEAP_SWITCH_THREAD(heap,newthr)  do { \
92 		(heap)->curr_thread = (newthr); \
93 	} while (0)
94 #endif
95 
96 /*
97  *  Other heap related defines
98  */
99 
100 /* Mark-and-sweep interval is relative to combined count of objects and
101  * strings kept in the heap during the latest mark-and-sweep pass.
102  * Fixed point .8 multiplier and .0 adder.  Trigger count (interval) is
103  * decreased by each (re)allocation attempt (regardless of size), and each
104  * refzero processed object.
105  *
106  * 'SKIP' indicates how many (re)allocations to wait until a retry if
107  * GC is skipped because there is no thread do it with yet (happens
108  * only during init phases).
109  */
110 #if defined(DUK_USE_MARK_AND_SWEEP)
111 #if defined(DUK_USE_REFERENCE_COUNTING)
112 #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT              12800L  /* 50x heap size */
113 #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD               1024L
114 #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP              256L
115 #else
116 #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_MULT              256L    /* 1x heap size */
117 #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_ADD               1024L
118 #define DUK_HEAP_MARK_AND_SWEEP_TRIGGER_SKIP              256L
119 #endif
120 #endif
121 
122 /* Stringcache is used for speeding up char-offset-to-byte-offset
123  * translations for non-ASCII strings.
124  */
125 #define DUK_HEAP_STRCACHE_SIZE                            4
126 #define DUK_HEAP_STRINGCACHE_NOCACHE_LIMIT                16  /* strings up to the this length are not cached */
127 
128 /* helper to insert a (non-string) heap object into heap allocated list */
129 #define DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap,hdr)     duk_heap_insert_into_heap_allocated((heap),(hdr))
130 
131 /*
132  *  Stringtable
133  */
134 
135 /* initial stringtable size, must be prime and higher than DUK_UTIL_MIN_HASH_PRIME */
136 #define DUK_STRTAB_INITIAL_SIZE            17
137 
138 /* indicates a deleted string; any fixed non-NULL, non-hstring pointer works */
139 #define DUK_STRTAB_DELETED_MARKER(heap)    ((duk_hstring *) heap)
140 
141 /* resizing parameters */
142 #define DUK_STRTAB_MIN_FREE_DIVISOR        4                /* load factor max 75% */
143 #define DUK_STRTAB_MIN_USED_DIVISOR        4                /* load factor min 25% */
144 #define DUK_STRTAB_GROW_ST_SIZE(n)         ((n) + (n))      /* used entries + approx 100% -> reset load to 50% */
145 
146 #define DUK_STRTAB_U32_MAX_STRLEN          10               /* 4'294'967'295 */
147 #define DUK_STRTAB_HIGHEST_32BIT_PRIME     0xfffffffbUL
148 
149 /* probe sequence (open addressing) */
150 #define DUK_STRTAB_HASH_INITIAL(hash,h_size)    ((hash) % (h_size))
151 #define DUK_STRTAB_HASH_PROBE_STEP(hash)        DUK_UTIL_GET_HASH_PROBE_STEP((hash))
152 
153 /* fixed top level hashtable size (separate chaining) */
154 #define DUK_STRTAB_CHAIN_SIZE              DUK_USE_STRTAB_CHAIN_SIZE
155 
156 /*
157  *  Built-in strings
158  */
159 
160 /* heap string indices are autogenerated in duk_strings.h */
161 #if defined(DUK_USE_ROM_STRINGS)
162 #define DUK_HEAP_GET_STRING(heap,idx) \
163 	((duk_hstring *) DUK_LOSE_CONST(duk_rom_strings_stridx[(idx)]))
164 #else  /* DUK_USE_ROM_STRINGS */
165 #if defined(DUK_USE_HEAPPTR16)
166 #define DUK_HEAP_GET_STRING(heap,idx) \
167 	((duk_hstring *) DUK_USE_HEAPPTR_DEC16((heap)->heap_udata, (heap)->strs16[(idx)]))
168 #else
169 #define DUK_HEAP_GET_STRING(heap,idx) \
170 	((heap)->strs[(idx)])
171 #endif
172 #endif  /* DUK_USE_ROM_STRINGS */
173 
174 /*
175  *  Raw memory calls: relative to heap, but no GC interaction
176  */
177 
178 #define DUK_ALLOC_RAW(heap,size) \
179 	((heap)->alloc_func((heap)->heap_udata, (size)))
180 
181 #define DUK_REALLOC_RAW(heap,ptr,newsize) \
182 	((heap)->realloc_func((heap)->heap_udata, (void *) (ptr), (newsize)))
183 
184 #define DUK_FREE_RAW(heap,ptr) \
185 	((heap)->free_func((heap)->heap_udata, (void *) (ptr)))
186 
187 /*
188  *  Memory calls: relative to heap, GC interaction, but no error throwing.
189  *
190  *  XXX: Currently a mark-and-sweep triggered by memory allocation will run
191  *  using the heap->heap_thread.  This thread is also used for running
192  *  mark-and-sweep finalization; this is not ideal because it breaks the
193  *  isolation between multiple global environments.
194  *
195  *  Notes:
196  *
197  *    - DUK_FREE() is required to ignore NULL and any other possible return
198  *      value of a zero-sized alloc/realloc (same as ANSI C free()).
199  *
200  *    - There is no DUK_REALLOC_ZEROED because we don't assume to know the
201  *      old size.  Caller must zero the reallocated memory.
202  *
203  *    - DUK_REALLOC_INDIRECT() must be used when a mark-and-sweep triggered
204  *      by an allocation failure might invalidate the original 'ptr', thus
205  *      causing a realloc retry to use an invalid pointer.  Example: we're
206  *      reallocating the value stack and a finalizer resizes the same value
207  *      stack during mark-and-sweep.  The indirect variant requests for the
208  *      current location of the pointer being reallocated using a callback
209  *      right before every realloc attempt; this circuitous approach is used
210  *      to avoid strict aliasing issues in a more straightforward indirect
211  *      pointer (void **) approach.  Note: the pointer in the storage
212  *      location is read but is NOT updated; the caller must do that.
213  */
214 
215 /* callback for indirect reallocs, request for current pointer */
216 typedef void *(*duk_mem_getptr)(duk_heap *heap, void *ud);
217 
218 #define DUK_ALLOC(heap,size)                            duk_heap_mem_alloc((heap), (size))
219 #define DUK_ALLOC_ZEROED(heap,size)                     duk_heap_mem_alloc_zeroed((heap), (size))
220 #define DUK_REALLOC(heap,ptr,newsize)                   duk_heap_mem_realloc((heap), (ptr), (newsize))
221 #define DUK_REALLOC_INDIRECT(heap,cb,ud,newsize)        duk_heap_mem_realloc_indirect((heap), (cb), (ud), (newsize))
222 #define DUK_FREE(heap,ptr)                              duk_heap_mem_free((heap), (ptr))
223 
224 /*
225  *  Memory constants
226  */
227 
228 #define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_LIMIT           5   /* Retry allocation after mark-and-sweep for this
229                                                               * many times.  A single mark-and-sweep round is
230                                                               * not guaranteed to free all unreferenced memory
231                                                               * because of finalization (in fact, ANY number of
232                                                               * rounds is strictly not enough).
233                                                               */
234 
235 #define DUK_HEAP_ALLOC_FAIL_MARKANDSWEEP_EMERGENCY_LIMIT  3  /* Starting from this round, use emergency mode
236                                                               * for mark-and-sweep.
237                                                               */
238 
239 /*
240  *  Debugger support
241  */
242 
243 /* Maximum number of breakpoints.  Only breakpoints that are set are
244  * consulted so increasing this has no performance impact.
245  */
246 #define DUK_HEAP_MAX_BREAKPOINTS          16
247 
248 /* Opcode interval for a Date-based status/peek rate limit check.  Only
249  * relevant when debugger is attached.  Requesting a timestamp may be a
250  * slow operation on some platforms so this shouldn't be too low.  On the
251  * other hand a high value makes Duktape react to a pause request slowly.
252  */
253 #define DUK_HEAP_DBG_RATELIMIT_OPCODES    4000
254 
255 /* Milliseconds between status notify and transport peeks. */
256 #define DUK_HEAP_DBG_RATELIMIT_MILLISECS  200
257 
258 /* Step types */
259 #define DUK_STEP_TYPE_NONE  0
260 #define DUK_STEP_TYPE_INTO  1
261 #define DUK_STEP_TYPE_OVER  2
262 #define DUK_STEP_TYPE_OUT   3
263 
264 struct duk_breakpoint {
265 	duk_hstring *filename;
266 	duk_uint32_t line;
267 };
268 
269 #if defined(DUK_USE_DEBUGGER_SUPPORT)
270 #define DUK_HEAP_IS_DEBUGGER_ATTACHED(heap) ((heap)->dbg_read_cb != NULL)
271 #define DUK_HEAP_CLEAR_STEP_STATE(heap) do { \
272 		(heap)->dbg_step_type = DUK_STEP_TYPE_NONE; \
273 		(heap)->dbg_step_thread = NULL; \
274 		(heap)->dbg_step_csindex = 0; \
275 		(heap)->dbg_step_startline = 0; \
276 	} while (0)
277 #define DUK_HEAP_SET_PAUSED(heap) do { \
278 		(heap)->dbg_paused = 1; \
279 		(heap)->dbg_state_dirty = 1; \
280 		DUK_HEAP_CLEAR_STEP_STATE((heap)); \
281 	} while (0)
282 #define DUK_HEAP_CLEAR_PAUSED(heap) do { \
283 		(heap)->dbg_paused = 0; \
284 		(heap)->dbg_state_dirty = 1; \
285 		DUK_HEAP_CLEAR_STEP_STATE((heap)); \
286 	} while (0)
287 #define DUK_HEAP_IS_PAUSED(heap) ((heap)->dbg_paused)
288 #endif  /* DUK_USE_DEBUGGER_SUPPORT */
289 
290 /*
291  *  String cache should ideally be at duk_hthread level, but that would
292  *  cause string finalization to slow down relative to the number of
293  *  threads; string finalization must check the string cache for "weak"
294  *  references to the string being finalized to avoid dead pointers.
295  *
296  *  Thus, string caches are now at the heap level now.
297  */
298 
299 struct duk_strcache {
300 	duk_hstring *h;
301 	duk_uint32_t bidx;
302 	duk_uint32_t cidx;
303 };
304 
305 /*
306  *  Longjmp state, contains the information needed to perform a longjmp.
307  *  Longjmp related values are written to value1, value2, and iserror.
308  */
309 
310 struct duk_ljstate {
311 	duk_jmpbuf *jmpbuf_ptr;   /* current setjmp() catchpoint */
312 	duk_small_uint_t type;    /* longjmp type */
313 	duk_bool_t iserror;       /* isError flag for yield */
314 	duk_tval value1;          /* 1st related value (type specific) */
315 	duk_tval value2;          /* 2nd related value (type specific) */
316 };
317 
318 /*
319  *  Stringtable entry for fixed size stringtable
320  */
321 
322 struct duk_strtab_entry {
323 #if defined(DUK_USE_HEAPPTR16)
324 	/* A 16-bit listlen makes sense with 16-bit heap pointers: there
325 	 * won't be space for 64k strings anyway.
326 	 */
327 	duk_uint16_t listlen;  /* if 0, 'str16' used, if > 0, 'strlist16' used */
328 	union {
329 		duk_uint16_t strlist16;
330 		duk_uint16_t str16;
331 	} u;
332 #else
333 	duk_size_t listlen;  /* if 0, 'str' used, if > 0, 'strlist' used */
334 	union {
335 		duk_hstring **strlist;
336 		duk_hstring *str;
337 	} u;
338 #endif
339 };
340 
341 /*
342  *  Main heap structure
343  */
344 
345 struct duk_heap {
346 	duk_small_uint_t flags;
347 
348 	/* Allocator functions. */
349 	duk_alloc_function alloc_func;
350 	duk_realloc_function realloc_func;
351 	duk_free_function free_func;
352 
353 	/* Heap udata, used for allocator functions but also for other heap
354 	 * level callbacks like pointer compression, etc.
355 	 */
356 	void *heap_udata;
357 
358 	/* Precomputed pointers when using 16-bit heap pointer packing. */
359 #if defined(DUK_USE_HEAPPTR16)
360 	duk_uint16_t heapptr_null16;
361 	duk_uint16_t heapptr_deleted16;
362 #endif
363 
364 	/* Fatal error handling, called e.g. when a longjmp() is needed but
365 	 * lj.jmpbuf_ptr is NULL.  fatal_func must never return; it's not
366 	 * declared as "noreturn" because doing that for typedefs is a bit
367 	 * challenging portability-wise.
368 	 */
369 	duk_fatal_function fatal_func;
370 
371 	/* allocated heap objects */
372 	duk_heaphdr *heap_allocated;
373 
374 	/* work list for objects whose refcounts are zero but which have not been
375 	 * "finalized"; avoids recursive C calls when refcounts go to zero in a
376 	 * chain of objects.
377 	 */
378 #if defined(DUK_USE_REFERENCE_COUNTING)
379 	duk_heaphdr *refzero_list;
380 	duk_heaphdr *refzero_list_tail;
381 #endif
382 
383 #if defined(DUK_USE_MARK_AND_SWEEP)
384 	/* mark-and-sweep control */
385 #if defined(DUK_USE_VOLUNTARY_GC)
386 	duk_int_t mark_and_sweep_trigger_counter;
387 #endif
388 	duk_int_t mark_and_sweep_recursion_depth;
389 
390 	/* mark-and-sweep flags automatically active (used for critical sections) */
391 	duk_small_uint_t mark_and_sweep_base_flags;
392 
393 	/* work list for objects to be finalized (by mark-and-sweep) */
394 	duk_heaphdr *finalize_list;
395 #endif
396 
397 	/* longjmp state */
398 	duk_ljstate lj;
399 
400 	/* marker for detecting internal "double faults", see duk_error_throw.c */
401 	duk_bool_t handling_error;
402 
403 	/* heap thread, used internally and for finalization */
404 	duk_hthread *heap_thread;
405 
406 	/* current thread */
407 	duk_hthread *curr_thread;  /* currently running thread */
408 
409 	/* heap level "stash" object (e.g., various reachability roots) */
410 	duk_hobject *heap_object;
411 
412 	/* duk_handle_call / duk_handle_safe_call recursion depth limiting */
413 	duk_int_t call_recursion_depth;
414 	duk_int_t call_recursion_limit;
415 
416 	/* mix-in value for computing string hashes; should be reasonably unpredictable */
417 	duk_uint32_t hash_seed;
418 
419 	/* rnd_state for duk_util_tinyrandom.c */
420 	duk_uint32_t rnd_state;
421 
422 	/* For manual debugging: instruction count based on executor and
423 	 * interrupt counter book-keeping.  Inspect debug logs to see how
424 	 * they match up.
425 	 */
426 #if defined(DUK_USE_INTERRUPT_COUNTER) && defined(DUK_USE_DEBUG)
427 	duk_int_t inst_count_exec;
428 	duk_int_t inst_count_interrupt;
429 #endif
430 
431 	/* debugger */
432 
433 #if defined(DUK_USE_DEBUGGER_SUPPORT)
434 	/* callbacks and udata; dbg_read_cb != NULL is used to indicate attached state */
435 	duk_debug_read_function dbg_read_cb;                /* required, NULL implies detached */
436 	duk_debug_write_function dbg_write_cb;              /* required */
437 	duk_debug_peek_function dbg_peek_cb;
438 	duk_debug_read_flush_function dbg_read_flush_cb;
439 	duk_debug_write_flush_function dbg_write_flush_cb;
440 	duk_debug_request_function dbg_request_cb;
441 	duk_debug_detached_function dbg_detached_cb;
442 	void *dbg_udata;
443 
444 	/* debugger state, only relevant when attached */
445 	duk_bool_t dbg_processing;              /* currently processing messages or breakpoints: don't enter message processing recursively (e.g. no breakpoints when processing debugger eval) */
446 	duk_bool_t dbg_paused;                  /* currently paused: talk with debug client until step/resume */
447 	duk_bool_t dbg_state_dirty;             /* resend state next time executor is about to run */
448 	duk_bool_t dbg_force_restart;           /* force executor restart to recheck breakpoints; used to handle function returns (see GH-303) */
449 	duk_bool_t dbg_detaching;               /* debugger detaching; used to avoid calling detach handler recursively */
450 	duk_small_uint_t dbg_step_type;         /* step type: none, step into, step over, step out */
451 	duk_hthread *dbg_step_thread;           /* borrowed; NULL if no step state (NULLed in unwind) */
452 	duk_size_t dbg_step_csindex;            /* callstack index */
453 	duk_uint32_t dbg_step_startline;        /* starting line number */
454 	duk_breakpoint dbg_breakpoints[DUK_HEAP_MAX_BREAKPOINTS];  /* breakpoints: [0,breakpoint_count[ gc reachable */
455 	duk_small_uint_t dbg_breakpoint_count;
456 	duk_breakpoint *dbg_breakpoints_active[DUK_HEAP_MAX_BREAKPOINTS + 1];  /* currently active breakpoints: NULL term, borrowed pointers */
457 	/* XXX: make active breakpoints actual copies instead of pointers? */
458 
459 	/* These are for rate limiting Status notifications and transport peeking. */
460 	duk_uint32_t dbg_exec_counter;          /* cumulative opcode execution count (overflows are OK) */
461 	duk_uint32_t dbg_last_counter;          /* value of dbg_exec_counter when we last did a Date-based check */
462 	duk_double_t dbg_last_time;             /* time when status/peek was last done (Date-based rate limit) */
463 
464 	/* Used to support single-byte stream lookahead. */
465 	duk_bool_t dbg_have_next_byte;
466 	duk_uint8_t dbg_next_byte;
467 #endif
468 
469 	/* string intern table (weak refs) */
470 #if defined(DUK_USE_STRTAB_PROBE)
471 #if defined(DUK_USE_HEAPPTR16)
472 	duk_uint16_t *strtable16;
473 #else
474 	duk_hstring **strtable;
475 #endif
476 	duk_uint32_t st_size;     /* alloc size in elements */
477 	duk_uint32_t st_used;     /* used elements (includes DELETED) */
478 #endif
479 
480 	/* XXX: static alloc is OK until separate chaining stringtable
481 	 * resizing is implemented.
482 	 */
483 #if defined(DUK_USE_STRTAB_CHAIN)
484 	duk_strtab_entry strtable[DUK_STRTAB_CHAIN_SIZE];
485 #endif
486 
487 	/* string access cache (codepoint offset -> byte offset) for fast string
488 	 * character looping; 'weak' reference which needs special handling in GC.
489 	 */
490 	duk_strcache strcache[DUK_HEAP_STRCACHE_SIZE];
491 
492 	/* built-in strings */
493 #if defined(DUK_USE_ROM_STRINGS)
494 	/* No field needed when strings are in ROM. */
495 #else
496 #if defined(DUK_USE_HEAPPTR16)
497 	duk_uint16_t strs16[DUK_HEAP_NUM_STRINGS];
498 #else
499 	duk_hstring *strs[DUK_HEAP_NUM_STRINGS];
500 #endif
501 #endif
502 };
503 
504 /*
505  *  Prototypes
506  */
507 
508 DUK_INTERNAL_DECL
509 duk_heap *duk_heap_alloc(duk_alloc_function alloc_func,
510                          duk_realloc_function realloc_func,
511                          duk_free_function free_func,
512                          void *heap_udata,
513                          duk_fatal_function fatal_func);
514 DUK_INTERNAL_DECL void duk_heap_free(duk_heap *heap);
515 DUK_INTERNAL_DECL void duk_free_hobject_inner(duk_heap *heap, duk_hobject *h);
516 DUK_INTERNAL_DECL void duk_free_hbuffer_inner(duk_heap *heap, duk_hbuffer *h);
517 DUK_INTERNAL_DECL void duk_free_hstring_inner(duk_heap *heap, duk_hstring *h);
518 DUK_INTERNAL_DECL void duk_heap_free_heaphdr_raw(duk_heap *heap, duk_heaphdr *hdr);
519 
520 DUK_INTERNAL_DECL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
521 #if defined(DUK_USE_DOUBLE_LINKED_HEAP) && defined(DUK_USE_REFERENCE_COUNTING)
522 DUK_INTERNAL_DECL void duk_heap_remove_any_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr);
523 #endif
524 #if defined(DUK_USE_INTERRUPT_COUNTER)
525 DUK_INTERNAL_DECL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr);
526 #endif
527 
528 #if 0  /*unused*/
529 DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen);
530 #endif
531 DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern(duk_heap *heap, const duk_uint8_t *str, duk_uint32_t blen);
532 DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_checked(duk_hthread *thr, const duk_uint8_t *str, duk_uint32_t len);
533 #if 0  /*unused*/
534 DUK_INTERNAL_DECL duk_hstring *duk_heap_string_lookup_u32(duk_heap *heap, duk_uint32_t val);
535 #endif
536 DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32(duk_heap *heap, duk_uint32_t val);
537 DUK_INTERNAL_DECL duk_hstring *duk_heap_string_intern_u32_checked(duk_hthread *thr, duk_uint32_t val);
538 #if defined(DUK_USE_REFERENCE_COUNTING)
539 DUK_INTERNAL_DECL void duk_heap_string_remove(duk_heap *heap, duk_hstring *h);
540 #endif
541 #if defined(DUK_USE_MARK_AND_SWEEP) && defined(DUK_USE_MS_STRINGTABLE_RESIZE)
542 DUK_INTERNAL_DECL void duk_heap_force_strtab_resize(duk_heap *heap);
543 #endif
544 DUK_INTERNAL void duk_heap_free_strtab(duk_heap *heap);
545 #if defined(DUK_USE_DEBUG)
546 DUK_INTERNAL void duk_heap_dump_strtab(duk_heap *heap);
547 #endif
548 
549 
550 DUK_INTERNAL_DECL void duk_heap_strcache_string_remove(duk_heap *heap, duk_hstring *h);
551 DUK_INTERNAL_DECL duk_uint_fast32_t duk_heap_strcache_offset_char2byte(duk_hthread *thr, duk_hstring *h, duk_uint_fast32_t char_offset);
552 
553 #if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS)
554 DUK_INTERNAL_DECL void *duk_default_alloc_function(void *udata, duk_size_t size);
555 DUK_INTERNAL_DECL void *duk_default_realloc_function(void *udata, void *ptr, duk_size_t newsize);
556 DUK_INTERNAL_DECL void duk_default_free_function(void *udata, void *ptr);
557 #endif
558 
559 DUK_INTERNAL_DECL void *duk_heap_mem_alloc(duk_heap *heap, duk_size_t size);
560 DUK_INTERNAL_DECL void *duk_heap_mem_alloc_zeroed(duk_heap *heap, duk_size_t size);
561 DUK_INTERNAL_DECL void *duk_heap_mem_realloc(duk_heap *heap, void *ptr, duk_size_t newsize);
562 DUK_INTERNAL_DECL void *duk_heap_mem_realloc_indirect(duk_heap *heap, duk_mem_getptr cb, void *ud, duk_size_t newsize);
563 DUK_INTERNAL_DECL void duk_heap_mem_free(duk_heap *heap, void *ptr);
564 
565 #ifdef DUK_USE_REFERENCE_COUNTING
566 #if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
567 DUK_INTERNAL_DECL void duk_tval_incref(duk_tval *tv);
568 #endif
569 #if 0  /* unused */
570 DUK_INTERNAL_DECL void duk_tval_incref_allownull(duk_tval *tv);
571 #endif
572 DUK_INTERNAL_DECL void duk_tval_decref(duk_hthread *thr, duk_tval *tv);
573 #if 0  /* unused */
574 DUK_INTERNAL_DECL void duk_tval_decref_allownull(duk_hthread *thr, duk_tval *tv);
575 #endif
576 #if !defined(DUK_USE_FAST_REFCOUNT_DEFAULT)
577 DUK_INTERNAL_DECL void duk_heaphdr_incref(duk_heaphdr *h);
578 #endif
579 #if 0  /* unused */
580 DUK_INTERNAL_DECL void duk_heaphdr_incref_allownull(duk_heaphdr *h);
581 #endif
582 DUK_INTERNAL_DECL void duk_heaphdr_decref(duk_hthread *thr, duk_heaphdr *h);
583 DUK_INTERNAL_DECL void duk_heaphdr_decref_allownull(duk_hthread *thr, duk_heaphdr *h);
584 DUK_INTERNAL_DECL void duk_heaphdr_refzero(duk_hthread *thr, duk_heaphdr *h);
585 DUK_INTERNAL_DECL void duk_heaphdr_refcount_finalize(duk_hthread *thr, duk_heaphdr *hdr);
586 #else
587 /* no refcounting */
588 #endif
589 
590 #if defined(DUK_USE_MARK_AND_SWEEP)
591 DUK_INTERNAL_DECL duk_bool_t duk_heap_mark_and_sweep(duk_heap *heap, duk_small_uint_t flags);
592 #endif
593 
594 DUK_INTERNAL_DECL duk_uint32_t duk_heap_hashstring(duk_heap *heap, const duk_uint8_t *str, duk_size_t len);
595 
596 #endif  /* DUK_HEAP_H_INCLUDED */
597