/* * Returns non-zero if a key and/or value was enumerated, and: * * [enum] -> [key] (get_value == 0) * [enum] -> [key value] (get_value == 1) * * Returns zero without pushing anything on the stack otherwise. */ 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_uint32_t 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_STRIDX_INT_NEXT); idx = (duk_uint32_t) duk_require_number(ctx, -1); duk_pop(ctx); DUK_DDDPRINT("enumeration: index is: %d", idx); duk_get_prop_stridx(ctx, -1, DUK_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_STRIDX_INT_NEXT); /* [... enum] */ if (res) { duk_push_hstring(ctx, res); if (get_value) { duk_push_hobject(ctx, target); duk_dup(ctx, -2); /* -> [... enum key target key] */ duk_get_prop(ctx, -2); /* -> [... enum key target val] */ duk_remove(ctx, -2); /* -> [... enum key val] */ duk_remove(ctx, -3); /* -> [... key val] */ } else { duk_remove(ctx, -2); /* -> [... key] */ } return 1; } else { duk_pop(ctx); /* -> [...] */ return 0; } }
/* * Returns non-zero if a key and/or value was enumerated, and: * * [enum] -> [key] (get_value == 0) * [enum] -> [key value] (get_value == 1) * * Returns zero without pushing anything on the stack otherwise. */ duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t get_value) { duk_hthread *thr = (duk_hthread *) ctx; duk_hobject *e; duk_hobject *enum_target; duk_hstring *res = NULL; duk_uint_fast32_t idx; duk_bool_t check_existence; DUK_ASSERT(ctx != NULL); /* [... enum] */ e = duk_require_hobject(ctx, -1); /* XXX use get tval ptr, more efficient */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_NEXT); idx = (duk_uint_fast32_t) duk_require_uint(ctx, -1); duk_pop(ctx); DUK_DDD(DUK_DDDPRINT("enumeration: index is: %ld", (long) idx)); /* Enumeration keys are checked against the enumeration target (to see * that they still exist). In the proxy enumeration case _target will * be the proxy, and checking key existence against the proxy is not * required (or sensible, as the keys may be fully virtual). */ duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET); enum_target = duk_require_hobject(ctx, -1); DUK_ASSERT(enum_target != NULL); #if defined(DUK_USE_ES6_PROXY) check_existence = (!DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(enum_target)); #else check_existence = 1; #endif duk_pop(ctx); /* still reachable */ DUK_DDD(DUK_DDDPRINT("getting next enum value, enum_target=%!iO, enumerator=%!iT", (duk_heaphdr *) enum_target, (duk_tval *) duk_get_tval(ctx, -1))); /* no array part */ for (;;) { duk_hstring *k; if (idx >= e->e_next) { DUK_DDD(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 (check_existence && !duk_hobject_hasprop_raw(thr, enum_target, k)) { DUK_DDD(DUK_DDDPRINT("property deleted during enumeration, skip")); continue; } DUK_DDD(DUK_DDDPRINT("enumeration: found element, key: %!O", (duk_heaphdr *) k)); res = k; break; } DUK_DDD(DUK_DDDPRINT("enumeration: updating next index to %ld", (long) idx)); duk_push_number(ctx, (double) idx); duk_put_prop_stridx(ctx, -2, DUK_STRIDX_INT_NEXT); /* [... enum] */ if (res) { duk_push_hstring(ctx, res); if (get_value) { duk_push_hobject(ctx, enum_target); duk_dup(ctx, -2); /* -> [... enum key enum_target key] */ duk_get_prop(ctx, -2); /* -> [... enum key enum_target val] */ duk_remove(ctx, -2); /* -> [... enum key val] */ duk_remove(ctx, -3); /* -> [... key val] */ } else { duk_remove(ctx, -2); /* -> [... key] */ } return 1; } else { duk_pop(ctx); /* -> [...] */ return 0; } }
DUK_LOCAL void duk__print_hobject(duk__dprint_state *st, duk_hobject *h) { duk_fixedbuffer *fb = st->fb; duk_uint_fast32_t i; duk_tval *tv; duk_hstring *key; duk_bool_t first = 1; const char *brace1 = "{"; const char *brace2 = "}"; duk_bool_t pushed_loopstack = 0; if (duk_fb_is_full(fb)) { return; } duk__print_shared_heaphdr(st, &h->hdr); if (h && DUK_HOBJECT_HAS_ARRAY_PART(h)) { brace1 = "["; brace2 = "]"; } if (!h) { duk_fb_put_cstring(fb, "NULL"); goto finished; } if (st->depth >= st->depth_limit) { const char *subtype = "generic"; if (DUK_HOBJECT_IS_COMPFUNC(h)) { subtype = "compfunc"; } else if (DUK_HOBJECT_IS_NATFUNC(h)) { subtype = "natfunc"; } else if (DUK_HOBJECT_IS_THREAD(h)) { subtype = "thread"; } else if (DUK_HOBJECT_IS_BUFOBJ(h)) { subtype = "bufobj"; } else if (DUK_HOBJECT_IS_ARRAY(h)) { subtype = "array"; } duk_fb_sprintf(fb, "%sobject/%s %p%s", (const char *) brace1, subtype, (void *) h, (const char *) brace2); return; } for (i = 0; i < (duk_uint_fast32_t) st->loop_stack_index; i++) { if (st->loop_stack[i] == h) { duk_fb_sprintf(fb, "%sLOOP:%p%s", (const char *) brace1, (void *) h, (const char *) brace2); return; } } /* after this, return paths should 'goto finished' for decrement */ st->depth++; if (st->loop_stack_index >= st->loop_stack_limit) { duk_fb_sprintf(fb, "%sOUT-OF-LOOP-STACK%s", (const char *) brace1, (const char *) brace2); goto finished; } st->loop_stack[st->loop_stack_index++] = h; pushed_loopstack = 1; /* * Notation: double underscore used for internal properties which are not * stored in the property allocation (e.g. '__valstack'). */ duk_fb_put_cstring(fb, brace1); if (DUK_HOBJECT_GET_PROPS(NULL, h)) { duk_uint32_t a_limit; a_limit = DUK_HOBJECT_GET_ASIZE(h); if (st->internal) { /* dump all allocated entries, unused entries print as 'unused', * note that these may extend beyond current 'length' and look * a bit funny. */ } else { /* leave out trailing 'unused' elements */ while (a_limit > 0) { tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, a_limit - 1); if (!DUK_TVAL_IS_UNUSED(tv)) { break; } a_limit--; } } for (i = 0; i < a_limit; i++) { tv = DUK_HOBJECT_A_GET_VALUE_PTR(NULL, h, i); DUK__COMMA(); duk__print_tval(st, tv); } for (i = 0; i < DUK_HOBJECT_GET_ENEXT(h); i++) { key = DUK_HOBJECT_E_GET_KEY(NULL, h, i); if (!key) { continue; } if (!st->internal && DUK_HSTRING_HAS_HIDDEN(key)) { continue; } DUK__COMMA(); duk__print_hstring(st, key, 0); duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COLON); if (DUK_HOBJECT_E_SLOT_IS_ACCESSOR(NULL, h, i)) { duk_fb_sprintf(fb, "[get:%p,set:%p]", (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.get, (void *) DUK_HOBJECT_E_GET_VALUE(NULL, h, i).a.set); } else { tv = &DUK_HOBJECT_E_GET_VALUE(NULL, h, i).v; duk__print_tval(st, tv); } if (st->heavy) { duk_fb_sprintf(fb, "<%02lx>", (unsigned long) DUK_HOBJECT_E_GET_FLAGS(NULL, h, i)); } } } if (st->internal) { if (DUK_HOBJECT_HAS_EXTENSIBLE(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__extensible:true"); } if (DUK_HOBJECT_HAS_CONSTRUCTABLE(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__constructable:true"); } if (DUK_HOBJECT_HAS_BOUNDFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__boundfunc:true"); } if (DUK_HOBJECT_HAS_COMPFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__compfunc:true"); } if (DUK_HOBJECT_HAS_NATFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__natfunc:true"); } if (DUK_HOBJECT_HAS_BUFOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__bufobj:true"); } if (DUK_HOBJECT_HAS_THREAD(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__thread:true"); } if (DUK_HOBJECT_HAS_ARRAY_PART(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__array_part:true"); } if (DUK_HOBJECT_HAS_STRICT(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__strict:true"); } if (DUK_HOBJECT_HAS_NOTAIL(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__notail:true"); } if (DUK_HOBJECT_HAS_NEWENV(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__newenv:true"); } if (DUK_HOBJECT_HAS_NAMEBINDING(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__namebinding:true"); } if (DUK_HOBJECT_HAS_CREATEARGS(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__createargs:true"); } if (DUK_HOBJECT_HAS_ENVRECCLOSED(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__envrecclosed:true"); } if (DUK_HOBJECT_HAS_EXOTIC_ARRAY(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_array:true"); } if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_stringobj:true"); } if (DUK_HOBJECT_HAS_EXOTIC_ARGUMENTS(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_arguments:true"); } if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_dukfunc:true"); } if (DUK_HOBJECT_IS_BUFOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_bufobj:true"); } if (DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(h)) { DUK__COMMA(); duk_fb_sprintf(fb, "__exotic_proxyobj:true"); } } if (st->internal && DUK_HOBJECT_IS_ARRAY(h)) { duk_harray *a = (duk_harray *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) a->length); DUK__COMMA(); duk_fb_sprintf(fb, "__length_nonwritable:%ld", (long) a->length_nonwritable); } else if (st->internal && DUK_HOBJECT_IS_COMPFUNC(h)) { duk_hcompfunc *f = (duk_hcompfunc *) h; DUK__COMMA(); duk_fb_put_cstring(fb, "__data:"); duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f)); DUK__COMMA(); duk_fb_put_cstring(fb, "__lexenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_LEXENV(NULL, f)); DUK__COMMA(); duk_fb_put_cstring(fb, "__varenv:"); duk__print_hobject(st, DUK_HCOMPFUNC_GET_VARENV(NULL, f)); DUK__COMMA(); duk_fb_sprintf(fb, "__nregs:%ld", (long) f->nregs); DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); #if defined(DUK_USE_DEBUGGER_SUPPORT) DUK__COMMA(); duk_fb_sprintf(fb, "__start_line:%ld", (long) f->start_line); DUK__COMMA(); duk_fb_sprintf(fb, "__end_line:%ld", (long) f->end_line); #endif DUK__COMMA(); duk_fb_put_cstring(fb, "__data:"); duk__print_hbuffer(st, (duk_hbuffer *) DUK_HCOMPFUNC_GET_DATA(NULL, f)); } else if (st->internal && DUK_HOBJECT_IS_NATFUNC(h)) { duk_hnatfunc *f = (duk_hnatfunc *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__func:"); duk_fb_put_funcptr(fb, (duk_uint8_t *) &f->func, sizeof(f->func)); DUK__COMMA(); duk_fb_sprintf(fb, "__nargs:%ld", (long) f->nargs); DUK__COMMA(); duk_fb_sprintf(fb, "__magic:%ld", (long) f->magic); #if defined(DUK_USE_BUFFEROBJECT_SUPPORT) } else if (st->internal && DUK_HOBJECT_IS_BUFOBJ(h)) { duk_hbufobj *b = (duk_hbufobj *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__buf:"); duk__print_hbuffer(st, (duk_hbuffer *) b->buf); DUK__COMMA(); duk_fb_sprintf(fb, "__buf_prop:"); duk__print_hobject(st, (duk_hobject *) b->buf_prop); DUK__COMMA(); duk_fb_sprintf(fb, "__offset:%ld", (long) b->offset); DUK__COMMA(); duk_fb_sprintf(fb, "__length:%ld", (long) b->length); DUK__COMMA(); duk_fb_sprintf(fb, "__shift:%ld", (long) b->shift); DUK__COMMA(); duk_fb_sprintf(fb, "__elemtype:%ld", (long) b->elem_type); #endif } else if (st->internal && DUK_HOBJECT_IS_THREAD(h)) { duk_hthread *t = (duk_hthread *) h; DUK__COMMA(); duk_fb_sprintf(fb, "__strict:%ld", (long) t->strict); DUK__COMMA(); duk_fb_sprintf(fb, "__state:%ld", (long) t->state); DUK__COMMA(); duk_fb_sprintf(fb, "__unused1:%ld", (long) t->unused1); DUK__COMMA(); duk_fb_sprintf(fb, "__unused2:%ld", (long) t->unused2); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_max:%ld", (long) t->valstack_max); DUK__COMMA(); duk_fb_sprintf(fb, "__callstack_max:%ld", (long) t->callstack_max); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_max:%ld", (long) t->catchstack_max); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack:%p", (void *) t->valstack); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_end:%p/%ld", (void *) t->valstack_end, (long) (t->valstack_end - t->valstack)); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_bottom:%p/%ld", (void *) t->valstack_bottom, (long) (t->valstack_bottom - t->valstack)); DUK__COMMA(); duk_fb_sprintf(fb, "__valstack_top:%p/%ld", (void *) t->valstack_top, (long) (t->valstack_top - t->valstack)); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack:%p", (void *) t->catchstack); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_size:%ld", (long) t->catchstack_size); DUK__COMMA(); duk_fb_sprintf(fb, "__catchstack_top:%ld", (long) t->catchstack_top); DUK__COMMA(); duk_fb_sprintf(fb, "__resumer:"); duk__print_hobject(st, (duk_hobject *) t->resumer); /* XXX: print built-ins array? */ } #if defined(DUK_USE_REFERENCE_COUNTING) if (st->internal) { DUK__COMMA(); duk_fb_sprintf(fb, "__refcount:%lu", (unsigned long) DUK_HEAPHDR_GET_REFCOUNT((duk_heaphdr *) h)); } #endif if (st->internal) { DUK__COMMA(); duk_fb_sprintf(fb, "__class:%ld", (long) DUK_HOBJECT_GET_CLASS_NUMBER(h)); } DUK__COMMA(); duk_fb_sprintf(fb, "__heapptr:%p", (void *) h); /* own pointer */ /* prototype should be last, for readability */ if (DUK_HOBJECT_GET_PROTOTYPE(NULL, h)) { if (st->follow_proto) { DUK__COMMA(); duk_fb_put_cstring(fb, "__prototype:"); duk__print_hobject(st, DUK_HOBJECT_GET_PROTOTYPE(NULL, h)); } else { DUK__COMMA(); duk_fb_sprintf(fb, "__prototype:%p", (void *) DUK_HOBJECT_GET_PROTOTYPE(NULL, h)); } } duk_fb_put_cstring(fb, brace2); #if defined(DUK_USE_HOBJECT_HASH_PART) if (st->heavy && DUK_HOBJECT_GET_HSIZE(h) > 0) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_LANGLE); for (i = 0; i < DUK_HOBJECT_GET_HSIZE(h); i++) { duk_uint_t h_idx = DUK_HOBJECT_H_GET_INDEX(NULL, h, i); if (i > 0) { duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_COMMA); } if (h_idx == DUK_HOBJECT_HASHIDX_UNUSED) { duk_fb_sprintf(fb, "u"); } else if (h_idx == DUK_HOBJECT_HASHIDX_DELETED) { duk_fb_sprintf(fb, "d"); } else { duk_fb_sprintf(fb, "%ld", (long) h_idx); } } duk_fb_put_byte(fb, (duk_uint8_t) DUK_ASC_RANGLE); } #endif finished: st->depth--; if (pushed_loopstack) { st->loop_stack_index--; st->loop_stack[st->loop_stack_index] = NULL; } }