/* * Heap creation and destruction */ #include "duk_internal.h" typedef struct duk_internal_thread_state duk_internal_thread_state; struct duk_internal_thread_state { duk_ljstate lj; duk_bool_t creating_error; duk_hthread *curr_thread; duk_int_t call_recursion_depth; }; DUK_EXTERNAL duk_hthread *duk_create_heap(duk_alloc_function alloc_func, duk_realloc_function realloc_func, duk_free_function free_func, void *heap_udata, duk_fatal_function fatal_handler) { duk_heap *heap = NULL; duk_hthread *thr; /* Assume that either all memory funcs are NULL or non-NULL, mixed * cases will now be unsafe. */ /* XXX: just assert non-NULL values here and make caller arguments * do the defaulting to the default implementations (smaller code)? */ if (!alloc_func) { DUK_ASSERT(realloc_func == NULL); DUK_ASSERT(free_func == NULL); #if defined(DUK_USE_PROVIDE_DEFAULT_ALLOC_FUNCTIONS) alloc_func = duk_default_alloc_function; realloc_func = duk_default_realloc_function; free_func = duk_default_free_function; #else DUK_D(DUK_DPRINT("no allocation functions given and no default providers")); return NULL; #endif } else { DUK_ASSERT(realloc_func != NULL); DUK_ASSERT(free_func != NULL); } if (!fatal_handler) { fatal_handler = duk_default_fatal_handler; } DUK_ASSERT(alloc_func != NULL); DUK_ASSERT(realloc_func != NULL); DUK_ASSERT(free_func != NULL); DUK_ASSERT(fatal_handler != NULL); heap = duk_heap_alloc(alloc_func, realloc_func, free_func, heap_udata, fatal_handler); if (!heap) { return NULL; } thr = heap->heap_thread; DUK_ASSERT(thr != NULL); DUK_ASSERT(thr->heap != NULL); return thr; } DUK_EXTERNAL void duk_destroy_heap(duk_hthread *thr) { duk_heap *heap; if (!thr) { return; } DUK_ASSERT_API_ENTRY(thr); heap = thr->heap; DUK_ASSERT(heap != NULL); duk_heap_free(heap); } DUK_EXTERNAL void duk_suspend(duk_hthread *thr, duk_thread_state *state) { duk_internal_thread_state *snapshot = (duk_internal_thread_state *) (void *) state; duk_heap *heap; duk_ljstate *lj; DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(state != NULL); /* unvalidated */ /* Currently not supported when called from within a finalizer. * If that is done, the finalizer will remain running indefinitely, * preventing other finalizers from executing. The assert is a bit * wider, checking that it would be OK to run pending finalizers. */ DUK_ASSERT(thr->heap->pf_prevent_count == 0); /* Currently not supported to duk_suspend() from an errCreate() * call. */ DUK_ASSERT(thr->heap->creating_error == 0); heap = thr->heap; lj = &heap->lj; duk_push_tval(thr, &lj->value1); duk_push_tval(thr, &lj->value2); /* XXX: creating_error == 0 is asserted above, so no need to store. */ duk_memcpy((void *) &snapshot->lj, (const void *) lj, sizeof(duk_ljstate)); snapshot->creating_error = heap->creating_error; snapshot->curr_thread = heap->curr_thread; snapshot->call_recursion_depth = heap->call_recursion_depth; lj->jmpbuf_ptr = NULL; lj->type = DUK_LJ_TYPE_UNKNOWN; DUK_TVAL_SET_UNDEFINED(&lj->value1); DUK_TVAL_SET_UNDEFINED(&lj->value2); heap->creating_error = 0; heap->curr_thread = NULL; heap->call_recursion_depth = 0; } DUK_EXTERNAL void duk_resume(duk_hthread *thr, const duk_thread_state *state) { const duk_internal_thread_state *snapshot = (const duk_internal_thread_state *) (const void *) state; duk_heap *heap; DUK_ASSERT_API_ENTRY(thr); DUK_ASSERT(thr->heap != NULL); DUK_ASSERT(state != NULL); /* unvalidated */ /* Shouldn't be necessary if duk_suspend() is called before * duk_resume(), but assert in case API sequence is incorrect. */ DUK_ASSERT(thr->heap->pf_prevent_count == 0); DUK_ASSERT(thr->heap->creating_error == 0); heap = thr->heap; duk_memcpy((void *) &heap->lj, (const void *) &snapshot->lj, sizeof(duk_ljstate)); heap->creating_error = snapshot->creating_error; heap->curr_thread = snapshot->curr_thread; heap->call_recursion_depth = snapshot->call_recursion_depth; duk_pop_2(thr); } /* XXX: better place for this */ DUK_EXTERNAL void duk_set_global_object(duk_hthread *thr) { duk_hobject *h_glob; duk_hobject *h_prev_glob; duk_hobjenv *h_env; duk_hobject *h_prev_env; DUK_ASSERT_API_ENTRY(thr); DUK_D(DUK_DPRINT("replace global object with: %!T", duk_get_tval(thr, -1))); h_glob = duk_require_hobject(thr, -1); DUK_ASSERT(h_glob != NULL); /* * Replace global object. */ h_prev_glob = thr->builtins[DUK_BIDX_GLOBAL]; DUK_UNREF(h_prev_glob); thr->builtins[DUK_BIDX_GLOBAL] = h_glob; DUK_HOBJECT_INCREF(thr, h_glob); DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_glob); /* side effects, in theory (referenced by global env) */ /* * Replace lexical environment for global scope * * Create a new object environment for the global lexical scope. * We can't just reset the _Target property of the current one, * because the lexical scope is shared by other threads with the * same (initial) built-ins. */ h_env = duk_hobjenv_alloc(thr, DUK_HOBJECT_FLAG_EXTENSIBLE | DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJENV)); DUK_ASSERT(h_env != NULL); DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, (duk_hobject *) h_env) == NULL); DUK_ASSERT(h_env->target == NULL); DUK_ASSERT(h_glob != NULL); h_env->target = h_glob; DUK_HOBJECT_INCREF(thr, h_glob); DUK_ASSERT(h_env->has_this == 0); /* [ ... new_glob ] */ h_prev_env = thr->builtins[DUK_BIDX_GLOBAL_ENV]; thr->builtins[DUK_BIDX_GLOBAL_ENV] = (duk_hobject *) h_env; DUK_HOBJECT_INCREF(thr, (duk_hobject *) h_env); DUK_HOBJECT_DECREF_ALLOWNULL(thr, h_prev_env); /* side effects */ DUK_UNREF(h_env); /* without refcounts */ DUK_UNREF(h_prev_env); /* [ ... new_glob ] */ duk_pop(thr); /* [ ... ] */ }