/* * Support functions for duk_heap. */ #include "duk_internal.h" DUK_INTERNAL void duk_heap_insert_into_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { duk_heaphdr *root; DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); root = heap->heap_allocated; #if defined(DUK_USE_DOUBLE_LINKED_HEAP) if (root != NULL) { DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); DUK_HEAPHDR_SET_PREV(heap, root, hdr); } DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); #endif DUK_HEAPHDR_SET_NEXT(heap, hdr, root); DUK_HEAPHDR_ASSERT_LINKS(heap, hdr); DUK_HEAPHDR_ASSERT_LINKS(heap, root); heap->heap_allocated = hdr; } #if defined(DUK_USE_REFERENCE_COUNTING) DUK_INTERNAL void duk_heap_remove_from_heap_allocated(duk_heap *heap, duk_heaphdr *hdr) { duk_heaphdr *prev; duk_heaphdr *next; /* Strings are in string table. */ DUK_ASSERT(hdr != NULL); DUK_ASSERT(DUK_HEAPHDR_GET_TYPE(hdr) != DUK_HTYPE_STRING); /* Target 'hdr' must be in heap_allocated (not e.g. finalize_list). * If not, heap lists will become corrupted so assert early for it. */ #if defined(DUK_USE_ASSERTIONS) { duk_heaphdr *tmp; for (tmp = heap->heap_allocated; tmp != NULL; tmp = DUK_HEAPHDR_GET_NEXT(heap, tmp)) { if (tmp == hdr) { break; } } DUK_ASSERT(tmp == hdr); } #endif /* Read/write only once to minimize pointer compression calls. */ prev = DUK_HEAPHDR_GET_PREV(heap, hdr); next = DUK_HEAPHDR_GET_NEXT(heap, hdr); if (prev != NULL) { DUK_ASSERT(heap->heap_allocated != hdr); DUK_HEAPHDR_SET_NEXT(heap, prev, next); } else { DUK_ASSERT(heap->heap_allocated == hdr); heap->heap_allocated = next; } if (next != NULL) { DUK_HEAPHDR_SET_PREV(heap, next, prev); } else { ; } } #endif /* DUK_USE_REFERENCE_COUNTING */ #if defined(DUK_USE_FINALIZER_SUPPORT) DUK_INTERNAL void duk_heap_insert_into_finalize_list(duk_heap *heap, duk_heaphdr *hdr) { duk_heaphdr *root; root = heap->finalize_list; #if defined(DUK_USE_DOUBLE_LINKED_HEAP) DUK_HEAPHDR_SET_PREV(heap, hdr, NULL); if (root != NULL) { DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, root) == NULL); DUK_HEAPHDR_SET_PREV(heap, root, hdr); } #endif DUK_HEAPHDR_SET_NEXT(heap, hdr, root); DUK_HEAPHDR_ASSERT_LINKS(heap, hdr); DUK_HEAPHDR_ASSERT_LINKS(heap, root); heap->finalize_list = hdr; } #endif /* DUK_USE_FINALIZER_SUPPORT */ #if defined(DUK_USE_FINALIZER_SUPPORT) DUK_INTERNAL void duk_heap_remove_from_finalize_list(duk_heap *heap, duk_heaphdr *hdr) { #if defined(DUK_USE_DOUBLE_LINKED_HEAP) duk_heaphdr *next; duk_heaphdr *prev; next = DUK_HEAPHDR_GET_NEXT(heap, hdr); prev = DUK_HEAPHDR_GET_PREV(heap, hdr); if (next != NULL) { DUK_ASSERT(DUK_HEAPHDR_GET_PREV(heap, next) == hdr); DUK_HEAPHDR_SET_PREV(heap, next, prev); } if (prev == NULL) { DUK_ASSERT(hdr == heap->finalize_list); heap->finalize_list = next; } else { DUK_ASSERT(hdr != heap->finalize_list); DUK_HEAPHDR_SET_NEXT(heap, prev, next); } #else duk_heaphdr *next; duk_heaphdr *curr; /* Random removal is expensive: we need to locate the previous element * because we don't have a 'prev' pointer. */ curr = heap->finalize_list; if (curr == hdr) { heap->finalize_list = DUK_HEAPHDR_GET_NEXT(heap, curr); } else { DUK_ASSERT(hdr != heap->finalize_list); for (;;) { DUK_ASSERT(curr != NULL); /* Caller responsibility. */ next = DUK_HEAPHDR_GET_NEXT(heap, curr); if (next == hdr) { next = DUK_HEAPHDR_GET_NEXT(heap, hdr); DUK_HEAPHDR_SET_NEXT(heap, curr, next); break; } } } #endif } #endif /* DUK_USE_FINALIZER_SUPPORT */ #if defined(DUK_USE_ASSERTIONS) DUK_INTERNAL duk_bool_t duk_heap_in_heap_allocated(duk_heap *heap, duk_heaphdr *ptr) { duk_heaphdr *curr; DUK_ASSERT(heap != NULL); for (curr = heap->heap_allocated; curr != NULL; curr = DUK_HEAPHDR_GET_NEXT(heap, curr)) { if (curr == ptr) { return 1; } } return 0; } #endif /* DUK_USE_ASSERTIONS */ #if defined(DUK_USE_INTERRUPT_COUNTER) DUK_INTERNAL void duk_heap_switch_thread(duk_heap *heap, duk_hthread *new_thr) { duk_hthread *curr_thr; DUK_ASSERT(heap != NULL); if (new_thr != NULL) { curr_thr = heap->curr_thread; if (curr_thr == NULL) { /* For initial entry use default value; zero forces an * interrupt before executing the first insturction. */ DUK_DD(DUK_DDPRINT("switch thread, initial entry, init default interrupt counter")); new_thr->interrupt_counter = 0; new_thr->interrupt_init = 0; } else { /* Copy interrupt counter/init value state to new thread (if any). * It's OK for new_thr to be the same as curr_thr. */ #if defined(DUK_USE_DEBUG) if (new_thr != curr_thr) { DUK_DD(DUK_DDPRINT("switch thread, not initial entry, copy interrupt counter")); } #endif new_thr->interrupt_counter = curr_thr->interrupt_counter; new_thr->interrupt_init = curr_thr->interrupt_init; } } else { DUK_DD(DUK_DDPRINT("switch thread, new thread is NULL, no interrupt counter changes")); } heap->curr_thread = new_thr; /* may be NULL */ } #endif /* DUK_USE_INTERRUPT_COUNTER */ #if defined(DUK_USE_ASSERTIONS) DUK_INTERNAL void duk_heap_assert_valid(duk_heap *heap) { DUK_ASSERT(heap != NULL); } #endif