mirror of https://github.com/svaarala/duktape.git
Sami Vaarala
12 years ago
7 changed files with 5179 additions and 3 deletions
@ -0,0 +1,153 @@ |
|||
/*
|
|||
* Hobject allocation. |
|||
* |
|||
* Provides primitive allocation functions for all object types (plain object, |
|||
* compiled function, native function, thread). The object return is not yet |
|||
* in "heap allocated" list and has a refcount of zero, so caller must careful. |
|||
*/ |
|||
|
|||
#include "duk_internal.h" |
|||
|
|||
static void init_object_parts(duk_heap *heap, duk_hobject *obj, int hobject_flags) { |
|||
#ifdef DUK_USE_EXPLICIT_NULL_INIT |
|||
obj->p = NULL; |
|||
#endif |
|||
|
|||
/* FIXME: macro? sets both heaphdr and object flags */ |
|||
obj->hdr.h_flags = hobject_flags; |
|||
DUK_HEAPHDR_SET_TYPE(&obj->hdr, DUK_HTYPE_OBJECT); /* also goes into flags */ |
|||
|
|||
DUK_HEAP_INSERT_INTO_HEAP_ALLOCATED(heap, &obj->hdr); |
|||
|
|||
/*
|
|||
* obj->p is intentionally left as NULL, and duk_hobject_props.c must deal |
|||
* with this properly. This is intentional: empty objects consume a minimum |
|||
* amount of memory. Further, an initial allocation might fail and cause |
|||
* 'obj' to "leak" (require a mark-and-sweep) since it is not reachable yet. |
|||
*/ |
|||
} |
|||
|
|||
/*
|
|||
* Allocate an duk_hobject. |
|||
* |
|||
* The allocated object has no allocation for properties; the caller may |
|||
* want to force a resize if a desired size is known. |
|||
* |
|||
* The allocated object has zero reference count and is not reachable. |
|||
* The caller MUST make the object reachable and increase its reference |
|||
* count before invoking any operation that might require memory allocation. |
|||
*/ |
|||
|
|||
duk_hobject *duk_hobject_alloc(duk_heap *heap, int hobject_flags) { |
|||
duk_hobject *res; |
|||
|
|||
DUK_ASSERT(heap != NULL); |
|||
|
|||
/* different memory layout, alloc size, and init */ |
|||
DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_COMPILEDFUNCTION) == 0); |
|||
DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_NATIVEFUNCTION) == 0); |
|||
DUK_ASSERT((hobject_flags & DUK_HOBJECT_FLAG_THREAD) == 0); |
|||
|
|||
res = DUK_ALLOC(heap, sizeof(duk_hobject)); |
|||
if (!res) { |
|||
return NULL; |
|||
} |
|||
memset(res, 0, sizeof(duk_hobject)); |
|||
|
|||
init_object_parts(heap, res, hobject_flags); |
|||
|
|||
return res; |
|||
} |
|||
|
|||
duk_hcompiledfunction *duk_hcompiledfunction_alloc(duk_heap *heap, int hobject_flags) { |
|||
duk_hcompiledfunction *res; |
|||
|
|||
res = DUK_ALLOC(heap, sizeof(duk_hcompiledfunction)); |
|||
if (!res) { |
|||
return NULL; |
|||
} |
|||
memset(res, 0, sizeof(duk_hcompiledfunction)); |
|||
|
|||
init_object_parts(heap, &res->obj, hobject_flags); |
|||
|
|||
#ifdef DUK_USE_EXPLICIT_NULL_INIT |
|||
res->data = NULL; |
|||
res->funcs = NULL; |
|||
res->bytecode = NULL; |
|||
#endif |
|||
|
|||
return res; |
|||
} |
|||
|
|||
duk_hnativefunction *duk_hnativefunction_alloc(duk_heap *heap, int hobject_flags) { |
|||
duk_hnativefunction *res; |
|||
|
|||
res = DUK_ALLOC(heap, sizeof(duk_hnativefunction)); |
|||
if (!res) { |
|||
return NULL; |
|||
} |
|||
memset(res, 0, sizeof(duk_hnativefunction)); |
|||
|
|||
init_object_parts(heap, &res->obj, hobject_flags); |
|||
|
|||
#ifdef DUK_USE_EXPLICIT_NULL_INIT |
|||
res->func = NULL; |
|||
#endif |
|||
|
|||
return res; |
|||
} |
|||
|
|||
/*
|
|||
* Allocate a new thread. |
|||
* |
|||
* Leaves the built-ins array uninitialized. The caller must either |
|||
* initialize a new global context or share existing built-ins from |
|||
* another thread. |
|||
*/ |
|||
|
|||
duk_hthread *duk_hthread_alloc(duk_heap *heap, int hobject_flags) { |
|||
duk_hthread *res; |
|||
|
|||
res = DUK_ALLOC(heap, sizeof(duk_hthread)); |
|||
if (!res) { |
|||
return NULL; |
|||
} |
|||
memset(res, 0, sizeof(duk_hthread)); |
|||
|
|||
init_object_parts(heap, &res->obj, hobject_flags); |
|||
|
|||
#ifdef DUK_USE_EXPLICIT_NULL_INIT |
|||
res->heap = NULL; |
|||
res->valstack = NULL; |
|||
res->valstack_end = NULL; |
|||
res->valstack_bottom = NULL; |
|||
res->valstack_top = NULL; |
|||
res->callstack = NULL; |
|||
res->catchstack = NULL; |
|||
res->resumer = NULL; |
|||
res->strs = NULL; |
|||
{ |
|||
int i; |
|||
for (i = 0; i < DUK_NUM_BUILTINS; i++) { |
|||
res->builtins[i] = NULL; |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
res->heap = heap; |
|||
res->valstack_max = DUK_VALSTACK_DEFAULT_MAX; |
|||
res->callstack_max = DUK_CALLSTACK_DEFAULT_MAX; |
|||
res->catchstack_max = DUK_CATCHSTACK_DEFAULT_MAX; |
|||
|
|||
return res; |
|||
} |
|||
|
|||
/* FIXME: unused now, remove */ |
|||
duk_hobject *duk_hobject_alloc_checked(duk_hthread *thr, int hobject_flags) { |
|||
duk_hobject *res = duk_hobject_alloc(thr->heap, hobject_flags); |
|||
if (!res) { |
|||
DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, "failed to allocate an object"); |
|||
} |
|||
return res; |
|||
} |
|||
|
@ -0,0 +1,70 @@ |
|||
/*
|
|||
* Hobject Ecmascript [[Class]]. |
|||
*/ |
|||
|
|||
#include "duk_internal.h" |
|||
|
|||
/* Maybe better to check these elsewhere */ |
|||
#if (DUK_HEAP_STRIDX_UC_ARGUMENTS > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_ARRAY > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_UC_BOOLEAN > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_DATE > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_ERROR > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_UC_FUNCTION > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_JSON > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_MATH > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_UC_NUMBER > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_UC_OBJECT > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_REG_EXP > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_UC_STRING > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_GLOBAL > 255) |
|||
#error constant too large |
|||
#endif |
|||
#if (DUK_HEAP_STRIDX_EMPTY_STRING > 255) |
|||
#error constant too large |
|||
#endif |
|||
|
|||
/* Note: assumes that these string indexes are 8-bit, genstrings.py must ensure that */ |
|||
duk_u8 duk_class_number_to_stridx[16] = { |
|||
DUK_HEAP_STRIDX_EMPTY_STRING, /* UNUSED, intentionally empty */ |
|||
DUK_HEAP_STRIDX_UC_ARGUMENTS, |
|||
DUK_HEAP_STRIDX_ARRAY, |
|||
DUK_HEAP_STRIDX_UC_BOOLEAN, |
|||
DUK_HEAP_STRIDX_DATE, |
|||
DUK_HEAP_STRIDX_ERROR, |
|||
DUK_HEAP_STRIDX_UC_FUNCTION, |
|||
DUK_HEAP_STRIDX_JSON, |
|||
DUK_HEAP_STRIDX_MATH, |
|||
DUK_HEAP_STRIDX_UC_NUMBER, |
|||
DUK_HEAP_STRIDX_UC_OBJECT, |
|||
DUK_HEAP_STRIDX_REG_EXP, |
|||
DUK_HEAP_STRIDX_UC_STRING, |
|||
DUK_HEAP_STRIDX_GLOBAL, |
|||
DUK_HEAP_STRIDX_EMPTY_STRING, /* OBJENV, intentionally empty */ |
|||
DUK_HEAP_STRIDX_EMPTY_STRING, /* DECENV, intentionally empty */ |
|||
}; |
|||
|
@ -0,0 +1,311 @@ |
|||
/*
|
|||
* Hobject enumeration support. |
|||
* |
|||
* Creates an internal enumeration state object to be used e.g. with for-in |
|||
* enumeration. The state object contains a snapshot of target object keys |
|||
* and internal control state for enumeration. Enumerator flags allow caller |
|||
* to e.g. request internal/non-enumerable properties, and to enumerate only |
|||
* "own" properties. |
|||
* |
|||
* Also creates the result value for e.g. Object.keys() based on the same |
|||
* internal structure. |
|||
* |
|||
* This snapshot-based enumeration approach is used to simplify enumeration: |
|||
* non-snapshot-based approaches are difficult to reconcile with mutating |
|||
* the enumeration target, running multiple long-lived enumerators at the |
|||
* same time, garbage collection details, etc. The downside is that the |
|||
* enumerator object is memory inefficient especially for iterating arrays. |
|||
*/ |
|||
|
|||
#include "duk_internal.h" |
|||
|
|||
/*
|
|||
* Create an internal enumerator object E, which has its keys ordered |
|||
* to match desired enumeration ordering. Also initialize internal control |
|||
* properties for enumeration. |
|||
* |
|||
* Note: if an array was used to hold enumeration keys instead, an array |
|||
* scan would be needed to eliminate duplicates found in the prototype chain. |
|||
*/ |
|||
|
|||
/* FIXME: identify enumeration target with an object index (not top of stack) */ |
|||
|
|||
/* must match exactly the number of internal properties inserted to enumerator */ |
|||
#define ENUM_START_INDEX 2 |
|||
|
|||
void duk_hobject_enumerator_create(duk_context *ctx, int enum_flags) { |
|||
duk_hthread *thr = (duk_hthread *) ctx; |
|||
duk_hobject *target; |
|||
duk_hobject *curr; |
|||
duk_hobject *res; |
|||
|
|||
DUK_ASSERT(ctx != NULL); |
|||
|
|||
DUK_DPRINT("create enumerator, stack top: %d", duk_get_top(ctx)); |
|||
|
|||
target = duk_require_hobject(ctx, -1); |
|||
DUK_ASSERT(target != NULL); |
|||
|
|||
duk_push_new_object_internal(ctx); |
|||
|
|||
DUK_DPRINT("created internal object"); |
|||
DUK_DEBUG_DUMP_HTHREAD(thr); |
|||
|
|||
/* [target res] */ |
|||
|
|||
duk_push_hstring_stridx(ctx, DUK_HEAP_STRIDX_INT_TARGET); |
|||
duk_push_hobject(ctx, target); |
|||
DUK_DEBUG_DUMP_HTHREAD(thr); |
|||
duk_put_prop(ctx, -3); |
|||
|
|||
/* initialize index so that we skip internal control keys */ |
|||
duk_push_hstring_stridx(ctx, DUK_HEAP_STRIDX_INT_NEXT); |
|||
duk_push_int(ctx, ENUM_START_INDEX); |
|||
duk_put_prop(ctx, -3); |
|||
|
|||
curr = target; |
|||
while (curr) { |
|||
duk_u32 i; |
|||
|
|||
/*
|
|||
* Virtual properties. |
|||
* |
|||
* String indices are always enumerable. |
|||
*/ |
|||
|
|||
if (DUK_HOBJECT_HAS_SPECIAL_STRINGOBJ(curr)) { |
|||
duk_hstring *h_val; |
|||
|
|||
h_val = duk_hobject_get_internal_value_string(thr->heap, curr); |
|||
DUK_ASSERT(h_val != NULL); /* string objects must not created without internal value */ |
|||
|
|||
/* FIXME: type for 'i' to match string max len (duk_u32) */ |
|||
for (i = 0; i < DUK_HSTRING_GET_CHARLEN(h_val); i++) { |
|||
duk_hstring *k; |
|||
|
|||
k = duk_heap_string_intern_u32_checked(thr, i); |
|||
DUK_ASSERT(k); |
|||
duk_push_hstring(ctx, k); |
|||
duk_push_true(ctx); |
|||
|
|||
/* [target res key true] */ |
|||
duk_put_prop(ctx, -3); |
|||
|
|||
/* [target res] */ |
|||
} |
|||
} |
|||
|
|||
/*
|
|||
* Array part |
|||
* |
|||
* Note: ordering between array and entry part must match 'abandon array' |
|||
* behavior in duk_hobject_props.c: key order after an array is abandoned |
|||
* must be the same. |
|||
*/ |
|||
|
|||
for (i = 0; i < curr->a_size; i++) { |
|||
duk_hstring *k; |
|||
duk_tval *tv; |
|||
|
|||
tv = DUK_HOBJECT_A_GET_VALUE_PTR(curr, i); |
|||
if (DUK_TVAL_IS_UNDEFINED_UNUSED(tv)) { |
|||
continue; |
|||
} |
|||
k = duk_heap_string_intern_u32_checked(thr, i); |
|||
DUK_ASSERT(k); |
|||
|
|||
duk_push_hstring(ctx, k); |
|||
duk_push_true(ctx); |
|||
|
|||
/* [target res key true] */ |
|||
duk_put_prop(ctx, -3); |
|||
|
|||
/* [target res] */ |
|||
} |
|||
|
|||
/*
|
|||
* Entries part |
|||
*/ |
|||
|
|||
for (i = 0; i < curr->e_used; i++) { |
|||
duk_hstring *k; |
|||
|
|||
k = DUK_HOBJECT_E_GET_KEY(curr, i); |
|||
if (!k) { |
|||
continue; |
|||
} |
|||
if (!DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(curr, i) && |
|||
!(enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) { |
|||
continue; |
|||
} |
|||
if (DUK_HSTRING_HAS_INTERNAL(k) && |
|||
!(enum_flags & DUK_ENUM_INCLUDE_INTERNAL)) { |
|||
continue; |
|||
} |
|||
|
|||
/* internal properties are assumed to never be enumerable */ |
|||
DUK_ASSERT(!DUK_HSTRING_HAS_INTERNAL(k)); |
|||
|
|||
DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(curr, i) || |
|||
!DUK_TVAL_IS_UNDEFINED_UNUSED(&DUK_HOBJECT_E_GET_VALUE_PTR(curr, i)->v)); |
|||
|
|||
duk_push_hstring(ctx, k); |
|||
duk_push_true(ctx); |
|||
|
|||
/* [target res key true] */ |
|||
duk_put_prop(ctx, -3); |
|||
|
|||
/* [target res] */ |
|||
} |
|||
|
|||
if (enum_flags & DUK_ENUM_OWN_PROPERTIES_ONLY) { |
|||
break; |
|||
} |
|||
|
|||
curr = curr->prototype; |
|||
} |
|||
|
|||
/* [target res] */ |
|||
|
|||
duk_remove(ctx, -2); |
|||
|
|||
/* [res] */ |
|||
|
|||
/* compact; no need to seal because object is internal */ |
|||
res = duk_require_hobject(ctx, -1); |
|||
duk_hobject_compact_props(thr, res); |
|||
|
|||
DUK_DDDPRINT("created enumerator object: %!iT", duk_get_tval(ctx, -1)); |
|||
} |
|||
|
|||
/* [enum] -> [key] (get_value == 0)
|
|||
* [enum] -> [key value] (get_value == 1) |
|||
* |
|||
* Returns non-zero if a key was enumerated. |
|||
*/ |
|||
int duk_hobject_enumerator_next(duk_context *ctx, int get_value) { |
|||
duk_hthread *thr = (duk_hthread *) ctx; |
|||
duk_hobject *e; |
|||
duk_hobject *target; |
|||
duk_hstring *res = NULL; |
|||
duk_u32 idx; |
|||
|
|||
DUK_ASSERT(ctx != NULL); |
|||
|
|||
/* [enum] */ |
|||
|
|||
e = duk_require_hobject(ctx, -1); |
|||
|
|||
/* FIXME: use get tval ptr, more efficient */ |
|||
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_INT_NEXT); |
|||
idx = (duk_u32) duk_require_number(ctx, -1); |
|||
duk_pop(ctx); |
|||
DUK_DPRINT("enumeration: index is: %d", idx); |
|||
|
|||
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_INT_TARGET); |
|||
target = duk_require_hobject(ctx, -1); |
|||
DUK_ASSERT(target != NULL); |
|||
duk_pop(ctx); /* still reachable */ |
|||
|
|||
DUK_DDDPRINT("getting next enum value, target=%!iO, enumerator=%!iT", |
|||
target, duk_get_tval(ctx, -1)); |
|||
|
|||
/* no array part */ |
|||
for (;;) { |
|||
duk_hstring *k; |
|||
|
|||
if (idx >= e->e_used) { |
|||
DUK_DDDPRINT("enumeration: ran out of elements"); |
|||
break; |
|||
} |
|||
|
|||
/* we know these because enum objects are internally created */ |
|||
k = DUK_HOBJECT_E_GET_KEY(e, idx); |
|||
DUK_ASSERT(k != NULL); |
|||
DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(e, idx)); |
|||
DUK_ASSERT(!DUK_TVAL_IS_UNDEFINED_UNUSED(&DUK_HOBJECT_E_GET_VALUE(e, idx).v)); |
|||
|
|||
idx++; |
|||
|
|||
/* recheck that the property still exists */ |
|||
if (!duk_hobject_hasprop_raw(thr, target, k)) { |
|||
DUK_DDDPRINT("property deleted during enumeration, skip"); |
|||
continue; |
|||
} |
|||
|
|||
DUK_DDDPRINT("enumeration: found element, key: %!O", k); |
|||
res = k; |
|||
break; |
|||
} |
|||
|
|||
DUK_DDDPRINT("enumeration: updating next index to %d", idx); |
|||
|
|||
duk_push_number(ctx, (double) idx); |
|||
duk_put_prop_stridx(ctx, -2, DUK_HEAP_STRIDX_INT_NEXT); |
|||
|
|||
if (res) { |
|||
duk_push_hstring(ctx, res); |
|||
} else { |
|||
duk_push_undefined(ctx); |
|||
} |
|||
|
|||
/* [enum key] */ |
|||
duk_remove(ctx, -2); |
|||
|
|||
/* [key] */ |
|||
|
|||
if (get_value) { |
|||
duk_push_hobject(ctx, target); |
|||
duk_dup(ctx, -2); /* -> [key target key] */ |
|||
duk_get_prop(ctx, -2); /* -> [key target val] */ |
|||
duk_remove(ctx, -2); /* -> [key val] */ |
|||
} |
|||
|
|||
return (res ? 1 : 0); |
|||
} |
|||
|
|||
/*
|
|||
* Get enumerated keys in an Ecmascript array. Matches Object.keys() behavior |
|||
* described in E5 Section 15.2.3.14. |
|||
*/ |
|||
|
|||
void duk_hobject_get_enumerated_keys(duk_context *ctx, int enum_flags) { |
|||
duk_hobject *e; |
|||
int i; |
|||
int idx; |
|||
|
|||
DUK_ASSERT(ctx != NULL); |
|||
DUK_ASSERT(duk_get_hobject(ctx, -1) != NULL); |
|||
|
|||
/* Create a temporary enumerator to get the (non-duplicated) key list;
|
|||
* the enumerator state is initialized without being needed, but that |
|||
* has little impact. |
|||
*/ |
|||
|
|||
duk_hobject_enumerator_create(ctx, enum_flags); |
|||
duk_push_new_object_internal(ctx); |
|||
|
|||
/* [target enum res] */ |
|||
|
|||
e = duk_require_hobject(ctx, -2); |
|||
DUK_ASSERT(e != NULL); |
|||
|
|||
idx = 0; |
|||
for (i = 0; i < e->e_used; i++) { |
|||
duk_hstring *k; |
|||
|
|||
k = DUK_HOBJECT_E_GET_KEY(e, i); |
|||
DUK_ASSERT(k); /* enumerator must have no keys deleted */ |
|||
|
|||
/* [target enum res] */ |
|||
duk_push_int(ctx, idx++); |
|||
duk_push_hstring(ctx, k); |
|||
duk_put_prop(ctx, -3); |
|||
} |
|||
|
|||
/* [target enum res] */ |
|||
duk_remove(ctx, -2); |
|||
|
|||
/* [target res] */ |
|||
} |
|||
|
@ -0,0 +1,86 @@ |
|||
/*
|
|||
* Run an duk_hobject finalizer. Used for both reference counting |
|||
* and mark-and-sweep algorithms. Must never throw an error. |
|||
* |
|||
* There is no return value. Any return value or error thrown by |
|||
* the finalizer is ignored (although errors are debug logged). |
|||
* |
|||
* Notes: |
|||
* |
|||
* - The thread used for calling the finalizer is the same as the |
|||
* 'thr' argument. This may need to change later. |
|||
* |
|||
* - The finalizer thread 'top' assertions are there because it is |
|||
* critical that strict stack policy is observed (i.e. no cruft |
|||
* left on the finalizer stack). |
|||
*/ |
|||
|
|||
#include "duk_internal.h" |
|||
|
|||
static int _finalize_helper(duk_context *ctx) { |
|||
DUK_ASSERT(ctx != NULL); |
|||
|
|||
DUK_DDDPRINT("protected finalization helper running"); |
|||
|
|||
/* [... obj] */ |
|||
|
|||
duk_get_prop_stridx(ctx, -1, DUK_HEAP_STRIDX_INT_FINALIZER); /* -> [... obj finalizer] */ |
|||
if (!duk_is_callable(ctx, -1)) { |
|||
DUK_DDDPRINT("-> no finalizer or finalizer not callable"); |
|||
return 0; |
|||
} |
|||
duk_dup(ctx, -2); /* -> [... obj finalizer obj] */ |
|||
DUK_DDDPRINT("-> finalizer found, calling finalizer"); |
|||
duk_call(ctx, 1); /* -> [... obj retval] */ |
|||
DUK_DDDPRINT("finalizer finished successfully"); |
|||
return 0; |
|||
|
|||
/* Note: we rely on duk_safe_call() to fix up the stack for the caller,
|
|||
* so we don't need to pop stuff here. There is no return value; |
|||
* caller determines rescued status based on object refcount. |
|||
*/ |
|||
} |
|||
|
|||
void duk_hobject_run_finalizer(duk_hthread *thr, duk_hobject *obj) { |
|||
duk_context *ctx = (duk_context *) thr; |
|||
int rc; |
|||
#ifdef DUK_USE_ASSERTIONS |
|||
int entry_top; |
|||
#endif |
|||
|
|||
DUK_DDDPRINT("running object finalizer for object: %p", (void *) obj); |
|||
|
|||
DUK_ASSERT(thr != NULL); |
|||
DUK_ASSERT(ctx != NULL); |
|||
DUK_ASSERT(obj != NULL); |
|||
|
|||
/* FIXME: assert stack space */ |
|||
|
|||
#ifdef DUK_USE_ASSERTIONS |
|||
entry_top = duk_get_top(ctx); |
|||
#endif |
|||
/*
|
|||
* Get and call the finalizer. All of this must be wrapped |
|||
* in a protected call, because even getting the finalizer |
|||
* may trigger an error (getter may throw one, for instance). |
|||
*/ |
|||
|
|||
/* FIXME: use a NULL error handler for the finalizer call? */ |
|||
|
|||
DUK_DDDPRINT("-> finalizer found, calling wrapped finalize helper"); |
|||
duk_push_hobject(ctx, obj); /* this also increases refcount by one */ |
|||
rc = duk_safe_call(ctx, _finalize_helper, 0 /*nargs*/, 1 /*nrets*/, DUK_INVALID_INDEX); /* -> [... obj retval/error] */ |
|||
DUK_ASSERT(duk_get_top(ctx) == entry_top + 2); /* duk_safe_call discipline */ |
|||
|
|||
if (rc != DUK_ERR_EXEC_SUCCESS) { |
|||
/* Note: we ask for one return value from duk_safe_call to get this
|
|||
* error debugging here. |
|||
*/ |
|||
DUK_DPRINT("wrapped finalizer call failed for object %p (ignored); error: %!T", |
|||
(void *) obj, duk_get_tval(ctx, -1)); |
|||
} |
|||
duk_pop_2(ctx); /* -> [...] */ |
|||
|
|||
DUK_ASSERT(duk_get_top(ctx) == entry_top); |
|||
} |
|||
|
@ -0,0 +1,44 @@ |
|||
/*
|
|||
* Misc support functions |
|||
*/ |
|||
|
|||
#include "duk_internal.h" |
|||
|
|||
int duk_hobject_prototype_chain_contains(duk_hthread *thr, duk_hobject *h, duk_hobject *p) { |
|||
duk_u32 sanity; |
|||
|
|||
DUK_ASSERT(thr != NULL); |
|||
DUK_ASSERT(h != NULL); |
|||
/* allow 'p' to be NULL; then the result is always false */ |
|||
|
|||
sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY; |
|||
do { |
|||
if (h == p) { |
|||
return 1; |
|||
} |
|||
|
|||
if (sanity-- == 0) { |
|||
DUK_ERROR(thr, DUK_ERR_INTERNAL_ERROR, "prototype chain max depth reached (loop?)"); |
|||
} |
|||
h = h->prototype; |
|||
} while(h); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* FIXME: needed? */ |
|||
void duk_hobject_set_prototype(duk_hthread *thr, duk_hobject *h, duk_hobject *p) { |
|||
#ifdef DUK_USE_REFERENCE_COUNTING |
|||
duk_hobject *tmp; |
|||
|
|||
DUK_ASSERT(h); |
|||
tmp = h->prototype; |
|||
h->prototype = p; |
|||
DUK_HOBJECT_INCREF(thr, p); /* avoid problems if p == h->prototype */ |
|||
DUK_HOBJECT_DECREF(thr, tmp); |
|||
#else |
|||
DUK_ASSERT(h); |
|||
h->prototype = p; |
|||
#endif |
|||
} |
|||
|
File diff suppressed because it is too large
Loading…
Reference in new issue