]> git.proxmox.com Git - ceph.git/blame - ceph/src/civetweb/src/third_party/duktape-1.5.2/src-separate/duk_api_codec.c
buildsys: switch source download to quincy
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.5.2 / src-separate / duk_api_codec.c
CommitLineData
11fdf7f2
TL
1/*
2 * Encoding and decoding basic formats: hex, base64.
3 *
4 * These are in-place operations which may allow an optimized implementation.
5 *
6 * Base-64: https://tools.ietf.org/html/rfc4648#section-4
7 */
8
9#include "duk_internal.h"
10
11/* Shared handling for encode/decode argument. Fast path handling for
12 * buffer and string values because they're the most common. In particular,
13 * avoid creating a temporary string or buffer when possible.
14 */
15DUK_LOCAL const duk_uint8_t *duk__prep_codec_arg(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
16 DUK_ASSERT(duk_is_valid_index(ctx, index)); /* checked by caller */
17 if (duk_is_buffer(ctx, index)) {
18 return (const duk_uint8_t *) duk_get_buffer(ctx, index, out_len);
19 } else {
20 return (const duk_uint8_t *) duk_to_lstring(ctx, index, out_len);
21 }
22}
23
24#if defined(DUK_USE_BASE64_FASTPATH)
25DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) {
26 duk_uint_t t;
27 duk_size_t n_full, n_full3, n_final;
28 const duk_uint8_t *src_end_fast;
29
30 n_full = srclen / 3; /* full 3-byte -> 4-char conversions */
31 n_full3 = n_full * 3;
32 n_final = srclen - n_full3;
33 DUK_ASSERT_DISABLE(n_final >= 0);
34 DUK_ASSERT(n_final <= 2);
35
36 src_end_fast = src + n_full3;
37 while (DUK_UNLIKELY(src != src_end_fast)) {
38 t = (duk_uint_t) (*src++);
39 t = (t << 8) + (duk_uint_t) (*src++);
40 t = (t << 8) + (duk_uint_t) (*src++);
41
42 *dst++ = duk_base64_enctab[t >> 18];
43 *dst++ = duk_base64_enctab[(t >> 12) & 0x3f];
44 *dst++ = duk_base64_enctab[(t >> 6) & 0x3f];
45 *dst++ = duk_base64_enctab[t & 0x3f];
46
47#if 0 /* Tested: not faster on x64 */
48 /* aaaaaabb bbbbcccc ccdddddd */
49 dst[0] = duk_base64_enctab[(src[0] >> 2) & 0x3f];
50 dst[1] = duk_base64_enctab[((src[0] << 4) & 0x30) | ((src[1] >> 4) & 0x0f)];
51 dst[2] = duk_base64_enctab[((src[1] << 2) & 0x3f) | ((src[2] >> 6) & 0x03)];
52 dst[3] = duk_base64_enctab[src[2] & 0x3f];
53 src += 3; dst += 4;
54#endif
55 }
56
57 switch (n_final) {
58 /* case 0: nop */
59 case 1: {
60 /* XX== */
61 t = (duk_uint_t) (*src++);
62 *dst++ = duk_base64_enctab[t >> 2]; /* XXXXXX-- */
63 *dst++ = duk_base64_enctab[(t << 4) & 0x3f]; /* ------XX */
64 *dst++ = DUK_ASC_EQUALS;
65 *dst++ = DUK_ASC_EQUALS;
66 break;
67 }
68 case 2: {
69 /* XXX= */
70 t = (duk_uint_t) (*src++);
71 t = (t << 8) + (duk_uint_t) (*src++);
72 *dst++ = duk_base64_enctab[t >> 10]; /* XXXXXX-- -------- */
73 *dst++ = duk_base64_enctab[(t >> 4) & 0x3f]; /* ------XX XXXX---- */
74 *dst++ = duk_base64_enctab[(t << 2) & 0x3f]; /* -------- ----XXXX */
75 *dst++ = DUK_ASC_EQUALS;
76 break;
77 }
78 }
79}
80#else /* DUK_USE_BASE64_FASTPATH */
81DUK_LOCAL void duk__base64_encode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst) {
82 duk_small_uint_t i, snip;
83 duk_uint_t t;
84 duk_uint_fast8_t x, y;
85 const duk_uint8_t *src_end;
86
87 src_end = src + srclen;
88
89 while (src < src_end) {
90 /* read 3 bytes into 't', padded by zero */
91 snip = 4;
92 t = 0;
93 for (i = 0; i < 3; i++) {
94 t = t << 8;
95 if (src >= src_end) {
96 snip--;
97 } else {
98 t += (duk_uint_t) (*src++);
99 }
100 }
101
102 /*
103 * Missing bytes snip base64 example
104 * 0 4 XXXX
105 * 1 3 XXX=
106 * 2 2 XX==
107 */
108
109 DUK_ASSERT(snip >= 2 && snip <= 4);
110
111 for (i = 0; i < 4; i++) {
112 x = (duk_uint_fast8_t) ((t >> 18) & 0x3f);
113 t = t << 6;
114
115 /* A straightforward 64-byte lookup would be faster
116 * and cleaner, but this is shorter.
117 */
118 if (i >= snip) {
119 y = '=';
120 } else if (x <= 25) {
121 y = x + 'A';
122 } else if (x <= 51) {
123 y = x - 26 + 'a';
124 } else if (x <= 61) {
125 y = x - 52 + '0';
126 } else if (x == 62) {
127 y = '+';
128 } else {
129 y = '/';
130 }
131
132 *dst++ = (duk_uint8_t) y;
133 }
134 }
135}
136#endif /* DUK_USE_BASE64_FASTPATH */
137
138#if defined(DUK_USE_BASE64_FASTPATH)
139DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst, duk_uint8_t **out_dst_final) {
140 duk_int_t x;
141 duk_int_t t;
142 duk_small_uint_t n_equal;
143 duk_small_uint_t n_chars;
144 const duk_uint8_t *src_end;
145 const duk_uint8_t *src_end_safe;
146
147 src_end = src + srclen;
148 src_end_safe = src_end - 4; /* if 'src < src_end_safe', safe to read 4 bytes */
149
150 /* Innermost fast path processes 4 valid base-64 characters at a time
151 * but bails out on whitespace, padding chars ('=') and invalid chars.
152 * Once the slow path segment has been processed, we return to the
153 * inner fast path again. This handles e.g. base64 with newlines
154 * reasonably well because the majority of a line is in the fast path.
155 */
156 for (;;) {
157 /* Fast path, handle units with just actual encoding characters. */
158
159 while (src <= src_end_safe) {
160 /* The lookup byte is intentionally sign extended to (at least)
161 * 32 bits and then ORed. This ensures that is at least 1 byte
162 * is negative, the highest bit of 't' will be set at the end
163 * and we don't need to check every byte.
164 */
165 DUK_DDD(DUK_DDDPRINT("fast loop: src=%p, src_end_safe=%p, src_end=%p",
166 (const void *) src, (const void *) src_end_safe, (const void *) src_end));
167
168 t = (duk_int_t) duk_base64_dectab[*src++];
169 t = (t << 6) | (duk_int_t) duk_base64_dectab[*src++];
170 t = (t << 6) | (duk_int_t) duk_base64_dectab[*src++];
171 t = (t << 6) | (duk_int_t) duk_base64_dectab[*src++];
172
173 if (DUK_UNLIKELY(t < 0)) {
174 DUK_DDD(DUK_DDDPRINT("fast loop unit was not clean, process one slow path unit"));
175 src -= 4;
176 break;
177 }
178
179 DUK_ASSERT(t <= 0xffffffL);
180 DUK_ASSERT((t >> 24) == 0);
181 *dst++ = (duk_uint8_t) (t >> 16);
182 *dst++ = (duk_uint8_t) ((t >> 8) & 0xff);
183 *dst++ = (duk_uint8_t) (t & 0xff);
184 }
185
186 /* Handle one slow path unit (or finish if we're done). */
187
188 n_equal = 0;
189 n_chars = 0;
190 t = 0;
191 for (;;) {
192 DUK_DDD(DUK_DDDPRINT("slow loop: src=%p, src_end=%p, n_chars=%ld, n_equal=%ld, t=%ld",
193 (const void *) src, (const void *) src_end, (long) n_chars, (long) n_equal, (long) t));
194
195 if (DUK_UNLIKELY(src >= src_end)) {
196 goto done; /* two level break */
197 }
198
199 x = duk_base64_dectab[*src++];
200 if (DUK_UNLIKELY(x < 0)) {
201 if (x == -2) {
202 continue; /* allowed ascii whitespace */
203 } else if (x == -3) {
204 n_equal++;
205 t <<= 6;
206 } else {
207 DUK_ASSERT(x == -1);
208 goto error;
209 }
210 } else {
211 DUK_ASSERT(x >= 0 && x <= 63);
212 if (n_equal > 0) {
213 /* Don't allow actual chars after equal sign. */
214 goto error;
215 }
216 t = (t << 6) + x;
217 }
218
219 if (DUK_UNLIKELY(n_chars == 3)) {
220 /* Emit 3 bytes and backtrack if there was padding. There's
221 * always space for the whole 3 bytes so no check needed.
222 */
223 DUK_ASSERT(t <= 0xffffffL);
224 DUK_ASSERT((t >> 24) == 0);
225 *dst++ = (duk_uint8_t) (t >> 16);
226 *dst++ = (duk_uint8_t) ((t >> 8) & 0xff);
227 *dst++ = (duk_uint8_t) (t & 0xff);
228
229 if (DUK_UNLIKELY(n_equal > 0)) {
230 DUK_ASSERT(n_equal <= 4);
231
232 /* There may be whitespace between the equal signs. */
233 if (n_equal == 1) {
234 /* XXX= */
235 dst -= 1;
236 } else if (n_equal == 2) {
237 /* XX== */
238 dst -= 2;
239 } else {
240 goto error; /* invalid padding */
241 }
242
243 /* Continue parsing after padding, allows concatenated,
244 * padded base64.
245 */
246 }
247 break; /* back to fast loop */
248 } else {
249 n_chars++;
250 }
251 }
252 }
253 done:
254 DUK_DDD(DUK_DDDPRINT("done; src=%p, src_end=%p, n_chars=%ld",
255 (const void *) src, (const void *) src_end, (long) n_chars));
256
257 DUK_ASSERT(src == src_end);
258
259 if (n_chars != 0) {
260 /* Here we'd have the option of decoding unpadded base64
261 * (e.g. "xxxxyy" instead of "xxxxyy==". Currently not
262 * accepted.
263 */
264 goto error;
265 }
266
267 *out_dst_final = dst;
268 return 1;
269
270 error:
271 return 0;
272}
273#else /* DUK_USE_BASE64_FASTPATH */
274DUK_LOCAL duk_bool_t duk__base64_decode_helper(const duk_uint8_t *src, duk_size_t srclen, duk_uint8_t *dst, duk_uint8_t **out_dst_final) {
275 duk_uint_t t;
276 duk_uint_fast8_t x, y;
277 duk_small_uint_t group_idx;
278 duk_small_uint_t n_equal;
279 const duk_uint8_t *src_end;
280
281 src_end = src + srclen;
282 t = 0;
283 group_idx = 0;
284 n_equal = 0;
285
286 while (src < src_end) {
287 x = *src++;
288
289 if (x >= 'A' && x <= 'Z') {
290 y = x - 'A' + 0;
291 } else if (x >= 'a' && x <= 'z') {
292 y = x - 'a' + 26;
293 } else if (x >= '0' && x <= '9') {
294 y = x - '0' + 52;
295 } else if (x == '+') {
296 y = 62;
297 } else if (x == '/') {
298 y = 63;
299 } else if (x == '=') {
300 /* We don't check the zero padding bytes here right now
301 * (that they're actually zero). This seems to be common
302 * behavior for base-64 decoders.
303 */
304
305 n_equal++;
306 t <<= 6; /* shift in zeroes */
307 goto skip_add;
308 } else if (x == 0x09 || x == 0x0a || x == 0x0d || x == 0x20) {
309 /* allow basic ASCII whitespace */
310 continue;
311 } else {
312 goto error;
313 }
314
315 if (n_equal > 0) {
316 /* Don't allow mixed padding and actual chars. */
317 goto error;
318 }
319 t = (t << 6) + y;
320 skip_add:
321
322 if (group_idx == 3) {
323 /* output 3 bytes from 't' */
324 *dst++ = (duk_uint8_t) ((t >> 16) & 0xff);
325 *dst++ = (duk_uint8_t) ((t >> 8) & 0xff);
326 *dst++ = (duk_uint8_t) (t & 0xff);
327
328 if (DUK_UNLIKELY(n_equal > 0)) {
329 /* Backtrack. */
330 DUK_ASSERT(n_equal <= 4);
331 if (n_equal == 1) {
332 dst -= 1;
333 } else if (n_equal == 2) {
334 dst -= 2;
335 } else {
336 goto error; /* invalid padding */
337 }
338
339 /* Here we can choose either to end parsing and ignore
340 * whatever follows, or to continue parsing in case
341 * multiple (possibly padded) base64 strings have been
342 * concatenated. Currently, keep on parsing.
343 */
344 n_equal = 0;
345 }
346
347 t = 0;
348 group_idx = 0;
349 } else {
350 group_idx++;
351 }
352 }
353
354 if (group_idx != 0) {
355 /* Here we'd have the option of decoding unpadded base64
356 * (e.g. "xxxxyy" instead of "xxxxyy==". Currently not
357 * accepted.
358 */
359 goto error;
360 }
361
362 *out_dst_final = dst;
363 return 1;
364
365 error:
366 return 0;
367}
368#endif /* DUK_USE_BASE64_FASTPATH */
369
370DUK_EXTERNAL const char *duk_base64_encode(duk_context *ctx, duk_idx_t index) {
371 duk_hthread *thr = (duk_hthread *) ctx;
372 const duk_uint8_t *src;
373 duk_size_t srclen;
374 duk_size_t dstlen;
375 duk_uint8_t *dst;
376 const char *ret;
377
378 DUK_ASSERT_CTX_VALID(ctx);
379
380 /* XXX: optimize for string inputs: no need to coerce to a buffer
381 * which makes a copy of the input.
382 */
383
384 index = duk_require_normalize_index(ctx, index);
385 src = duk__prep_codec_arg(ctx, index, &srclen);
386 /* Note: for srclen=0, src may be NULL */
387
388 /* Computation must not wrap; this limit works for 32-bit size_t:
389 * >>> srclen = 3221225469
390 * >>> '%x' % ((srclen + 2) / 3 * 4)
391 * 'fffffffc'
392 */
393 if (srclen > 3221225469UL) {
394 goto type_error;
395 }
396 dstlen = (srclen + 2) / 3 * 4;
397 dst = (duk_uint8_t *) duk_push_fixed_buffer(ctx, dstlen);
398
399 duk__base64_encode_helper((const duk_uint8_t *) src, srclen, dst);
400
401 ret = duk_to_string(ctx, -1);
402 duk_replace(ctx, index);
403 return ret;
404
405 type_error:
406 DUK_ERROR_TYPE(thr, DUK_STR_ENCODE_FAILED);
407 return NULL; /* never here */
408}
409
410DUK_EXTERNAL void duk_base64_decode(duk_context *ctx, duk_idx_t index) {
411 duk_hthread *thr = (duk_hthread *) ctx;
412 const duk_uint8_t *src;
413 duk_size_t srclen;
414 duk_size_t dstlen;
415 duk_uint8_t *dst;
416 duk_uint8_t *dst_final;
417 duk_bool_t retval;
418
419 DUK_ASSERT_CTX_VALID(ctx);
420
421 /* XXX: optimize for buffer inputs: no need to coerce to a string
422 * which causes an unnecessary interning.
423 */
424
425 index = duk_require_normalize_index(ctx, index);
426 src = duk__prep_codec_arg(ctx, index, &srclen);
427
428 /* Computation must not wrap, only srclen + 3 is at risk of
429 * wrapping because after that the number gets smaller.
430 * This limit works for 32-bit size_t:
431 * 0x100000000 - 3 - 1 = 4294967292
432 */
433 if (srclen > 4294967292UL) {
434 goto type_error;
435 }
436 dstlen = (srclen + 3) / 4 * 3; /* upper limit, assuming no whitespace etc */
437 dst = (duk_uint8_t *) duk_push_dynamic_buffer(ctx, dstlen);
438 /* Note: for dstlen=0, dst may be NULL */
439
440 retval = duk__base64_decode_helper((const duk_uint8_t *) src, srclen, dst, &dst_final);
441 if (!retval) {
442 goto type_error;
443 }
444
445 /* XXX: convert to fixed buffer? */
446 (void) duk_resize_buffer(ctx, -1, (duk_size_t) (dst_final - dst));
447 duk_replace(ctx, index);
448 return;
449
450 type_error:
451 DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED);
452}
453
454DUK_EXTERNAL const char *duk_hex_encode(duk_context *ctx, duk_idx_t index) {
455 const duk_uint8_t *inp;
456 duk_size_t len;
457 duk_size_t i;
458 duk_uint8_t *buf;
459 const char *ret;
460#if defined(DUK_USE_HEX_FASTPATH)
461 duk_size_t len_safe;
462 duk_uint16_t *p16;
463#endif
464
465 DUK_ASSERT_CTX_VALID(ctx);
466
467 index = duk_require_normalize_index(ctx, index);
468 inp = duk__prep_codec_arg(ctx, index, &len);
469 DUK_ASSERT(inp != NULL || len == 0);
470
471 /* Fixed buffer, no zeroing because we'll fill all the data. */
472 buf = (duk_uint8_t *) duk_push_buffer_raw(ctx, len * 2, DUK_BUF_FLAG_NOZERO /*flags*/);
473 DUK_ASSERT(buf != NULL);
474
475#if defined(DUK_USE_HEX_FASTPATH)
476 DUK_ASSERT((((duk_size_t) buf) & 0x01U) == 0); /* pointer is aligned, guaranteed for fixed buffer */
477 p16 = (duk_uint16_t *) (void *) buf;
478 len_safe = len & ~0x03U;
479 for (i = 0; i < len_safe; i += 4) {
480 p16[0] = duk_hex_enctab[inp[i]];
481 p16[1] = duk_hex_enctab[inp[i + 1]];
482 p16[2] = duk_hex_enctab[inp[i + 2]];
483 p16[3] = duk_hex_enctab[inp[i + 3]];
484 p16 += 4;
485 }
486 for (; i < len; i++) {
487 *p16++ = duk_hex_enctab[inp[i]];
488 }
489#else /* DUK_USE_HEX_FASTPATH */
490 for (i = 0; i < len; i++) {
491 duk_small_uint_t t;
492 t = (duk_small_uint_t) inp[i];
493 buf[i*2 + 0] = duk_lc_digits[t >> 4];
494 buf[i*2 + 1] = duk_lc_digits[t & 0x0f];
495 }
496#endif /* DUK_USE_HEX_FASTPATH */
497
498 /* XXX: Using a string return value forces a string intern which is
499 * not always necessary. As a rough performance measure, hex encode
500 * time for tests/perf/test-hex-encode.js dropped from ~35s to ~15s
501 * without string coercion. Change to returning a buffer and let the
502 * caller coerce to string if necessary?
503 */
504
505 ret = duk_to_string(ctx, -1);
506 duk_replace(ctx, index);
507 return ret;
508}
509
510DUK_EXTERNAL void duk_hex_decode(duk_context *ctx, duk_idx_t index) {
511 duk_hthread *thr = (duk_hthread *) ctx;
512 const duk_uint8_t *inp;
513 duk_size_t len;
514 duk_size_t i;
515 duk_int_t t;
516 duk_uint8_t *buf;
517#if defined(DUK_USE_HEX_FASTPATH)
518 duk_int_t chk;
519 duk_uint8_t *p;
520 duk_size_t len_safe;
521#endif
522
523 DUK_ASSERT_CTX_VALID(ctx);
524
525 index = duk_require_normalize_index(ctx, index);
526 inp = duk__prep_codec_arg(ctx, index, &len);
527 DUK_ASSERT(inp != NULL || len == 0);
528
529 if (len & 0x01) {
530 goto type_error;
531 }
532
533 /* Fixed buffer, no zeroing because we'll fill all the data. */
534 buf = (duk_uint8_t *) duk_push_buffer_raw(ctx, len / 2, DUK_BUF_FLAG_NOZERO /*flags*/);
535 DUK_ASSERT(buf != NULL);
536
537#if defined(DUK_USE_HEX_FASTPATH)
538 p = buf;
539 len_safe = len & ~0x07U;
540 for (i = 0; i < len_safe; i += 8) {
541 t = ((duk_int_t) duk_hex_dectab_shift4[inp[i]]) |
542 ((duk_int_t) duk_hex_dectab[inp[i + 1]]);
543 chk = t;
544 p[0] = (duk_uint8_t) t;
545 t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 2]]) |
546 ((duk_int_t) duk_hex_dectab[inp[i + 3]]);
547 chk |= t;
548 p[1] = (duk_uint8_t) t;
549 t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 4]]) |
550 ((duk_int_t) duk_hex_dectab[inp[i + 5]]);
551 chk |= t;
552 p[2] = (duk_uint8_t) t;
553 t = ((duk_int_t) duk_hex_dectab_shift4[inp[i + 6]]) |
554 ((duk_int_t) duk_hex_dectab[inp[i + 7]]);
555 chk |= t;
556 p[3] = (duk_uint8_t) t;
557 p += 4;
558
559 /* Check if any lookup above had a negative result. */
560 if (DUK_UNLIKELY(chk < 0)) {
561 goto type_error;
562 }
563 }
564 for (; i < len; i += 2) {
565 t = (((duk_int_t) duk_hex_dectab[inp[i]]) << 4) |
566 ((duk_int_t) duk_hex_dectab[inp[i + 1]]);
567 if (DUK_UNLIKELY(t < 0)) {
568 goto type_error;
569 }
570 *p++ = (duk_uint8_t) t;
571 }
572#else /* DUK_USE_HEX_FASTPATH */
573 for (i = 0; i < len; i += 2) {
574 /* For invalid characters the value -1 gets extended to
575 * at least 16 bits. If either nybble is invalid, the
576 * resulting 't' will be < 0.
577 */
578 t = (((duk_int_t) duk_hex_dectab[inp[i]]) << 4) |
579 ((duk_int_t) duk_hex_dectab[inp[i + 1]]);
580 if (DUK_UNLIKELY(t < 0)) {
581 goto type_error;
582 }
583 buf[i >> 1] = (duk_uint8_t) t;
584 }
585#endif /* DUK_USE_HEX_FASTPATH */
586
587 duk_replace(ctx, index);
588 return;
589
590 type_error:
591 DUK_ERROR_TYPE(thr, DUK_STR_DECODE_FAILED);
592}
593
594DUK_EXTERNAL const char *duk_json_encode(duk_context *ctx, duk_idx_t index) {
595#ifdef DUK_USE_ASSERTIONS
596 duk_idx_t top_at_entry;
597#endif
598 const char *ret;
599
600 DUK_ASSERT_CTX_VALID(ctx);
601#ifdef DUK_USE_ASSERTIONS
602 top_at_entry = duk_get_top(ctx);
603#endif
604
605 index = duk_require_normalize_index(ctx, index);
606 duk_bi_json_stringify_helper(ctx,
607 index /*idx_value*/,
608 DUK_INVALID_INDEX /*idx_replacer*/,
609 DUK_INVALID_INDEX /*idx_space*/,
610 0 /*flags*/);
611 DUK_ASSERT(duk_is_string(ctx, -1));
612 duk_replace(ctx, index);
613 ret = duk_get_string(ctx, index);
614
615 DUK_ASSERT(duk_get_top(ctx) == top_at_entry);
616
617 return ret;
618}
619
620DUK_EXTERNAL void duk_json_decode(duk_context *ctx, duk_idx_t index) {
621#ifdef DUK_USE_ASSERTIONS
622 duk_idx_t top_at_entry;
623#endif
624
625 DUK_ASSERT_CTX_VALID(ctx);
626#ifdef DUK_USE_ASSERTIONS
627 top_at_entry = duk_get_top(ctx);
628#endif
629
630 index = duk_require_normalize_index(ctx, index);
631 duk_bi_json_parse_helper(ctx,
632 index /*idx_value*/,
633 DUK_INVALID_INDEX /*idx_reviver*/,
634 0 /*flags*/);
635 duk_replace(ctx, index);
636
637 DUK_ASSERT(duk_get_top(ctx) == top_at_entry);
638}