Browse Source

object and object property code

pull/1/head
Sami Vaarala 12 years ago
parent
commit
ac4ff0b964
  1. 6
      src/duk_hobject.h
  2. 153
      src/duk_hobject_alloc.c
  3. 70
      src/duk_hobject_class.c
  4. 311
      src/duk_hobject_enum.c
  5. 86
      src/duk_hobject_finalizer.c
  6. 44
      src/duk_hobject_misc.c
  7. 4512
      src/duk_hobject_props.c

6
src/duk_hobject.h

@ -486,9 +486,9 @@ duk_hstring *duk_hobject_get_internal_value_string(duk_heap *heap, duk_hobject *
void duk_hobject_compact_props(duk_hthread *thr, duk_hobject *obj);
/* enumeration */
int duk_hobject_enumerator_create(duk_context *ctx, int enum_flags);
int duk_hobject_get_enumerated_keys(duk_context *ctx, int enum_flags);
int duk_hobject_enumerator_next(duk_context *ctx);
void duk_hobject_enumerator_create(duk_context *ctx, int enum_flags);
void duk_hobject_get_enumerated_keys(duk_context *ctx, int enum_flags);
int duk_hobject_enumerator_next(duk_context *ctx, int get_value);
/* macros */
void duk_hobject_set_prototype(duk_hthread *thr, duk_hobject *h, duk_hobject *p);

153
src/duk_hobject_alloc.c

@ -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;
}

70
src/duk_hobject_class.c

@ -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 */
};

311
src/duk_hobject_enum.c

@ -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] */
}

86
src/duk_hobject_finalizer.c

@ -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);
}

44
src/duk_hobject_misc.c

@ -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
}

4512
src/duk_hobject_props.c

File diff suppressed because it is too large
Loading…
Cancel
Save