2 * duk_heap allocation and freeing.
5 #include "duk_internal.h"
7 /* Constants for built-in string data depacking. */
8 #define DUK__BITPACK_LETTER_LIMIT 26
9 #define DUK__BITPACK_UNDERSCORE 26
10 #define DUK__BITPACK_FF 27
11 #define DUK__BITPACK_SWITCH1 29
12 #define DUK__BITPACK_SWITCH 30
13 #define DUK__BITPACK_SEVENBIT 31
15 #if defined(DUK_USE_ROM_STRINGS)
16 /* Fixed seed value used with ROM strings. */
17 #define DUK__FIXED_HASH_SEED 0xabcd1234
23 * Free heap object and its internal (non-heap) pointers. Assumes that
24 * caller has removed the object from heap allocated list or the string
25 * intern table, and any weak references (which strings may have) have
26 * been already dealt with.
29 DUK_INTERNAL
void duk_free_hobject_inner(duk_heap
*heap
, duk_hobject
*h
) {
30 DUK_ASSERT(heap
!= NULL
);
31 DUK_ASSERT(h
!= NULL
);
33 DUK_FREE(heap
, DUK_HOBJECT_GET_PROPS(heap
, h
));
35 if (DUK_HOBJECT_IS_COMPILEDFUNCTION(h
)) {
36 duk_hcompiledfunction
*f
= (duk_hcompiledfunction
*) h
;
38 /* Currently nothing to free; 'data' is a heap object */
39 } else if (DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
40 duk_hnativefunction
*f
= (duk_hnativefunction
*) h
;
42 /* Currently nothing to free */
43 } else if (DUK_HOBJECT_IS_THREAD(h
)) {
44 duk_hthread
*t
= (duk_hthread
*) h
;
45 DUK_FREE(heap
, t
->valstack
);
46 DUK_FREE(heap
, t
->callstack
);
47 DUK_FREE(heap
, t
->catchstack
);
48 /* Don't free h->resumer because it exists in the heap.
49 * Callstack entries also contain function pointers which
50 * are not freed for the same reason.
53 /* XXX: with 'caller' property the callstack would need
54 * to be unwound to update the 'caller' properties of
55 * functions in the callstack.
60 DUK_INTERNAL
void duk_free_hbuffer_inner(duk_heap
*heap
, duk_hbuffer
*h
) {
61 DUK_ASSERT(heap
!= NULL
);
62 DUK_ASSERT(h
!= NULL
);
64 if (DUK_HBUFFER_HAS_DYNAMIC(h
) && !DUK_HBUFFER_HAS_EXTERNAL(h
)) {
65 duk_hbuffer_dynamic
*g
= (duk_hbuffer_dynamic
*) h
;
66 DUK_DDD(DUK_DDDPRINT("free dynamic buffer %p", (void *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap
, g
)));
67 DUK_FREE(heap
, DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(heap
, g
));
71 DUK_INTERNAL
void duk_free_hstring_inner(duk_heap
*heap
, duk_hstring
*h
) {
72 DUK_ASSERT(heap
!= NULL
);
73 DUK_ASSERT(h
!= NULL
);
78 #if defined(DUK_USE_HSTRING_EXTDATA) && defined(DUK_USE_EXTSTR_FREE)
79 if (DUK_HSTRING_HAS_EXTDATA(h
)) {
80 DUK_DDD(DUK_DDDPRINT("free extstr: hstring %!O, extdata: %p",
81 h
, DUK_HSTRING_GET_EXTDATA((duk_hstring_external
*) h
)));
82 DUK_USE_EXTSTR_FREE(heap
->heap_udata
, (const void *) DUK_HSTRING_GET_EXTDATA((duk_hstring_external
*) h
));
87 DUK_INTERNAL
void duk_heap_free_heaphdr_raw(duk_heap
*heap
, duk_heaphdr
*hdr
) {
91 DUK_DDD(DUK_DDDPRINT("free heaphdr %p, htype %ld", (void *) hdr
, (long) DUK_HEAPHDR_GET_TYPE(hdr
)));
93 switch ((int) DUK_HEAPHDR_GET_TYPE(hdr
)) {
94 case DUK_HTYPE_STRING
:
95 duk_free_hstring_inner(heap
, (duk_hstring
*) hdr
);
97 case DUK_HTYPE_OBJECT
:
98 duk_free_hobject_inner(heap
, (duk_hobject
*) hdr
);
100 case DUK_HTYPE_BUFFER
:
101 duk_free_hbuffer_inner(heap
, (duk_hbuffer
*) hdr
);
113 * Frees heap-related non-heap-tracked allocations such as the
114 * string intern table; then frees the heap allocated objects;
115 * and finally frees the heap structure itself. Reference counts
116 * and GC markers are ignored (and not updated) in this process,
117 * and finalizers won't be called.
119 * The heap pointer and heap object pointers must not be used
123 DUK_LOCAL
void duk__free_allocated(duk_heap
*heap
) {
127 curr
= heap
->heap_allocated
;
129 /* We don't log or warn about freeing zero refcount objects
130 * because they may happen with finalizer processing.
133 DUK_DDD(DUK_DDDPRINT("FINALFREE (allocated): %!iO",
134 (duk_heaphdr
*) curr
));
135 next
= DUK_HEAPHDR_GET_NEXT(heap
, curr
);
136 duk_heap_free_heaphdr_raw(heap
, curr
);
141 #if defined(DUK_USE_REFERENCE_COUNTING)
142 DUK_LOCAL
void duk__free_refzero_list(duk_heap
*heap
) {
146 curr
= heap
->refzero_list
;
148 DUK_DDD(DUK_DDDPRINT("FINALFREE (refzero_list): %!iO",
149 (duk_heaphdr
*) curr
));
150 next
= DUK_HEAPHDR_GET_NEXT(heap
, curr
);
151 duk_heap_free_heaphdr_raw(heap
, curr
);
157 #if defined(DUK_USE_MARK_AND_SWEEP)
158 DUK_LOCAL
void duk__free_markandsweep_finalize_list(duk_heap
*heap
) {
162 curr
= heap
->finalize_list
;
164 DUK_DDD(DUK_DDDPRINT("FINALFREE (finalize_list): %!iO",
165 (duk_heaphdr
*) curr
));
166 next
= DUK_HEAPHDR_GET_NEXT(heap
, curr
);
167 duk_heap_free_heaphdr_raw(heap
, curr
);
173 DUK_LOCAL
void duk__free_stringtable(duk_heap
*heap
) {
174 /* strings are only tracked by stringtable */
175 duk_heap_free_strtab(heap
);
178 DUK_LOCAL
void duk__free_run_finalizers(duk_heap
*heap
) {
182 duk_size_t count_all
;
183 duk_size_t count_finalized
;
184 duk_size_t curr_limit
;
186 DUK_ASSERT(heap
!= NULL
);
187 DUK_ASSERT(heap
->heap_thread
!= NULL
);
189 #if defined(DUK_USE_REFERENCE_COUNTING)
190 DUK_ASSERT(heap
->refzero_list
== NULL
); /* refzero not running -> must be empty */
192 #if defined(DUK_USE_MARK_AND_SWEEP)
193 DUK_ASSERT(heap
->finalize_list
== NULL
); /* mark-and-sweep not running -> must be empty */
196 /* XXX: here again finalizer thread is the heap_thread which needs
197 * to be coordinated with finalizer thread fixes.
199 thr
= heap
->heap_thread
;
200 DUK_ASSERT(thr
!= NULL
);
202 /* Prevent mark-and-sweep for the pending finalizers, also prevents
203 * refzero handling from moving objects away from the heap_allocated
204 * list. (The flag meaning is slightly abused here.)
206 DUK_ASSERT(!DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap
));
207 DUK_HEAP_SET_MARKANDSWEEP_RUNNING(heap
);
209 curr_limit
= 0; /* suppress warning, not used */
210 for (round_no
= 0; ; round_no
++) {
211 curr
= heap
->heap_allocated
;
216 if (DUK_HEAPHDR_GET_TYPE(curr
) == DUK_HTYPE_OBJECT
) {
217 /* Only objects in heap_allocated may have finalizers. Check that
218 * the object itself has a _Finalizer property (own or inherited)
219 * so that we don't execute finalizers for e.g. Proxy objects.
221 DUK_ASSERT(thr
!= NULL
);
222 DUK_ASSERT(curr
!= NULL
);
224 if (duk_hobject_hasprop_raw(thr
, (duk_hobject
*) curr
, DUK_HTHREAD_STRING_INT_FINALIZER(thr
))) {
225 if (!DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr
*) curr
)) {
226 DUK_ASSERT(DUK_HEAP_HAS_FINALIZER_NORESCUE(heap
)); /* maps to finalizer 2nd argument */
227 duk_hobject_run_finalizer(thr
, (duk_hobject
*) curr
);
232 curr
= DUK_HEAPHDR_GET_NEXT(heap
, curr
);
235 /* Each round of finalizer execution may spawn new finalizable objects
236 * which is normal behavior for some applications. Allow multiple
237 * rounds of finalization, but use a shrinking limit based on the
238 * first round to detect the case where a runaway finalizer creates
239 * an unbounded amount of new finalizable objects. Finalizer rescue
240 * is not supported: the semantics are unclear because most of the
241 * objects being finalized here are already reachable. The finalizer
242 * is given a boolean to indicate that rescue is not possible.
244 * See discussion in: https://github.com/svaarala/duktape/pull/473
248 /* Cannot wrap: each object is at least 8 bytes so count is
249 * at most 1/8 of that.
251 curr_limit
= count_all
* 2;
253 curr_limit
= (curr_limit
* 3) / 4; /* Decrease by 25% every round */
255 DUK_D(DUK_DPRINT("finalizer round %ld complete, %ld objects, tried to execute %ld finalizers, current limit is %ld",
256 (long) round_no
, (long) count_all
, (long) count_finalized
, (long) curr_limit
));
258 if (count_finalized
== 0) {
259 DUK_D(DUK_DPRINT("no more finalizable objects, forced finalization finished"));
262 if (count_finalized
>= curr_limit
) {
263 DUK_D(DUK_DPRINT("finalizer count above limit, potentially runaway finalizer; skip remaining finalizers"));
268 DUK_ASSERT(DUK_HEAP_HAS_MARKANDSWEEP_RUNNING(heap
));
269 DUK_HEAP_CLEAR_MARKANDSWEEP_RUNNING(heap
);
272 DUK_INTERNAL
void duk_heap_free(duk_heap
*heap
) {
273 DUK_D(DUK_DPRINT("free heap: %p", (void *) heap
));
275 #if defined(DUK_USE_DEBUG)
276 duk_heap_dump_strtab(heap
);
279 #if defined(DUK_USE_DEBUGGER_SUPPORT)
280 /* Detach a debugger if attached (can be called multiple times)
283 /* XXX: Add a flag to reject an attempt to re-attach? Otherwise
284 * the detached callback may immediately reattach.
286 duk_debug_do_detach(heap
);
289 /* Execute finalizers before freeing the heap, even for reachable
290 * objects, and regardless of whether or not mark-and-sweep is
291 * enabled. This gives finalizers the chance to free any native
292 * resources like file handles, allocations made outside Duktape,
293 * etc. This is quite tricky to get right, so that all finalizer
294 * guarantees are honored.
296 * XXX: this perhaps requires an execution time limit.
298 DUK_D(DUK_DPRINT("execute finalizers before freeing heap"));
299 #if defined(DUK_USE_MARK_AND_SWEEP)
300 /* Run mark-and-sweep a few times just in case (unreachable object
301 * finalizers run already here). The last round must rescue objects
302 * from the previous round without running any more finalizers. This
303 * ensures rescued objects get their FINALIZED flag cleared so that
304 * their finalizer is called once more in forced finalization to
305 * satisfy finalizer guarantees. However, we don't want to run any
306 * more finalizer because that'd required one more loop, and so on.
308 DUK_D(DUK_DPRINT("forced gc #1 in heap destruction"));
309 duk_heap_mark_and_sweep(heap
, 0);
310 DUK_D(DUK_DPRINT("forced gc #2 in heap destruction"));
311 duk_heap_mark_and_sweep(heap
, 0);
312 DUK_D(DUK_DPRINT("forced gc #3 in heap destruction (don't run finalizers)"));
313 duk_heap_mark_and_sweep(heap
, DUK_MS_FLAG_SKIP_FINALIZERS
); /* skip finalizers; queue finalizable objects to heap_allocated */
316 DUK_HEAP_SET_FINALIZER_NORESCUE(heap
); /* rescue no longer supported */
317 duk__free_run_finalizers(heap
);
319 /* Note: heap->heap_thread, heap->curr_thread, and heap->heap_object
320 * are on the heap allocated list.
323 DUK_D(DUK_DPRINT("freeing heap objects of heap: %p", (void *) heap
));
324 duk__free_allocated(heap
);
326 #if defined(DUK_USE_REFERENCE_COUNTING)
327 DUK_D(DUK_DPRINT("freeing refzero list of heap: %p", (void *) heap
));
328 duk__free_refzero_list(heap
);
331 #if defined(DUK_USE_MARK_AND_SWEEP)
332 DUK_D(DUK_DPRINT("freeing mark-and-sweep finalize list of heap: %p", (void *) heap
));
333 duk__free_markandsweep_finalize_list(heap
);
336 DUK_D(DUK_DPRINT("freeing string table of heap: %p", (void *) heap
));
337 duk__free_stringtable(heap
);
339 DUK_D(DUK_DPRINT("freeing heap structure: %p", (void *) heap
));
340 heap
->free_func(heap
->heap_udata
, heap
);
346 * String table is initialized with built-in strings from genbuiltins.py,
347 * either by dynamically creating the strings or by referring to ROM strings.
350 #if defined(DUK_USE_ROM_STRINGS)
351 DUK_LOCAL duk_bool_t
duk__init_heap_strings(duk_heap
*heap
) {
352 #if defined(DUK_USE_ASSERTIONS)
356 /* With ROM-based strings, heap->strs[] and thr->strs[] are omitted
357 * so nothing to initialize for strs[].
360 #if defined(DUK_USE_ASSERTIONS)
361 for (i
= 0; i
< sizeof(duk_rom_strings
) / sizeof(const duk_hstring
*); i
++) {
363 const duk_hstring
*h
;
364 h
= duk_rom_strings
[i
];
365 DUK_ASSERT(h
!= NULL
);
366 hash
= duk_heap_hashstring(heap
, (const duk_uint8_t
*) DUK_HSTRING_GET_DATA(h
), DUK_HSTRING_GET_BYTELEN(h
));
367 DUK_DD(DUK_DDPRINT("duk_rom_strings[%d] -> hash 0x%08lx, computed 0x%08lx",
368 (int) i
, (unsigned long) DUK_HSTRING_GET_HASH(h
), (unsigned long) hash
));
369 DUK_ASSERT(hash
== (duk_uint32_t
) DUK_HSTRING_GET_HASH(h
));
374 #else /* DUK_USE_ROM_STRINGS */
375 DUK_LOCAL duk_bool_t
duk__init_heap_strings(duk_heap
*heap
) {
376 duk_bitdecoder_ctx bd_ctx
;
377 duk_bitdecoder_ctx
*bd
= &bd_ctx
; /* convenience */
378 duk_small_uint_t i
, j
;
380 DUK_MEMZERO(&bd_ctx
, sizeof(bd_ctx
));
381 bd
->data
= (const duk_uint8_t
*) duk_strings_data
;
382 bd
->length
= (duk_size_t
) DUK_STRDATA_DATA_LENGTH
;
384 for (i
= 0; i
< DUK_HEAP_NUM_STRINGS
; i
++) {
385 duk_uint8_t tmp
[DUK_STRDATA_MAX_STRLEN
];
387 duk_small_uint_t len
;
388 duk_small_uint_t mode
;
391 len
= duk_bd_decode(bd
, 5);
392 mode
= 32; /* 0 = uppercase, 32 = lowercase (= 'a' - 'A') */
393 for (j
= 0; j
< len
; j
++) {
394 t
= duk_bd_decode(bd
, 5);
395 if (t
< DUK__BITPACK_LETTER_LIMIT
) {
396 t
= t
+ DUK_ASC_UC_A
+ mode
;
397 } else if (t
== DUK__BITPACK_UNDERSCORE
) {
398 t
= DUK_ASC_UNDERSCORE
;
399 } else if (t
== DUK__BITPACK_FF
) {
400 /* Internal keys are prefixed with 0xFF in the stringtable
401 * (which makes them invalid UTF-8 on purpose).
404 } else if (t
== DUK__BITPACK_SWITCH1
) {
405 t
= duk_bd_decode(bd
, 5);
406 DUK_ASSERT_DISABLE(t
>= 0); /* unsigned */
408 t
= t
+ DUK_ASC_UC_A
+ (mode
^ 32);
409 } else if (t
== DUK__BITPACK_SWITCH
) {
411 t
= duk_bd_decode(bd
, 5);
412 DUK_ASSERT_DISABLE(t
>= 0);
414 t
= t
+ DUK_ASC_UC_A
+ mode
;
415 } else if (t
== DUK__BITPACK_SEVENBIT
) {
416 t
= duk_bd_decode(bd
, 7);
418 tmp
[j
] = (duk_uint8_t
) t
;
421 /* No need to length check string: it will never exceed even
422 * the 16-bit length maximum.
424 DUK_ASSERT(len
<= 0xffffUL
);
425 DUK_DDD(DUK_DDDPRINT("intern built-in string %ld", (long) i
));
426 h
= duk_heap_string_intern(heap
, tmp
, len
);
430 DUK_ASSERT(!DUK_HEAPHDR_HAS_READONLY((duk_heaphdr
*) h
));
432 /* Special flags checks. Since these strings are always
433 * reachable and a string cannot appear twice in the string
434 * table, there's no need to check/set these flags elsewhere.
435 * The 'internal' flag is set by string intern code.
437 if (i
== DUK_STRIDX_EVAL
|| i
== DUK_STRIDX_LC_ARGUMENTS
) {
438 DUK_HSTRING_SET_EVAL_OR_ARGUMENTS(h
);
440 if (i
>= DUK_STRIDX_START_RESERVED
&& i
< DUK_STRIDX_END_RESERVED
) {
441 DUK_HSTRING_SET_RESERVED_WORD(h
);
442 if (i
>= DUK_STRIDX_START_STRICT_RESERVED
) {
443 DUK_HSTRING_SET_STRICT_RESERVED_WORD(h
);
447 DUK_DDD(DUK_DDDPRINT("interned: %!O", (duk_heaphdr
*) h
));
449 /* XXX: The incref macro takes a thread pointer but doesn't
452 DUK_HSTRING_INCREF(_never_referenced_
, h
);
454 #if defined(DUK_USE_HEAPPTR16)
455 heap
->strs16
[i
] = DUK_USE_HEAPPTR_ENC16(heap
->heap_udata
, (void *) h
);
466 #endif /* DUK_USE_ROM_STRINGS */
468 DUK_LOCAL duk_bool_t
duk__init_heap_thread(duk_heap
*heap
) {
471 DUK_DD(DUK_DDPRINT("heap init: alloc heap thread"));
472 thr
= duk_hthread_alloc(heap
,
473 DUK_HOBJECT_FLAG_EXTENSIBLE
|
474 DUK_HOBJECT_FLAG_THREAD
|
475 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD
));
477 DUK_D(DUK_DPRINT("failed to alloc heap_thread"));
480 thr
->state
= DUK_HTHREAD_STATE_INACTIVE
;
481 #if defined(DUK_USE_ROM_STRINGS)
482 /* No strs[] pointer. */
483 #else /* DUK_USE_ROM_STRINGS */
484 #if defined(DUK_USE_HEAPPTR16)
485 thr
->strs16
= heap
->strs16
;
487 thr
->strs
= heap
->strs
;
489 #endif /* DUK_USE_ROM_STRINGS */
491 heap
->heap_thread
= thr
;
492 DUK_HTHREAD_INCREF(thr
, thr
); /* Note: first argument not really used */
494 /* 'thr' is now reachable */
496 if (!duk_hthread_init_stacks(heap
, thr
)) {
500 /* XXX: this may now fail, and is not handled correctly */
501 duk_hthread_create_builtin_objects(thr
);
503 /* default prototype (Note: 'thr' must be reachable) */
504 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) thr
, thr
->builtins
[DUK_BIDX_THREAD_PROTOTYPE
]);
509 #if defined(DUK_USE_DEBUG)
510 #define DUK__DUMPSZ(t) do { \
511 DUK_D(DUK_DPRINT("" #t "=%ld", (long) sizeof(t))); \
514 /* These is not 100% because format would need to be non-portable "long long".
515 * Also print out as doubles to catch cases where the "long" type is not wide
516 * enough; the limits will then not be printed accurately but the magnitude
519 #define DUK__DUMPLM_SIGNED_RAW(t,a,b) do { \
520 DUK_D(DUK_DPRINT(t "=[%ld,%ld]=[%lf,%lf]", \
521 (long) (a), (long) (b), \
522 (double) (a), (double) (b))); \
524 #define DUK__DUMPLM_UNSIGNED_RAW(t,a,b) do { \
525 DUK_D(DUK_DPRINT(t "=[%lu,%lu]=[%lf,%lf]", \
526 (unsigned long) (a), (unsigned long) (b), \
527 (double) (a), (double) (b))); \
529 #define DUK__DUMPLM_SIGNED(t) do { \
530 DUK__DUMPLM_SIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \
532 #define DUK__DUMPLM_UNSIGNED(t) do { \
533 DUK__DUMPLM_UNSIGNED_RAW("DUK_" #t "_{MIN,MAX}", DUK_##t##_MIN, DUK_##t##_MAX); \
536 DUK_LOCAL
void duk__dump_type_sizes(void) {
537 DUK_D(DUK_DPRINT("sizeof()"));
539 /* basic platform types */
548 /* basic types from duk_features.h */
549 DUK__DUMPSZ(duk_uint8_t
);
550 DUK__DUMPSZ(duk_int8_t
);
551 DUK__DUMPSZ(duk_uint16_t
);
552 DUK__DUMPSZ(duk_int16_t
);
553 DUK__DUMPSZ(duk_uint32_t
);
554 DUK__DUMPSZ(duk_int32_t
);
555 DUK__DUMPSZ(duk_uint64_t
);
556 DUK__DUMPSZ(duk_int64_t
);
557 DUK__DUMPSZ(duk_uint_least8_t
);
558 DUK__DUMPSZ(duk_int_least8_t
);
559 DUK__DUMPSZ(duk_uint_least16_t
);
560 DUK__DUMPSZ(duk_int_least16_t
);
561 DUK__DUMPSZ(duk_uint_least32_t
);
562 DUK__DUMPSZ(duk_int_least32_t
);
563 #if defined(DUK_USE_64BIT_OPS)
564 DUK__DUMPSZ(duk_uint_least64_t
);
565 DUK__DUMPSZ(duk_int_least64_t
);
567 DUK__DUMPSZ(duk_uint_fast8_t
);
568 DUK__DUMPSZ(duk_int_fast8_t
);
569 DUK__DUMPSZ(duk_uint_fast16_t
);
570 DUK__DUMPSZ(duk_int_fast16_t
);
571 DUK__DUMPSZ(duk_uint_fast32_t
);
572 DUK__DUMPSZ(duk_int_fast32_t
);
573 #if defined(DUK_USE_64BIT_OPS)
574 DUK__DUMPSZ(duk_uint_fast64_t
);
575 DUK__DUMPSZ(duk_int_fast64_t
);
577 DUK__DUMPSZ(duk_uintptr_t
);
578 DUK__DUMPSZ(duk_intptr_t
);
579 DUK__DUMPSZ(duk_uintmax_t
);
580 DUK__DUMPSZ(duk_intmax_t
);
581 DUK__DUMPSZ(duk_double_t
);
583 /* important chosen base types */
584 DUK__DUMPSZ(duk_int_t
);
585 DUK__DUMPSZ(duk_uint_t
);
586 DUK__DUMPSZ(duk_int_fast_t
);
587 DUK__DUMPSZ(duk_uint_fast_t
);
588 DUK__DUMPSZ(duk_small_int_t
);
589 DUK__DUMPSZ(duk_small_uint_t
);
590 DUK__DUMPSZ(duk_small_int_fast_t
);
591 DUK__DUMPSZ(duk_small_uint_fast_t
);
593 /* some derived types */
594 DUK__DUMPSZ(duk_codepoint_t
);
595 DUK__DUMPSZ(duk_ucodepoint_t
);
596 DUK__DUMPSZ(duk_idx_t
);
597 DUK__DUMPSZ(duk_errcode_t
);
598 DUK__DUMPSZ(duk_uarridx_t
);
601 DUK__DUMPSZ(duk_double_union
);
602 DUK__DUMPSZ(duk_tval
);
604 /* structs from duk_forwdecl.h */
605 DUK__DUMPSZ(duk_jmpbuf
); /* just one 'int' for C++ exceptions */
606 DUK__DUMPSZ(duk_heaphdr
);
607 DUK__DUMPSZ(duk_heaphdr_string
);
608 DUK__DUMPSZ(duk_hstring
);
609 DUK__DUMPSZ(duk_hstring_external
);
610 DUK__DUMPSZ(duk_hobject
);
611 DUK__DUMPSZ(duk_hcompiledfunction
);
612 DUK__DUMPSZ(duk_hnativefunction
);
613 DUK__DUMPSZ(duk_hthread
);
614 DUK__DUMPSZ(duk_hbuffer
);
615 DUK__DUMPSZ(duk_hbuffer_fixed
);
616 DUK__DUMPSZ(duk_hbuffer_dynamic
);
617 DUK__DUMPSZ(duk_hbuffer_external
);
618 DUK__DUMPSZ(duk_propaccessor
);
619 DUK__DUMPSZ(duk_propvalue
);
620 DUK__DUMPSZ(duk_propdesc
);
621 DUK__DUMPSZ(duk_heap
);
622 #if defined(DUK_USE_STRTAB_CHAIN)
623 DUK__DUMPSZ(duk_strtab_entry
);
625 DUK__DUMPSZ(duk_activation
);
626 DUK__DUMPSZ(duk_catcher
);
627 DUK__DUMPSZ(duk_strcache
);
628 DUK__DUMPSZ(duk_ljstate
);
629 DUK__DUMPSZ(duk_fixedbuffer
);
630 DUK__DUMPSZ(duk_bitdecoder_ctx
);
631 DUK__DUMPSZ(duk_bitencoder_ctx
);
632 DUK__DUMPSZ(duk_token
);
633 DUK__DUMPSZ(duk_re_token
);
634 DUK__DUMPSZ(duk_lexer_point
);
635 DUK__DUMPSZ(duk_lexer_ctx
);
636 DUK__DUMPSZ(duk_compiler_instr
);
637 DUK__DUMPSZ(duk_compiler_func
);
638 DUK__DUMPSZ(duk_compiler_ctx
);
639 DUK__DUMPSZ(duk_re_matcher_ctx
);
640 DUK__DUMPSZ(duk_re_compiler_ctx
);
642 DUK_LOCAL
void duk__dump_type_limits(void) {
643 DUK_D(DUK_DPRINT("limits"));
646 DUK__DUMPLM_SIGNED(INT8
);
647 DUK__DUMPLM_UNSIGNED(UINT8
);
648 DUK__DUMPLM_SIGNED(INT_FAST8
);
649 DUK__DUMPLM_UNSIGNED(UINT_FAST8
);
650 DUK__DUMPLM_SIGNED(INT_LEAST8
);
651 DUK__DUMPLM_UNSIGNED(UINT_LEAST8
);
652 DUK__DUMPLM_SIGNED(INT16
);
653 DUK__DUMPLM_UNSIGNED(UINT16
);
654 DUK__DUMPLM_SIGNED(INT_FAST16
);
655 DUK__DUMPLM_UNSIGNED(UINT_FAST16
);
656 DUK__DUMPLM_SIGNED(INT_LEAST16
);
657 DUK__DUMPLM_UNSIGNED(UINT_LEAST16
);
658 DUK__DUMPLM_SIGNED(INT32
);
659 DUK__DUMPLM_UNSIGNED(UINT32
);
660 DUK__DUMPLM_SIGNED(INT_FAST32
);
661 DUK__DUMPLM_UNSIGNED(UINT_FAST32
);
662 DUK__DUMPLM_SIGNED(INT_LEAST32
);
663 DUK__DUMPLM_UNSIGNED(UINT_LEAST32
);
664 #if defined(DUK_USE_64BIT_OPS)
665 DUK__DUMPLM_SIGNED(INT64
);
666 DUK__DUMPLM_UNSIGNED(UINT64
);
667 DUK__DUMPLM_SIGNED(INT_FAST64
);
668 DUK__DUMPLM_UNSIGNED(UINT_FAST64
);
669 DUK__DUMPLM_SIGNED(INT_LEAST64
);
670 DUK__DUMPLM_UNSIGNED(UINT_LEAST64
);
672 DUK__DUMPLM_SIGNED(INTPTR
);
673 DUK__DUMPLM_UNSIGNED(UINTPTR
);
674 DUK__DUMPLM_SIGNED(INTMAX
);
675 DUK__DUMPLM_UNSIGNED(UINTMAX
);
678 DUK__DUMPLM_SIGNED(INT
);
679 DUK__DUMPLM_UNSIGNED(UINT
);
680 DUK__DUMPLM_SIGNED(INT_FAST
);
681 DUK__DUMPLM_UNSIGNED(UINT_FAST
);
682 DUK__DUMPLM_SIGNED(SMALL_INT
);
683 DUK__DUMPLM_UNSIGNED(SMALL_UINT
);
684 DUK__DUMPLM_SIGNED(SMALL_INT_FAST
);
685 DUK__DUMPLM_UNSIGNED(SMALL_UINT_FAST
);
688 #undef DUK__DUMPLM_SIGNED_RAW
689 #undef DUK__DUMPLM_UNSIGNED_RAW
690 #undef DUK__DUMPLM_SIGNED
691 #undef DUK__DUMPLM_UNSIGNED
693 DUK_LOCAL
void duk__dump_misc_options(void) {
694 DUK_D(DUK_DPRINT("DUK_VERSION: %ld", (long) DUK_VERSION
));
695 DUK_D(DUK_DPRINT("DUK_GIT_DESCRIBE: %s", DUK_GIT_DESCRIBE
));
696 DUK_D(DUK_DPRINT("OS string: %s", DUK_USE_OS_STRING
));
697 DUK_D(DUK_DPRINT("architecture string: %s", DUK_USE_ARCH_STRING
));
698 DUK_D(DUK_DPRINT("compiler string: %s", DUK_USE_COMPILER_STRING
));
699 #if defined(DUK_USE_PACKED_TVAL)
700 DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: yes"));
702 DUK_D(DUK_DPRINT("DUK_USE_PACKED_TVAL: no"));
704 #if defined(DUK_USE_VARIADIC_MACROS)
705 DUK_D(DUK_DPRINT("DUK_USE_VARIADIC_MACROS: yes"));
707 DUK_D(DUK_DPRINT("DUK_USE_VARIADIC_MACROS: no"));
709 #if defined(DUK_USE_INTEGER_LE)
710 DUK_D(DUK_DPRINT("integer endianness: little"));
711 #elif defined(DUK_USE_INTEGER_ME)
712 DUK_D(DUK_DPRINT("integer endianness: mixed"));
713 #elif defined(DUK_USE_INTEGER_BE)
714 DUK_D(DUK_DPRINT("integer endianness: big"));
716 DUK_D(DUK_DPRINT("integer endianness: ???"));
718 #if defined(DUK_USE_DOUBLE_LE)
719 DUK_D(DUK_DPRINT("IEEE double endianness: little"));
720 #elif defined(DUK_USE_DOUBLE_ME)
721 DUK_D(DUK_DPRINT("IEEE double endianness: mixed"));
722 #elif defined(DUK_USE_DOUBLE_BE)
723 DUK_D(DUK_DPRINT("IEEE double endianness: big"));
725 DUK_D(DUK_DPRINT("IEEE double endianness: ???"));
728 #endif /* DUK_USE_DEBUG */
731 duk_heap
*duk_heap_alloc(duk_alloc_function alloc_func
,
732 duk_realloc_function realloc_func
,
733 duk_free_function free_func
,
735 duk_fatal_function fatal_func
) {
736 duk_heap
*res
= NULL
;
738 /* Silence a few global unused warnings here. */
739 DUK_UNREF(duk_str_unsupported
);
741 DUK_D(DUK_DPRINT("allocate heap"));
744 * Debug dump type sizes
747 #if defined(DUK_USE_DEBUG)
748 duk__dump_misc_options();
749 duk__dump_type_sizes();
750 duk__dump_type_limits();
754 * If selftests enabled, run them as early as possible
756 #if defined(DUK_USE_SELF_TESTS)
757 DUK_D(DUK_DPRINT("running self tests"));
758 duk_selftest_run_tests();
759 DUK_D(DUK_DPRINT("self tests passed"));
763 * Computed values (e.g. INFINITY)
766 #if defined(DUK_USE_COMPUTED_NAN)
768 /* Workaround for some exotic platforms where NAN is missing
769 * and the expression (0.0 / 0.0) does NOT result in a NaN.
770 * Such platforms use the global 'duk_computed_nan' which must
771 * be initialized at runtime. Use 'volatile' to ensure that
772 * the compiler will actually do the computation and not try
773 * to do constant folding which might result in the original
776 volatile double dbl1
= 0.0;
777 volatile double dbl2
= 0.0;
778 duk_computed_nan
= dbl1
/ dbl2
;
782 #if defined(DUK_USE_COMPUTED_INFINITY)
784 /* Similar workaround for INFINITY. */
785 volatile double dbl1
= 1.0;
786 volatile double dbl2
= 0.0;
787 duk_computed_infinity
= dbl1
/ dbl2
;
792 * Allocate heap struct
794 * Use a raw call, all macros expect the heap to be initialized
797 res
= (duk_heap
*) alloc_func(heap_udata
, sizeof(duk_heap
));
803 * Zero the struct, and start initializing roughly in order
806 DUK_MEMZERO(res
, sizeof(*res
));
808 /* explicit NULL inits */
809 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
810 res
->heap_udata
= NULL
;
811 res
->heap_allocated
= NULL
;
812 #if defined(DUK_USE_REFERENCE_COUNTING)
813 res
->refzero_list
= NULL
;
814 res
->refzero_list_tail
= NULL
;
816 #if defined(DUK_USE_MARK_AND_SWEEP)
817 res
->finalize_list
= NULL
;
819 res
->heap_thread
= NULL
;
820 res
->curr_thread
= NULL
;
821 res
->heap_object
= NULL
;
822 #if defined(DUK_USE_STRTAB_CHAIN)
823 /* nothing to NULL */
824 #elif defined(DUK_USE_STRTAB_PROBE)
825 #if defined(DUK_USE_HEAPPTR16)
826 res
->strtable16
= (duk_uint16_t
*) NULL
;
828 res
->strtable
= (duk_hstring
**) NULL
;
831 #if defined(DUK_USE_ROM_STRINGS)
833 #else /* DUK_USE_ROM_STRINGS */
834 #if defined(DUK_USE_HEAPPTR16)
835 /* res->strs16[] is zeroed and zero decodes to NULL, so no NULL inits. */
839 for (i
= 0; i
< DUK_HEAP_NUM_STRINGS
; i
++) {
844 #endif /* DUK_USE_ROM_STRINGS */
845 #if defined(DUK_USE_DEBUGGER_SUPPORT)
846 res
->dbg_read_cb
= NULL
;
847 res
->dbg_write_cb
= NULL
;
848 res
->dbg_peek_cb
= NULL
;
849 res
->dbg_read_flush_cb
= NULL
;
850 res
->dbg_write_flush_cb
= NULL
;
851 res
->dbg_request_cb
= NULL
;
852 res
->dbg_udata
= NULL
;
853 res
->dbg_step_thread
= NULL
;
855 #endif /* DUK_USE_EXPLICIT_NULL_INIT */
857 res
->alloc_func
= alloc_func
;
858 res
->realloc_func
= realloc_func
;
859 res
->free_func
= free_func
;
860 res
->heap_udata
= heap_udata
;
861 res
->fatal_func
= fatal_func
;
863 #if defined(DUK_USE_HEAPPTR16)
864 /* XXX: zero assumption */
865 res
->heapptr_null16
= DUK_USE_HEAPPTR_ENC16(res
->heap_udata
, (void *) NULL
);
866 res
->heapptr_deleted16
= DUK_USE_HEAPPTR_ENC16(res
->heap_udata
, (void *) DUK_STRTAB_DELETED_MARKER(res
));
869 /* res->mark_and_sweep_trigger_counter == 0 -> now causes immediate GC; which is OK */
871 res
->call_recursion_depth
= 0;
872 res
->call_recursion_limit
= DUK_USE_NATIVE_CALL_RECLIMIT
;
874 /* XXX: use the pointer as a seed for now: mix in time at least */
876 /* The casts through duk_intr_pt is to avoid the following GCC warning:
878 * warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
880 * This still generates a /Wp64 warning on VS2010 when compiling for x86.
882 #if defined(DUK_USE_ROM_STRINGS)
883 /* XXX: make a common DUK_USE_ option, and allow custom fixed seed? */
884 DUK_D(DUK_DPRINT("using rom strings, force heap hash_seed to fixed value 0x%08lx", (long) DUK__FIXED_HASH_SEED
));
885 res
->hash_seed
= (duk_uint32_t
) DUK__FIXED_HASH_SEED
;
886 #else /* DUK_USE_ROM_STRINGS */
887 res
->hash_seed
= (duk_uint32_t
) (duk_intptr_t
) res
;
888 #if !defined(DUK_USE_STRHASH_DENSE)
889 res
->hash_seed
^= 5381; /* Bernstein hash init value is normally 5381; XOR it in in case pointer low bits are 0 */
891 #endif /* DUK_USE_ROM_STRINGS */
892 res
->rnd_state
= (duk_uint32_t
) (duk_intptr_t
) res
;
894 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
895 res
->lj
.jmpbuf_ptr
= NULL
;
897 DUK_ASSERT(res
->lj
.type
== DUK_LJ_TYPE_UNKNOWN
); /* zero */
899 DUK_TVAL_SET_UNDEFINED(&res
->lj
.value1
);
900 DUK_TVAL_SET_UNDEFINED(&res
->lj
.value2
);
902 #if (DUK_STRTAB_INITIAL_SIZE < DUK_UTIL_MIN_HASH_PRIME)
903 #error initial heap stringtable size is defined incorrectly
907 * Init stringtable: fixed variant
910 #if defined(DUK_USE_STRTAB_CHAIN)
911 DUK_MEMZERO(res
->strtable
, sizeof(duk_strtab_entry
) * DUK_STRTAB_CHAIN_SIZE
);
912 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
915 for (i
= 0; i
< DUK_STRTAB_CHAIN_SIZE
; i
++) {
916 #if defined(DUK_USE_HEAPPTR16)
917 res
->strtable
[i
].u
.str16
= res
->heapptr_null16
;
919 res
->strtable
[i
].u
.str
= NULL
;
923 #endif /* DUK_USE_EXPLICIT_NULL_INIT */
924 #endif /* DUK_USE_STRTAB_CHAIN */
927 * Init stringtable: probe variant
930 #if defined(DUK_USE_STRTAB_PROBE)
931 #if defined(DUK_USE_HEAPPTR16)
932 res
->strtable16
= (duk_uint16_t
*) alloc_func(heap_udata
, sizeof(duk_uint16_t
) * DUK_STRTAB_INITIAL_SIZE
);
933 if (!res
->strtable16
) {
936 #else /* DUK_USE_HEAPPTR16 */
937 res
->strtable
= (duk_hstring
**) alloc_func(heap_udata
, sizeof(duk_hstring
*) * DUK_STRTAB_INITIAL_SIZE
);
938 if (!res
->strtable
) {
941 #endif /* DUK_USE_HEAPPTR16 */
942 res
->st_size
= DUK_STRTAB_INITIAL_SIZE
;
943 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
946 DUK_ASSERT(res
->st_size
== DUK_STRTAB_INITIAL_SIZE
);
947 for (i
= 0; i
< DUK_STRTAB_INITIAL_SIZE
; i
++) {
948 #if defined(DUK_USE_HEAPPTR16)
949 res
->strtable16
[i
] = res
->heapptr_null16
;
951 res
->strtable
[i
] = NULL
;
955 #else /* DUK_USE_EXPLICIT_NULL_INIT */
956 #if defined(DUK_USE_HEAPPTR16)
957 DUK_MEMZERO(res
->strtable16
, sizeof(duk_uint16_t
) * DUK_STRTAB_INITIAL_SIZE
);
959 DUK_MEMZERO(res
->strtable
, sizeof(duk_hstring
*) * DUK_STRTAB_INITIAL_SIZE
);
961 #endif /* DUK_USE_EXPLICIT_NULL_INIT */
962 #endif /* DUK_USE_STRTAB_PROBE */
968 #if defined(DUK_USE_EXPLICIT_NULL_INIT)
971 for (i
= 0; i
< DUK_HEAP_STRCACHE_SIZE
; i
++) {
972 res
->strcache
[i
].h
= NULL
;
977 /* XXX: error handling is incomplete. It would be cleanest if
978 * there was a setjmp catchpoint, so that all init code could
979 * freely throw errors. If that were the case, the return code
980 * passing here could be removed.
984 * Init built-in strings
987 DUK_DD(DUK_DDPRINT("HEAP: INIT STRINGS"));
988 if (!duk__init_heap_strings(res
)) {
993 * Init the heap thread
996 DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP THREAD"));
997 if (!duk__init_heap_thread(res
)) {
1001 DUK_ASSERT(res
->heap_thread
!= NULL
);
1002 res
->rnd_state
^= (duk_uint32_t
) DUK_USE_DATE_GET_NOW((duk_context
*) res
->heap_thread
);
1005 * Init the heap object
1008 DUK_DD(DUK_DDPRINT("HEAP: INIT HEAP OBJECT"));
1009 DUK_ASSERT(res
->heap_thread
!= NULL
);
1010 res
->heap_object
= duk_hobject_alloc(res
, DUK_HOBJECT_FLAG_EXTENSIBLE
|
1011 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT
));
1012 if (!res
->heap_object
) {
1015 DUK_HOBJECT_INCREF(res
->heap_thread
, res
->heap_object
);
1021 DUK_D(DUK_DPRINT("allocated heap: %p", (void *) res
));
1025 DUK_D(DUK_DPRINT("heap allocation failed"));
1028 /* assumes that allocated pointers and alloc funcs are valid
1031 DUK_ASSERT(res
->alloc_func
!= NULL
);
1032 DUK_ASSERT(res
->realloc_func
!= NULL
);
1033 DUK_ASSERT(res
->free_func
!= NULL
);
1039 #undef DUK__BITPACK_LETTER_LIMIT
1040 #undef DUK__BITPACK_UNDERSCORE
1041 #undef DUK__BITPACK_FF
1042 #undef DUK__BITPACK_SWITCH1
1043 #undef DUK__BITPACK_SWITCH
1044 #undef DUK__BITPACK_SEVENBIT
1045 #undef DUK__FIXED_HASH_SEED