void duk_hthread_callstack_shrink_check(duk_hthread *thr) { duk_size_t new_size; duk_activation *p; DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */ DUK_ASSERT(thr->callstack_size >= thr->callstack_top); if (thr->callstack_size - thr->callstack_top < DUK_CALLSTACK_SHRINK_THRESHOLD) { return; } new_size = thr->callstack_top + DUK_CALLSTACK_SHRINK_SPARE; DUK_ASSERT(new_size >= thr->callstack_top); DUK_DDPRINT("shrinking callstack %d -> %d", thr->callstack_size, new_size); /* * Note: must use indirect variant of DUK_REALLOC() because underlying * pointer may be changed by mark-and-sweep. */ /* shrink failure is not fatal */ p = (duk_activation *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_callstack_ptr, (void *) thr, sizeof(duk_activation) * new_size); if (p) { thr->callstack = p; thr->callstack_size = new_size; } else { DUK_DPRINT("callstack shrink failed, ignoring"); } /* note: any entries above the callstack top are garbage and not zeroed */ }
void *duk_heap_mem_realloc_indirect_checked(duk_hthread *thr, duk_mem_getptr cb, void *ud, size_t newsize, const char *filename, int line) { #else void *duk_heap_mem_realloc_indirect_checked(duk_hthread *thr, duk_mem_getptr cb, void *ud, size_t newsize) { #endif void *res; DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(newsize >= 0); res = DUK_REALLOC_INDIRECT(thr->heap, cb, ud, newsize); if (!res) { #ifdef DUK_USE_VERBOSE_ERRORS DUK_ERROR_RAW(filename, line, thr, DUK_ERR_ALLOC_ERROR, "memory realloc failed"); #else DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "memory realloc failed"); #endif } return res; }
DUK_INTERNAL void duk_hthread_catchstack_grow(duk_hthread *thr) { duk_catcher *new_ptr; duk_size_t old_size; duk_size_t new_size; DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(thr->catchstack_top); /* avoid warning (unsigned) */ DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top); if (thr->catchstack_top < thr->catchstack_size) { return; } old_size = thr->catchstack_size; new_size = old_size + DUK_CATCHSTACK_GROW_STEP; /* this is a bit approximate (errors out before max is reached); this is OK */ if (new_size >= thr->catchstack_max) { DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_CATCHSTACK_LIMIT); } DUK_DD(DUK_DDPRINT("growing catchstack %ld -> %ld", (long) old_size, (long) new_size)); /* * Note: must use indirect variant of DUK_REALLOC() because underlying * pointer may be changed by mark-and-sweep. */ DUK_ASSERT(new_size > 0); new_ptr = (duk_catcher *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_catchstack_ptr, (void *) thr, sizeof(duk_catcher) * new_size); if (!new_ptr) { /* No need for a NULL/zero-size check because new_size > 0) */ DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_REALLOC_FAILED); } thr->catchstack = new_ptr; thr->catchstack_size = new_size; /* note: any entries above the catchstack top are garbage and not zeroed */ }
DUK_INTERNAL void duk_hthread_catchstack_shrink_check(duk_hthread *thr) { duk_size_t new_size; duk_catcher *p; DUK_ASSERT(thr != NULL); DUK_ASSERT_DISABLE(thr->catchstack_top >= 0); /* avoid warning (unsigned) */ DUK_ASSERT(thr->catchstack_size >= thr->catchstack_top); if (thr->catchstack_size - thr->catchstack_top < DUK_CATCHSTACK_SHRINK_THRESHOLD) { return; } new_size = thr->catchstack_top + DUK_CATCHSTACK_SHRINK_SPARE; DUK_ASSERT(new_size >= thr->catchstack_top); DUK_DD(DUK_DDPRINT("shrinking catchstack %ld -> %ld", (long) thr->catchstack_size, (long) new_size)); /* * Note: must use indirect variant of DUK_REALLOC() because underlying * pointer may be changed by mark-and-sweep. */ /* shrink failure is not fatal */ p = (duk_catcher *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_catchstack_ptr, (void *) thr, sizeof(duk_catcher) * new_size); if (p) { thr->catchstack = p; thr->catchstack_size = new_size; } else { /* Because new_size != 0, if condition doesn't need to be * (p != NULL || new_size == 0). */ DUK_ASSERT(new_size != 0); DUK_D(DUK_DPRINT("catchstack shrink failed, ignoring")); } /* note: any entries above the catchstack top are garbage and not zeroed */ }
void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t new_size, size_t new_usable_size) { size_t new_alloc_size; void *res; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(new_usable_size >= new_size); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); /* * Maximum size check * * XXX: check against usable size? */ if (new_size > DUK_HBUFFER_MAX_BYTELEN) { DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, "buffer too long"); } /* * Note: use indirect realloc variant just in case mark-and-sweep * (finalizers) might resize this same buffer during garbage * collection. */ /* FIXME: maybe remove safety NUL term for buffers? */ new_alloc_size = new_usable_size + 1; /* +1 for safety nul term */ res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_alloc_size); if (res) { DUK_DDDPRINT("resized dynamic buffer %p:%d:%d -> %p:%d:%d", buf->curr_alloc, buf->size, buf->usable_size, res, new_size, new_usable_size); /* * The entire allocated buffer area, regardless of actual used size, * is kept zeroed in resizes for simplicity. If the buffer is grown, * zero the new part (the safety NUL byte is re-zeroed every time). * Another policy would be to ensure data is zeroed as the used part * is extended (with one safety NUL byte) this is much more simple, * and not a big deal because the spart part is relatively small. */ if (new_alloc_size > buf->usable_size) { /* When new_usable_size == old_usable_size, one byte will * be rezeroed (the safety NUL byte). */ DUK_ASSERT(new_alloc_size - buf->usable_size > 0); #ifdef DUK_USE_ZERO_BUFFER_DATA DUK_MEMZERO((void *) ((char *) res + buf->usable_size), new_alloc_size - buf->usable_size); #endif } buf->size = new_size; buf->usable_size = new_usable_size; buf->curr_alloc = res; } else { DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "failed to resize buffer from %d:%d to %d:%d", buf->size, buf->usable_size, new_size, new_usable_size); } DUK_ASSERT(res != NULL); }
void duk_hbuffer_resize(duk_hthread *thr, duk_hbuffer_dynamic *buf, size_t new_size, size_t new_usable_size) { size_t new_alloc_size; void *res; DUK_ASSERT(thr != NULL); DUK_ASSERT(buf != NULL); DUK_ASSERT(new_usable_size >= new_size); DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(buf)); /* * Maximum size check * * XXX: check against usable size? */ if (new_size > DUK_HBUFFER_MAX_BYTELEN) { DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, "buffer too long"); } /* * Note: use indirect realloc variant just in case mark-and-sweep * (finalizers) might resize this same buffer during garbage * collection. */ new_alloc_size = new_usable_size; res = DUK_REALLOC_INDIRECT(thr->heap, duk_hbuffer_get_dynalloc_ptr, (void *) buf, new_alloc_size); if (res || new_alloc_size == 0) { /* 'res' may be NULL if new allocation size is 0. */ DUK_DDD(DUK_DDDPRINT("resized dynamic buffer %p:%d:%d -> %p:%d:%d", buf->curr_alloc, buf->size, buf->usable_size, res, new_size, new_usable_size)); /* * The entire allocated buffer area, regardless of actual used * size, is kept zeroed in resizes for simplicity. If the buffer * is grown, zero the new part. Another policy would be to * ensure data is zeroed as the used part is extended. The * current approach is much more simple and is not a big deal * because the spare part is relatively small. */ if (new_alloc_size > buf->usable_size) { DUK_ASSERT(new_alloc_size - buf->usable_size > 0); #ifdef DUK_USE_ZERO_BUFFER_DATA DUK_MEMZERO((void *) ((char *) res + buf->usable_size), new_alloc_size - buf->usable_size); #endif } buf->size = new_size; buf->usable_size = new_usable_size; buf->curr_alloc = res; } else { DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "failed to resize buffer from %d:%d to %d:%d", buf->size, buf->usable_size, new_size, new_usable_size); } DUK_ASSERT(res != NULL || new_alloc_size == 0); }