]> git.proxmox.com Git - ceph.git/blob - ceph/src/civetweb/src/third_party/duktape-1.5.2/src-separate/duk_hobject_enum.c
75142ab56999d8c8397a551d2ff30557b558b4b4
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.5.2 / src-separate / duk_hobject_enum.c
1 /*
2 * Hobject enumeration support.
3 *
4 * Creates an internal enumeration state object to be used e.g. with for-in
5 * enumeration. The state object contains a snapshot of target object keys
6 * and internal control state for enumeration. Enumerator flags allow caller
7 * to e.g. request internal/non-enumerable properties, and to enumerate only
8 * "own" properties.
9 *
10 * Also creates the result value for e.g. Object.keys() based on the same
11 * internal structure.
12 *
13 * This snapshot-based enumeration approach is used to simplify enumeration:
14 * non-snapshot-based approaches are difficult to reconcile with mutating
15 * the enumeration target, running multiple long-lived enumerators at the
16 * same time, garbage collection details, etc. The downside is that the
17 * enumerator object is memory inefficient especially for iterating arrays.
18 */
19
20 #include "duk_internal.h"
21
22 /* XXX: identify enumeration target with an object index (not top of stack) */
23
24 /* must match exactly the number of internal properties inserted to enumerator */
25 #define DUK__ENUM_START_INDEX 2
26
27 DUK_LOCAL const duk_uint16_t duk__bufferobject_virtual_props[] = {
28 DUK_STRIDX_LENGTH,
29 DUK_STRIDX_BYTE_LENGTH,
30 DUK_STRIDX_BYTE_OFFSET,
31 DUK_STRIDX_BYTES_PER_ELEMENT
32 };
33
34 /*
35 * Helper to sort array index keys. The keys are in the enumeration object
36 * entry part, starting from DUK__ENUM_START_INDEX, and the entry part is dense.
37 *
38 * We use insertion sort because it is simple (leading to compact code,)
39 * works nicely in-place, and minimizes operations if data is already sorted
40 * or nearly sorted (which is a very common case here). It also minimizes
41 * the use of element comparisons in general. This is nice because element
42 * comparisons here involve re-parsing the string keys into numbers each
43 * time, which is naturally very expensive.
44 *
45 * Note that the entry part values are all "true", e.g.
46 *
47 * "1" -> true, "3" -> true, "2" -> true
48 *
49 * so it suffices to only work in the key part without exchanging any keys,
50 * simplifying the sort.
51 *
52 * http://en.wikipedia.org/wiki/Insertion_sort
53 *
54 * (Compiles to about 160 bytes now as a stand-alone function.)
55 */
56
57 DUK_LOCAL void duk__sort_array_indices(duk_hthread *thr, duk_hobject *h_obj) {
58 duk_hstring **keys;
59 duk_hstring **p_curr, **p_insert, **p_end;
60 duk_hstring *h_curr;
61 duk_uarridx_t val_highest, val_curr, val_insert;
62
63 DUK_ASSERT(h_obj != NULL);
64 DUK_ASSERT(DUK_HOBJECT_GET_ENEXT(h_obj) >= 2); /* control props */
65 DUK_UNREF(thr);
66
67 if (DUK_HOBJECT_GET_ENEXT(h_obj) <= 1 + DUK__ENUM_START_INDEX) {
68 return;
69 }
70
71 keys = DUK_HOBJECT_E_GET_KEY_BASE(thr->heap, h_obj);
72 p_end = keys + DUK_HOBJECT_GET_ENEXT(h_obj);
73 keys += DUK__ENUM_START_INDEX;
74
75 DUK_DDD(DUK_DDDPRINT("keys=%p, p_end=%p (after skipping enum props)",
76 (void *) keys, (void *) p_end));
77
78 #ifdef DUK_USE_DDDPRINT
79 {
80 duk_uint_fast32_t i;
81 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h_obj); i++) {
82 DUK_DDD(DUK_DDDPRINT("initial: %ld %p -> %!O",
83 (long) i,
84 (void *) DUK_HOBJECT_E_GET_KEY_PTR(thr->heap, h_obj, i),
85 (duk_heaphdr *) DUK_HOBJECT_E_GET_KEY(thr->heap, h_obj, i)));
86 }
87 }
88 #endif
89
90 val_highest = DUK_HSTRING_GET_ARRIDX_SLOW(keys[0]);
91 for (p_curr = keys + 1; p_curr < p_end; p_curr++) {
92 DUK_ASSERT(*p_curr != NULL);
93 val_curr = DUK_HSTRING_GET_ARRIDX_SLOW(*p_curr);
94
95 if (val_curr >= val_highest) {
96 DUK_DDD(DUK_DDDPRINT("p_curr=%p, p_end=%p, val_highest=%ld, val_curr=%ld -> "
97 "already in correct order, next",
98 (void *) p_curr, (void *) p_end, (long) val_highest, (long) val_curr));
99 val_highest = val_curr;
100 continue;
101 }
102
103 DUK_DDD(DUK_DDDPRINT("p_curr=%p, p_end=%p, val_highest=%ld, val_curr=%ld -> "
104 "needs to be inserted",
105 (void *) p_curr, (void *) p_end, (long) val_highest, (long) val_curr));
106
107 /* Needs to be inserted; scan backwards, since we optimize
108 * for the case where elements are nearly in order.
109 */
110
111 p_insert = p_curr - 1;
112 for (;;) {
113 val_insert = DUK_HSTRING_GET_ARRIDX_SLOW(*p_insert);
114 if (val_insert < val_curr) {
115 DUK_DDD(DUK_DDDPRINT("p_insert=%p, val_insert=%ld, val_curr=%ld -> insert after this",
116 (void *) p_insert, (long) val_insert, (long) val_curr));
117 p_insert++;
118 break;
119 }
120 if (p_insert == keys) {
121 DUK_DDD(DUK_DDDPRINT("p_insert=%p -> out of keys, insert to beginning", (void *) p_insert));
122 break;
123 }
124 DUK_DDD(DUK_DDDPRINT("p_insert=%p, val_insert=%ld, val_curr=%ld -> search backwards",
125 (void *) p_insert, (long) val_insert, (long) val_curr));
126 p_insert--;
127 }
128
129 DUK_DDD(DUK_DDDPRINT("final p_insert=%p", (void *) p_insert));
130
131 /* .-- p_insert .-- p_curr
132 * v v
133 * | ... | insert | ... | curr
134 */
135
136 h_curr = *p_curr;
137 DUK_DDD(DUK_DDDPRINT("memmove: dest=%p, src=%p, size=%ld, h_curr=%p",
138 (void *) (p_insert + 1), (void *) p_insert,
139 (long) (p_curr - p_insert), (void *) h_curr));
140
141 DUK_MEMMOVE((void *) (p_insert + 1),
142 (const void *) p_insert,
143 (size_t) ((p_curr - p_insert) * sizeof(duk_hstring *)));
144 *p_insert = h_curr;
145 /* keep val_highest */
146 }
147
148 #ifdef DUK_USE_DDDPRINT
149 {
150 duk_uint_fast32_t i;
151 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(h_obj); i++) {
152 DUK_DDD(DUK_DDDPRINT("final: %ld %p -> %!O",
153 (long) i,
154 (void *) DUK_HOBJECT_E_GET_KEY_PTR(thr->heap, h_obj, i),
155 (duk_heaphdr *) DUK_HOBJECT_E_GET_KEY(thr->heap, h_obj, i)));
156 }
157 }
158 #endif
159 }
160
161 /*
162 * Create an internal enumerator object E, which has its keys ordered
163 * to match desired enumeration ordering. Also initialize internal control
164 * properties for enumeration.
165 *
166 * Note: if an array was used to hold enumeration keys instead, an array
167 * scan would be needed to eliminate duplicates found in the prototype chain.
168 */
169
170 DUK_INTERNAL void duk_hobject_enumerator_create(duk_context *ctx, duk_small_uint_t enum_flags) {
171 duk_hthread *thr = (duk_hthread *) ctx;
172 duk_hobject *enum_target;
173 duk_hobject *curr;
174 duk_hobject *res;
175 #if defined(DUK_USE_ES6_PROXY)
176 duk_hobject *h_proxy_target;
177 duk_hobject *h_proxy_handler;
178 duk_hobject *h_trap_result;
179 #endif
180 duk_uint_fast32_t i, len; /* used for array, stack, and entry indices */
181
182 DUK_ASSERT(ctx != NULL);
183
184 DUK_DDD(DUK_DDDPRINT("create enumerator, stack top: %ld", (long) duk_get_top(ctx)));
185
186 enum_target = duk_require_hobject(ctx, -1);
187 DUK_ASSERT(enum_target != NULL);
188
189 duk_push_object_internal(ctx);
190 res = duk_require_hobject(ctx, -1);
191
192 DUK_DDD(DUK_DDDPRINT("created internal object"));
193
194 /* [enum_target res] */
195
196 /* Target must be stored so that we can recheck whether or not
197 * keys still exist when we enumerate. This is not done if the
198 * enumeration result comes from a proxy trap as there is no
199 * real object to check against.
200 */
201 duk_push_hobject(ctx, enum_target);
202 duk_put_prop_stridx(ctx, -2, DUK_STRIDX_INT_TARGET);
203
204 /* Initialize index so that we skip internal control keys. */
205 duk_push_int(ctx, DUK__ENUM_START_INDEX);
206 duk_put_prop_stridx(ctx, -2, DUK_STRIDX_INT_NEXT);
207
208 /*
209 * Proxy object handling
210 */
211
212 #if defined(DUK_USE_ES6_PROXY)
213 if (DUK_LIKELY((enum_flags & DUK_ENUM_NO_PROXY_BEHAVIOR) != 0)) {
214 goto skip_proxy;
215 }
216 if (DUK_LIKELY(!duk_hobject_proxy_check(thr,
217 enum_target,
218 &h_proxy_target,
219 &h_proxy_handler))) {
220 goto skip_proxy;
221 }
222
223 DUK_DDD(DUK_DDDPRINT("proxy enumeration"));
224 duk_push_hobject(ctx, h_proxy_handler);
225 if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_ENUMERATE)) {
226 /* No need to replace the 'enum_target' value in stack, only the
227 * enum_target reference. This also ensures that the original
228 * enum target is reachable, which keeps the proxy and the proxy
229 * target reachable. We do need to replace the internal _Target.
230 */
231 DUK_DDD(DUK_DDDPRINT("no enumerate trap, enumerate proxy target instead"));
232 DUK_DDD(DUK_DDDPRINT("h_proxy_target=%!O", (duk_heaphdr *) h_proxy_target));
233 enum_target = h_proxy_target;
234
235 duk_push_hobject(ctx, enum_target); /* -> [ ... enum_target res handler undefined target ] */
236 duk_put_prop_stridx(ctx, -4, DUK_STRIDX_INT_TARGET);
237
238 duk_pop_2(ctx); /* -> [ ... enum_target res ] */
239 goto skip_proxy;
240 }
241
242 /* [ ... enum_target res handler trap ] */
243 duk_insert(ctx, -2);
244 duk_push_hobject(ctx, h_proxy_target); /* -> [ ... enum_target res trap handler target ] */
245 duk_call_method(ctx, 1 /*nargs*/); /* -> [ ... enum_target res trap_result ] */
246 h_trap_result = duk_require_hobject(ctx, -1);
247 DUK_UNREF(h_trap_result);
248
249 /* Copy trap result keys into the enumerator object. */
250 len = (duk_uint_fast32_t) duk_get_length(ctx, -1);
251 for (i = 0; i < len; i++) {
252 /* XXX: not sure what the correct semantic details are here,
253 * e.g. handling of missing values (gaps), handling of non-array
254 * trap results, etc.
255 *
256 * For keys, we simply skip non-string keys which seems to be
257 * consistent with how e.g. Object.keys() will process proxy trap
258 * results (ES6, Section 19.1.2.14).
259 */
260 if (duk_get_prop_index(ctx, -1, i) && duk_is_string(ctx, -1)) {
261 /* [ ... enum_target res trap_result val ] */
262 duk_push_true(ctx);
263 /* [ ... enum_target res trap_result val true ] */
264 duk_put_prop(ctx, -4);
265 } else {
266 duk_pop(ctx);
267 }
268 }
269 /* [ ... enum_target res trap_result ] */
270 duk_pop(ctx);
271 duk_remove(ctx, -2);
272
273 /* [ ... res ] */
274
275 /* The internal _Target property is kept pointing to the original
276 * enumeration target (the proxy object), so that the enumerator
277 * 'next' operation can read property values if so requested. The
278 * fact that the _Target is a proxy disables key existence check
279 * during enumeration.
280 */
281 DUK_DDD(DUK_DDDPRINT("proxy enumeration, final res: %!O", (duk_heaphdr *) res));
282 goto compact_and_return;
283
284 skip_proxy:
285 #endif /* DUK_USE_ES6_PROXY */
286
287 curr = enum_target;
288 while (curr) {
289 /*
290 * Virtual properties.
291 *
292 * String and buffer indices are virtual and always enumerable,
293 * 'length' is virtual and non-enumerable. Array and arguments
294 * object props have special behavior but are concrete.
295 */
296
297 if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr) ||
298 DUK_HOBJECT_IS_BUFFEROBJECT(curr)) {
299 /* String and buffer enumeration behavior is identical now,
300 * so use shared handler.
301 */
302 if (DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr)) {
303 duk_hstring *h_val;
304 h_val = duk_hobject_get_internal_value_string(thr->heap, curr);
305 DUK_ASSERT(h_val != NULL); /* string objects must not created without internal value */
306 len = (duk_uint_fast32_t) DUK_HSTRING_GET_CHARLEN(h_val);
307 } else {
308 duk_hbufferobject *h_bufobj;
309 DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT(curr));
310 h_bufobj = (duk_hbufferobject *) curr;
311 if (h_bufobj == NULL) {
312 /* Neutered buffer, zero length seems
313 * like good behavior here.
314 */
315 len = 0;
316 } else {
317 /* There's intentionally no check for
318 * current underlying buffer length.
319 */
320 len = (duk_uint_fast32_t) (h_bufobj->length >> h_bufobj->shift);
321 }
322 }
323
324 for (i = 0; i < len; i++) {
325 duk_hstring *k;
326
327 k = duk_heap_string_intern_u32_checked(thr, i);
328 DUK_ASSERT(k);
329 duk_push_hstring(ctx, k);
330 duk_push_true(ctx);
331
332 /* [enum_target res key true] */
333 duk_put_prop(ctx, -3);
334
335 /* [enum_target res] */
336 }
337
338 /* 'length' and other virtual properties are not
339 * enumerable, but are included if non-enumerable
340 * properties are requested.
341 */
342
343 if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) {
344 duk_uint_fast32_t n;
345
346 if (DUK_HOBJECT_IS_BUFFEROBJECT(curr)) {
347 n = sizeof(duk__bufferobject_virtual_props) / sizeof(duk_uint16_t);
348 } else {
349 DUK_ASSERT(DUK_HOBJECT_HAS_EXOTIC_STRINGOBJ(curr));
350 DUK_ASSERT(duk__bufferobject_virtual_props[0] == DUK_STRIDX_LENGTH);
351 n = 1; /* only 'length' */
352 }
353
354 for (i = 0; i < n; i++) {
355 duk_push_hstring_stridx(ctx, duk__bufferobject_virtual_props[i]);
356 duk_push_true(ctx);
357 duk_put_prop(ctx, -3);
358 }
359
360 }
361 } else if (DUK_HOBJECT_HAS_EXOTIC_DUKFUNC(curr)) {
362 if (enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE) {
363 duk_push_hstring_stridx(ctx, DUK_STRIDX_LENGTH);
364 duk_push_true(ctx);
365 duk_put_prop(ctx, -3);
366 }
367 }
368
369 /*
370 * Array part
371 *
372 * Note: ordering between array and entry part must match 'abandon array'
373 * behavior in duk_hobject_props.c: key order after an array is abandoned
374 * must be the same.
375 */
376
377 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ASIZE(curr); i++) {
378 duk_hstring *k;
379 duk_tval *tv;
380
381 tv = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, curr, i);
382 if (DUK_TVAL_IS_UNUSED(tv)) {
383 continue;
384 }
385 k = duk_heap_string_intern_u32_checked(thr, i);
386 DUK_ASSERT(k);
387
388 duk_push_hstring(ctx, k);
389 duk_push_true(ctx);
390
391 /* [enum_target res key true] */
392 duk_put_prop(ctx, -3);
393
394 /* [enum_target res] */
395 }
396
397 /*
398 * Entries part
399 */
400
401 for (i = 0; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(curr); i++) {
402 duk_hstring *k;
403
404 k = DUK_HOBJECT_E_GET_KEY(thr->heap, curr, i);
405 if (!k) {
406 continue;
407 }
408 if (!DUK_HOBJECT_E_SLOT_IS_ENUMERABLE(thr->heap, curr, i) &&
409 !(enum_flags & DUK_ENUM_INCLUDE_NONENUMERABLE)) {
410 continue;
411 }
412 if (DUK_HSTRING_HAS_INTERNAL(k) &&
413 !(enum_flags & DUK_ENUM_INCLUDE_INTERNAL)) {
414 continue;
415 }
416 if ((enum_flags & DUK_ENUM_ARRAY_INDICES_ONLY) &&
417 (DUK_HSTRING_GET_ARRIDX_SLOW(k) == DUK_HSTRING_NO_ARRAY_INDEX)) {
418 continue;
419 }
420
421 DUK_ASSERT(DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, curr, i) ||
422 !DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE_PTR(thr->heap, curr, i)->v));
423
424 duk_push_hstring(ctx, k);
425 duk_push_true(ctx);
426
427 /* [enum_target res key true] */
428 duk_put_prop(ctx, -3);
429
430 /* [enum_target res] */
431 }
432
433 if (enum_flags & DUK_ENUM_OWN_PROPERTIES_ONLY) {
434 break;
435 }
436
437 curr = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, curr);
438 }
439
440 /* [enum_target res] */
441
442 duk_remove(ctx, -2);
443
444 /* [res] */
445
446 if ((enum_flags & (DUK_ENUM_ARRAY_INDICES_ONLY | DUK_ENUM_SORT_ARRAY_INDICES)) ==
447 (DUK_ENUM_ARRAY_INDICES_ONLY | DUK_ENUM_SORT_ARRAY_INDICES)) {
448 /*
449 * Some E5/E5.1 algorithms require that array indices are iterated
450 * in a strictly ascending order. This is the case for e.g.
451 * Array.prototype.forEach() and JSON.stringify() PropertyList
452 * handling.
453 *
454 * To ensure this property for arrays with an array part (and
455 * arbitrary objects too, since e.g. forEach() can be applied
456 * to an array), the caller can request that we sort the keys
457 * here.
458 */
459
460 /* XXX: avoid this at least when enum_target is an Array, it has an
461 * array part, and no ancestor properties were included? Not worth
462 * it for JSON, but maybe worth it for forEach().
463 */
464
465 /* XXX: may need a 'length' filter for forEach()
466 */
467 DUK_DDD(DUK_DDDPRINT("sort array indices by caller request"));
468 duk__sort_array_indices(thr, res);
469 }
470
471 #if defined(DUK_USE_ES6_PROXY)
472 compact_and_return:
473 #endif
474 /* compact; no need to seal because object is internal */
475 duk_hobject_compact_props(thr, res);
476
477 DUK_DDD(DUK_DDDPRINT("created enumerator object: %!iT", (duk_tval *) duk_get_tval(ctx, -1)));
478 }
479
480 /*
481 * Returns non-zero if a key and/or value was enumerated, and:
482 *
483 * [enum] -> [key] (get_value == 0)
484 * [enum] -> [key value] (get_value == 1)
485 *
486 * Returns zero without pushing anything on the stack otherwise.
487 */
488 DUK_INTERNAL duk_bool_t duk_hobject_enumerator_next(duk_context *ctx, duk_bool_t get_value) {
489 duk_hthread *thr = (duk_hthread *) ctx;
490 duk_hobject *e;
491 duk_hobject *enum_target;
492 duk_hstring *res = NULL;
493 duk_uint_fast32_t idx;
494 duk_bool_t check_existence;
495
496 DUK_ASSERT(ctx != NULL);
497
498 /* [... enum] */
499
500 e = duk_require_hobject(ctx, -1);
501
502 /* XXX use get tval ptr, more efficient */
503 duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_NEXT);
504 idx = (duk_uint_fast32_t) duk_require_uint(ctx, -1);
505 duk_pop(ctx);
506 DUK_DDD(DUK_DDDPRINT("enumeration: index is: %ld", (long) idx));
507
508 /* Enumeration keys are checked against the enumeration target (to see
509 * that they still exist). In the proxy enumeration case _Target will
510 * be the proxy, and checking key existence against the proxy is not
511 * required (or sensible, as the keys may be fully virtual).
512 */
513 duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_TARGET);
514 enum_target = duk_require_hobject(ctx, -1);
515 DUK_ASSERT(enum_target != NULL);
516 #if defined(DUK_USE_ES6_PROXY)
517 check_existence = (!DUK_HOBJECT_HAS_EXOTIC_PROXYOBJ(enum_target));
518 #else
519 check_existence = 1;
520 #endif
521 duk_pop(ctx); /* still reachable */
522
523 DUK_DDD(DUK_DDDPRINT("getting next enum value, enum_target=%!iO, enumerator=%!iT",
524 (duk_heaphdr *) enum_target, (duk_tval *) duk_get_tval(ctx, -1)));
525
526 /* no array part */
527 for (;;) {
528 duk_hstring *k;
529
530 if (idx >= DUK_HOBJECT_GET_ENEXT(e)) {
531 DUK_DDD(DUK_DDDPRINT("enumeration: ran out of elements"));
532 break;
533 }
534
535 /* we know these because enum objects are internally created */
536 k = DUK_HOBJECT_E_GET_KEY(thr->heap, e, idx);
537 DUK_ASSERT(k != NULL);
538 DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, e, idx));
539 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(&DUK_HOBJECT_E_GET_VALUE(thr->heap, e, idx).v));
540
541 idx++;
542
543 /* recheck that the property still exists */
544 if (check_existence && !duk_hobject_hasprop_raw(thr, enum_target, k)) {
545 DUK_DDD(DUK_DDDPRINT("property deleted during enumeration, skip"));
546 continue;
547 }
548
549 DUK_DDD(DUK_DDDPRINT("enumeration: found element, key: %!O", (duk_heaphdr *) k));
550 res = k;
551 break;
552 }
553
554 DUK_DDD(DUK_DDDPRINT("enumeration: updating next index to %ld", (long) idx));
555
556 duk_push_u32(ctx, (duk_uint32_t) idx);
557 duk_put_prop_stridx(ctx, -2, DUK_STRIDX_INT_NEXT);
558
559 /* [... enum] */
560
561 if (res) {
562 duk_push_hstring(ctx, res);
563 if (get_value) {
564 duk_push_hobject(ctx, enum_target);
565 duk_dup(ctx, -2); /* -> [... enum key enum_target key] */
566 duk_get_prop(ctx, -2); /* -> [... enum key enum_target val] */
567 duk_remove(ctx, -2); /* -> [... enum key val] */
568 duk_remove(ctx, -3); /* -> [... key val] */
569 } else {
570 duk_remove(ctx, -2); /* -> [... key] */
571 }
572 return 1;
573 } else {
574 duk_pop(ctx); /* -> [...] */
575 return 0;
576 }
577 }
578
579 /*
580 * Get enumerated keys in an Ecmascript array. Matches Object.keys() behavior
581 * described in E5 Section 15.2.3.14.
582 */
583
584 DUK_INTERNAL duk_ret_t duk_hobject_get_enumerated_keys(duk_context *ctx, duk_small_uint_t enum_flags) {
585 duk_hthread *thr = (duk_hthread *) ctx;
586 duk_hobject *e;
587 duk_uint_fast32_t i;
588 duk_uint_fast32_t idx;
589
590 DUK_ASSERT(ctx != NULL);
591 DUK_ASSERT(duk_get_hobject(ctx, -1) != NULL);
592 DUK_UNREF(thr);
593
594 /* Create a temporary enumerator to get the (non-duplicated) key list;
595 * the enumerator state is initialized without being needed, but that
596 * has little impact.
597 */
598
599 duk_hobject_enumerator_create(ctx, enum_flags);
600 duk_push_array(ctx);
601
602 /* [enum_target enum res] */
603
604 e = duk_require_hobject(ctx, -2);
605 DUK_ASSERT(e != NULL);
606
607 idx = 0;
608 for (i = DUK__ENUM_START_INDEX; i < (duk_uint_fast32_t) DUK_HOBJECT_GET_ENEXT(e); i++) {
609 duk_hstring *k;
610
611 k = DUK_HOBJECT_E_GET_KEY(thr->heap, e, i);
612 DUK_ASSERT(k); /* enumerator must have no keys deleted */
613
614 /* [enum_target enum res] */
615 duk_push_hstring(ctx, k);
616 duk_put_prop_index(ctx, -2, idx);
617 idx++;
618 }
619
620 /* [enum_target enum res] */
621 duk_remove(ctx, -2);
622
623 /* [enum_target res] */
624
625 return 1; /* return 1 to allow callers to tail call */
626 }