2 * API calls related to general value stack manipulation: resizing the value
3 * stack, pushing and popping values, type checking and reading values,
4 * coercing values, etc.
6 * Also contains internal functions (such as duk_get_tval()), defined
7 * in duk_api_internal.h, with semantics similar to the public API.
10 /* XXX: repetition of stack pre-checks -> helper or macro or inline */
11 /* XXX: shared api error strings, and perhaps even throw code for rare cases? */
13 #include "duk_internal.h"
16 * Forward declarations
19 DUK_LOCAL_DECL duk_idx_t
duk__push_c_function_raw(duk_context
*ctx
, duk_c_function func
, duk_idx_t nargs
, duk_uint_t flags
);
22 * Global state for working around missing variadic macros
25 #ifndef DUK_USE_VARIADIC_MACROS
26 DUK_EXTERNAL
const char *duk_api_global_filename
= NULL
;
27 DUK_EXTERNAL duk_int_t duk_api_global_line
= 0;
34 /* Check that there's room to push one value. */
35 #if defined(DUK_USE_VALSTACK_UNSAFE)
36 /* Faster but value stack overruns are memory unsafe. */
37 #define DUK__CHECK_SPACE() do { \
38 DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
41 #define DUK__CHECK_SPACE() do { \
42 if (DUK_UNLIKELY(thr->valstack_top >= thr->valstack_end)) { \
43 DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); \
48 DUK_LOCAL_DECL duk_heaphdr
*duk__get_tagged_heaphdr_raw(duk_context
*ctx
, duk_idx_t index
, duk_uint_t tag
);
50 DUK_LOCAL duk_int_t
duk__api_coerce_d2i(duk_context
*ctx
, duk_idx_t index
, duk_bool_t require
) {
56 thr
= (duk_hthread
*) ctx
;
58 tv
= duk_get_tval(ctx
, index
);
64 * Special cases like NaN and +/- Infinity are handled explicitly
65 * because a plain C coercion from double to int handles these cases
66 * in undesirable ways. For instance, NaN may coerce to INT_MIN
67 * (not zero), and INT_MAX + 1 may coerce to INT_MIN (not INT_MAX).
69 * This double-to-int coercion differs from ToInteger() because it
70 * has a finite range (ToInteger() allows e.g. +/- Infinity). It
71 * also differs from ToInt32() because the INT_MIN/INT_MAX clamping
72 * depends on the size of the int type on the platform. In particular,
73 * on platforms with a 64-bit int type, the full range is allowed.
76 #if defined(DUK_USE_FASTINT)
77 if (DUK_TVAL_IS_FASTINT(tv
)) {
78 duk_int64_t t
= DUK_TVAL_GET_FASTINT(tv
);
79 #if (DUK_INT_MAX <= 0x7fffffffL)
80 /* Clamping only necessary for 32-bit ints. */
81 if (t
< DUK_INT_MIN
) {
83 } else if (t
> DUK_INT_MAX
) {
91 if (DUK_TVAL_IS_NUMBER(tv
)) {
92 d
= DUK_TVAL_GET_NUMBER(tv
);
93 c
= (duk_small_int_t
) DUK_FPCLASSIFY(d
);
94 if (c
== DUK_FP_NAN
) {
96 } else if (d
< (duk_double_t
) DUK_INT_MIN
) {
97 /* covers -Infinity */
99 } else if (d
> (duk_double_t
) DUK_INT_MAX
) {
100 /* covers +Infinity */
103 /* coerce towards zero */
104 return (duk_int_t
) d
;
111 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "number", DUK_STR_NOT_NUMBER
);
117 DUK_LOCAL duk_uint_t
duk__api_coerce_d2ui(duk_context
*ctx
, duk_idx_t index
, duk_bool_t require
) {
123 /* Same as above but for unsigned int range. */
125 thr
= (duk_hthread
*) ctx
;
127 tv
= duk_get_tval(ctx
, index
);
129 goto error_notnumber
;
132 #if defined(DUK_USE_FASTINT)
133 if (DUK_TVAL_IS_FASTINT(tv
)) {
134 duk_int64_t t
= DUK_TVAL_GET_FASTINT(tv
);
138 #if (DUK_UINT_MAX <= 0xffffffffUL)
139 /* Clamping only necessary for 32-bit ints. */
140 else if (t
> DUK_UINT_MAX
) {
144 return (duk_uint_t
) t
;
148 if (DUK_TVAL_IS_NUMBER(tv
)) {
149 d
= DUK_TVAL_GET_NUMBER(tv
);
150 c
= (duk_small_int_t
) DUK_FPCLASSIFY(d
);
151 if (c
== DUK_FP_NAN
) {
153 } else if (d
< 0.0) {
154 /* covers -Infinity */
155 return (duk_uint_t
) 0;
156 } else if (d
> (duk_double_t
) DUK_UINT_MAX
) {
157 /* covers +Infinity */
158 return (duk_uint_t
) DUK_UINT_MAX
;
160 /* coerce towards zero */
161 return (duk_uint_t
) d
;
168 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "number", DUK_STR_NOT_NUMBER
);
175 * Stack index validation/normalization and getting a stack duk_tval ptr.
177 * These are called by many API entrypoints so the implementations must be
178 * fast and "inlined".
180 * There's some repetition because of this; keep the functions in sync.
183 DUK_EXTERNAL duk_idx_t
duk_normalize_index(duk_context
*ctx
, duk_idx_t index
) {
184 duk_hthread
*thr
= (duk_hthread
*) ctx
;
188 DUK_ASSERT_CTX_VALID(ctx
);
189 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
191 /* Care must be taken to avoid pointer wrapping in the index
192 * validation. For instance, on a 32-bit platform with 8-byte
193 * duk_tval the index 0x20000000UL would wrap the memory space
197 /* Assume value stack sizes (in elements) fits into duk_idx_t. */
198 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
199 vs_size
= (duk_uidx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
200 DUK_ASSERT_DISABLE(vs_size
>= 0); /* unsigned */
203 uindex
= vs_size
+ (duk_uidx_t
) index
;
205 /* since index non-negative */
206 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
207 uindex
= (duk_uidx_t
) index
;
210 /* DUK_INVALID_INDEX won't be accepted as a valid index. */
211 DUK_ASSERT(vs_size
+ (duk_uidx_t
) DUK_INVALID_INDEX
>= vs_size
);
213 if (DUK_LIKELY(uindex
< vs_size
)) {
214 return (duk_idx_t
) uindex
;
216 return DUK_INVALID_INDEX
;
219 DUK_EXTERNAL duk_idx_t
duk_require_normalize_index(duk_context
*ctx
, duk_idx_t index
) {
220 duk_hthread
*thr
= (duk_hthread
*) ctx
;
224 DUK_ASSERT_CTX_VALID(ctx
);
225 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
227 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
228 vs_size
= (duk_uidx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
229 DUK_ASSERT_DISABLE(vs_size
>= 0); /* unsigned */
232 uindex
= vs_size
+ (duk_uidx_t
) index
;
234 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
235 uindex
= (duk_uidx_t
) index
;
238 /* DUK_INVALID_INDEX won't be accepted as a valid index. */
239 DUK_ASSERT(vs_size
+ (duk_uidx_t
) DUK_INVALID_INDEX
>= vs_size
);
241 if (DUK_LIKELY(uindex
< vs_size
)) {
242 return (duk_idx_t
) uindex
;
244 DUK_ERROR_API_INDEX(thr
, index
);
245 return 0; /* unreachable */
248 DUK_INTERNAL duk_tval
*duk_get_tval(duk_context
*ctx
, duk_idx_t index
) {
249 duk_hthread
*thr
= (duk_hthread
*) ctx
;
253 DUK_ASSERT_CTX_VALID(ctx
);
254 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
256 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
257 vs_size
= (duk_uidx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
258 DUK_ASSERT_DISABLE(vs_size
>= 0); /* unsigned */
261 uindex
= vs_size
+ (duk_uidx_t
) index
;
263 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
264 uindex
= (duk_uidx_t
) index
;
267 /* DUK_INVALID_INDEX won't be accepted as a valid index. */
268 DUK_ASSERT(vs_size
+ (duk_uidx_t
) DUK_INVALID_INDEX
>= vs_size
);
270 if (DUK_LIKELY(uindex
< vs_size
)) {
271 return thr
->valstack_bottom
+ uindex
;
276 DUK_INTERNAL duk_tval
*duk_require_tval(duk_context
*ctx
, duk_idx_t index
) {
277 duk_hthread
*thr
= (duk_hthread
*) ctx
;
281 DUK_ASSERT_CTX_VALID(ctx
);
282 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
284 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
285 vs_size
= (duk_uidx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
286 DUK_ASSERT_DISABLE(vs_size
>= 0); /* unsigned */
288 /* Use unsigned arithmetic to optimize comparison. */
290 uindex
= vs_size
+ (duk_uidx_t
) index
;
292 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
293 uindex
= (duk_uidx_t
) index
;
296 /* DUK_INVALID_INDEX won't be accepted as a valid index. */
297 DUK_ASSERT(vs_size
+ (duk_uidx_t
) DUK_INVALID_INDEX
>= vs_size
);
299 if (DUK_LIKELY(uindex
< vs_size
)) {
300 return thr
->valstack_bottom
+ uindex
;
302 DUK_ERROR_API_INDEX(thr
, index
);
307 DUK_EXTERNAL duk_bool_t
duk_is_valid_index(duk_context
*ctx
, duk_idx_t index
) {
308 DUK_ASSERT_CTX_VALID(ctx
);
309 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
311 return (duk_normalize_index(ctx
, index
) >= 0);
315 DUK_EXTERNAL
void duk_require_valid_index(duk_context
*ctx
, duk_idx_t index
) {
316 duk_hthread
*thr
= (duk_hthread
*) ctx
;
318 DUK_ASSERT_CTX_VALID(ctx
);
319 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
321 if (duk_normalize_index(ctx
, index
) < 0) {
322 DUK_ERROR_API_INDEX(thr
, index
);
323 return; /* unreachable */
328 * Value stack top handling
331 DUK_EXTERNAL duk_idx_t
duk_get_top(duk_context
*ctx
) {
332 duk_hthread
*thr
= (duk_hthread
*) ctx
;
334 DUK_ASSERT_CTX_VALID(ctx
);
336 return (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
339 /* Set stack top within currently allocated range, but don't reallocate.
340 * This is performance critical especially for call handling, so whenever
341 * changing, profile and look at generated code.
343 DUK_EXTERNAL
void duk_set_top(duk_context
*ctx
, duk_idx_t index
) {
344 duk_hthread
*thr
= (duk_hthread
*) ctx
;
350 DUK_ASSERT_CTX_VALID(ctx
);
351 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
353 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
354 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_bottom
);
355 vs_size
= (duk_uidx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
356 vs_limit
= (duk_uidx_t
) (thr
->valstack_end
- thr
->valstack_bottom
);
359 /* Negative indices are always within allocated stack but
360 * must not go below zero index.
362 uindex
= vs_size
+ (duk_uidx_t
) index
;
364 /* Positive index can be higher than valstack top but must
365 * not go above allocated stack (equality is OK).
367 uindex
= (duk_uidx_t
) index
;
370 /* DUK_INVALID_INDEX won't be accepted as a valid index. */
371 DUK_ASSERT(vs_size
+ (duk_uidx_t
) DUK_INVALID_INDEX
>= vs_size
);
372 DUK_ASSERT(vs_size
+ (duk_uidx_t
) DUK_INVALID_INDEX
>= vs_limit
);
374 #if defined(DUK_USE_VALSTACK_UNSAFE)
375 DUK_ASSERT(uindex
<= vs_limit
);
378 if (DUK_UNLIKELY(uindex
> vs_limit
)) {
379 DUK_ERROR_API_INDEX(thr
, index
);
380 return; /* unreachable */
383 DUK_ASSERT(uindex
<= vs_limit
);
385 /* Handle change in value stack top. Respect value stack
386 * initialization policy: 'undefined' above top. Note that
387 * DECREF may cause a side effect that reallocates valstack,
388 * so must relookup after DECREF.
391 if (uindex
>= vs_size
) {
392 /* Stack size increases or stays the same. */
393 #if defined(DUK_USE_ASSERTIONS)
396 count
= uindex
- vs_size
;
399 tv
= thr
->valstack_top
+ count
;
400 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv
));
403 thr
->valstack_top
= thr
->valstack_bottom
+ uindex
;
405 /* Stack size decreases. */
406 #if defined(DUK_USE_REFERENCE_COUNTING)
409 count
= vs_size
- uindex
;
410 DUK_ASSERT(count
> 0);
413 tv
= --thr
->valstack_top
; /* tv -> value just before prev top value; must relookup */
414 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
415 DUK_TVAL_SET_UNDEFINED_UPDREF(thr
, tv
); /* side effects */
417 #else /* DUK_USE_REFERENCE_COUNTING */
421 count
= vs_size
- uindex
;
422 tv
= thr
->valstack_top
;
424 DUK_ASSERT(tv
> tv_end
);
427 DUK_TVAL_SET_UNDEFINED(tv
);
428 } while (tv
!= tv_end
);
429 thr
->valstack_top
= tv_end
;
430 #endif /* DUK_USE_REFERENCE_COUNTING */
434 DUK_EXTERNAL duk_idx_t
duk_get_top_index(duk_context
*ctx
) {
435 duk_hthread
*thr
= (duk_hthread
*) ctx
;
438 DUK_ASSERT_CTX_VALID(ctx
);
440 ret
= ((duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
)) - 1;
441 if (DUK_UNLIKELY(ret
< 0)) {
442 /* Return invalid index; if caller uses this without checking
443 * in another API call, the index won't map to a valid stack
446 return DUK_INVALID_INDEX
;
451 DUK_EXTERNAL duk_idx_t
duk_require_top_index(duk_context
*ctx
) {
452 duk_hthread
*thr
= (duk_hthread
*) ctx
;
455 DUK_ASSERT_CTX_VALID(ctx
);
457 ret
= ((duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
)) - 1;
458 if (DUK_UNLIKELY(ret
< 0)) {
459 DUK_ERROR_API_INDEX(thr
, -1);
460 return 0; /* unreachable */
466 * Value stack resizing.
468 * This resizing happens above the current "top": the value stack can be
469 * grown or shrunk, but the "top" is not affected. The value stack cannot
470 * be resized to a size below the current "top".
472 * The low level reallocation primitive must carefully recompute all value
473 * stack pointers, and must also work if ALL pointers are NULL. The resize
474 * is quite tricky because the valstack realloc may cause a mark-and-sweep,
475 * which may run finalizers. Running finalizers may resize the valstack
476 * recursively (the same value stack we're working on). So, after realloc
477 * returns, we know that the valstack "top" should still be the same (there
478 * should not be live values above the "top"), but its underlying size and
479 * pointer may have changed.
482 /* XXX: perhaps refactor this to allow caller to specify some parameters, or
483 * at least a 'compact' flag which skips any spare or round-up .. useful for
487 DUK_LOCAL duk_bool_t
duk__resize_valstack(duk_context
*ctx
, duk_size_t new_size
) {
488 duk_hthread
*thr
= (duk_hthread
*) ctx
;
489 duk_ptrdiff_t old_bottom_offset
;
490 duk_ptrdiff_t old_top_offset
;
491 duk_ptrdiff_t old_end_offset_post
;
493 duk_ptrdiff_t old_end_offset_pre
;
494 duk_tval
*old_valstack_pre
;
495 duk_tval
*old_valstack_post
;
497 duk_tval
*new_valstack
;
498 duk_size_t new_alloc_size
;
501 DUK_ASSERT_CTX_VALID(ctx
);
502 DUK_ASSERT(thr
!= NULL
);
503 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
504 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
505 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
506 DUK_ASSERT((duk_size_t
) (thr
->valstack_top
- thr
->valstack
) <= new_size
); /* can't resize below 'top' */
507 DUK_ASSERT(new_size
<= thr
->valstack_max
); /* valstack limit caller has check, prevents wrapping */
508 DUK_ASSERT(new_size
<= DUK_SIZE_MAX
/ sizeof(duk_tval
)); /* specific assert for wrapping */
510 /* get pointer offsets for tweaking below */
511 old_bottom_offset
= (((duk_uint8_t
*) thr
->valstack_bottom
) - ((duk_uint8_t
*) thr
->valstack
));
512 old_top_offset
= (((duk_uint8_t
*) thr
->valstack_top
) - ((duk_uint8_t
*) thr
->valstack
));
514 old_end_offset_pre
= (((duk_uint8_t
*) thr
->valstack_end
) - ((duk_uint8_t
*) thr
->valstack
)); /* not very useful, used for debugging */
515 old_valstack_pre
= thr
->valstack
;
518 /* Allocate a new valstack.
520 * Note: cannot use a plain DUK_REALLOC() because a mark-and-sweep may
521 * invalidate the original thr->valstack base pointer inside the realloc
522 * process. See doc/memory-management.rst.
525 new_alloc_size
= sizeof(duk_tval
) * new_size
;
526 new_valstack
= (duk_tval
*) DUK_REALLOC_INDIRECT(thr
->heap
, duk_hthread_get_valstack_ptr
, (void *) thr
, new_alloc_size
);
528 /* Because new_size != 0, if condition doesn't need to be
529 * (new_valstack != NULL || new_size == 0).
531 DUK_ASSERT(new_size
!= 0);
532 DUK_D(DUK_DPRINT("failed to resize valstack to %lu entries (%lu bytes)",
533 (unsigned long) new_size
, (unsigned long) new_alloc_size
));
537 /* Note: the realloc may have triggered a mark-and-sweep which may
538 * have resized our valstack internally. However, the mark-and-sweep
539 * MUST NOT leave the stack bottom/top in a different state. Particular
540 * assumptions and facts:
542 * - The thr->valstack pointer may be different after realloc,
543 * and the offset between thr->valstack_end <-> thr->valstack
545 * - The offset between thr->valstack_bottom <-> thr->valstack
546 * and thr->valstack_top <-> thr->valstack MUST NOT have changed,
547 * because mark-and-sweep must adhere to a strict stack policy.
548 * In other words, logical bottom and top MUST NOT have changed.
549 * - All values above the top are unreachable but are initialized
550 * to UNDEFINED, up to the post-realloc valstack_end.
551 * - 'old_end_offset' must be computed after realloc to be correct.
554 DUK_ASSERT((((duk_uint8_t
*) thr
->valstack_bottom
) - ((duk_uint8_t
*) thr
->valstack
)) == old_bottom_offset
);
555 DUK_ASSERT((((duk_uint8_t
*) thr
->valstack_top
) - ((duk_uint8_t
*) thr
->valstack
)) == old_top_offset
);
557 /* success, fixup pointers */
558 old_end_offset_post
= (((duk_uint8_t
*) thr
->valstack_end
) - ((duk_uint8_t
*) thr
->valstack
)); /* must be computed after realloc */
560 old_valstack_post
= thr
->valstack
;
562 thr
->valstack
= new_valstack
;
563 thr
->valstack_end
= new_valstack
+ new_size
;
564 #if !defined(DUK_USE_PREFER_SIZE)
565 thr
->valstack_size
= new_size
;
567 thr
->valstack_bottom
= (duk_tval
*) (void *) ((duk_uint8_t
*) new_valstack
+ old_bottom_offset
);
568 thr
->valstack_top
= (duk_tval
*) (void *) ((duk_uint8_t
*) new_valstack
+ old_top_offset
);
570 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
571 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
572 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
574 /* useful for debugging */
576 if (old_end_offset_pre
!= old_end_offset_post
) {
577 DUK_D(DUK_DPRINT("valstack was resized during valstack_resize(), probably by mark-and-sweep; "
578 "end offset changed: %lu -> %lu",
579 (unsigned long) old_end_offset_pre
,
580 (unsigned long) old_end_offset_post
));
582 if (old_valstack_pre
!= old_valstack_post
) {
583 DUK_D(DUK_DPRINT("valstack pointer changed during valstack_resize(), probably by mark-and-sweep: %p -> %p",
584 (void *) old_valstack_pre
,
585 (void *) old_valstack_post
));
589 DUK_DD(DUK_DDPRINT("resized valstack to %lu elements (%lu bytes), bottom=%ld, top=%ld, "
590 "new pointers: start=%p end=%p bottom=%p top=%p",
591 (unsigned long) new_size
, (unsigned long) new_alloc_size
,
592 (long) (thr
->valstack_bottom
- thr
->valstack
),
593 (long) (thr
->valstack_top
- thr
->valstack
),
594 (void *) thr
->valstack
, (void *) thr
->valstack_end
,
595 (void *) thr
->valstack_bottom
, (void *) thr
->valstack_top
));
597 /* Init newly allocated slots (only). */
598 p
= (duk_tval
*) (void *) ((duk_uint8_t
*) thr
->valstack
+ old_end_offset_post
);
599 while (p
< thr
->valstack_end
) {
600 /* Never executed if new size is smaller. */
601 DUK_TVAL_SET_UNDEFINED(p
);
605 /* Assert for value stack initialization policy. */
606 #if defined(DUK_USE_ASSERTIONS)
607 p
= thr
->valstack_top
;
608 while (p
< thr
->valstack_end
) {
609 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(p
));
618 duk_bool_t
duk_valstack_resize_raw(duk_context
*ctx
,
619 duk_size_t min_new_size
,
620 duk_small_uint_t flags
) {
621 duk_hthread
*thr
= (duk_hthread
*) ctx
;
624 duk_bool_t is_shrink
= 0;
625 duk_small_uint_t shrink_flag
= (flags
& DUK_VSRESIZE_FLAG_SHRINK
);
626 duk_small_uint_t compact_flag
= (flags
& DUK_VSRESIZE_FLAG_COMPACT
);
627 duk_small_uint_t throw_flag
= (flags
& DUK_VSRESIZE_FLAG_THROW
);
629 DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, "
630 "curr_bottom=%ld, shrink=%d, compact=%d, throw=%d",
631 (unsigned long) min_new_size
,
632 (long) (thr
->valstack_end
- thr
->valstack
),
633 (long) (thr
->valstack_top
- thr
->valstack
),
634 (long) (thr
->valstack_bottom
- thr
->valstack
),
635 (int) shrink_flag
, (int) compact_flag
, (int) throw_flag
));
637 DUK_ASSERT_CTX_VALID(ctx
);
638 DUK_ASSERT(thr
!= NULL
);
639 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
640 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
641 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
643 #if defined(DUK_USE_PREFER_SIZE)
644 old_size
= (duk_size_t
) (thr
->valstack_end
- thr
->valstack
);
646 DUK_ASSERT((duk_size_t
) (thr
->valstack_end
- thr
->valstack
) == thr
->valstack_size
);
647 old_size
= thr
->valstack_size
;
650 if (min_new_size
<= old_size
) {
653 old_size
- min_new_size
< DUK_VALSTACK_SHRINK_THRESHOLD
) {
654 DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack"));
659 new_size
= min_new_size
;
662 /* shrink case; leave some spare */
663 new_size
+= DUK_VALSTACK_SHRINK_SPARE
;
666 /* round up roughly to next 'grow step' */
667 new_size
= (new_size
/ DUK_VALSTACK_GROW_STEP
+ 1) * DUK_VALSTACK_GROW_STEP
;
670 DUK_DD(DUK_DDPRINT("want to %s valstack: %lu -> %lu elements (min_new_size %lu)",
671 (const char *) (new_size
> old_size
? "grow" : "shrink"),
672 (unsigned long) old_size
, (unsigned long) new_size
,
673 (unsigned long) min_new_size
));
675 if (new_size
> thr
->valstack_max
) {
676 /* Note: may be triggered even if minimal new_size would not reach the limit,
677 * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account).
680 DUK_ERROR_RANGE(thr
, DUK_STR_VALSTACK_LIMIT
);
687 * When resizing the valstack, a mark-and-sweep may be triggered for
688 * the allocation of the new valstack. If the mark-and-sweep needs
689 * to use our thread for something, it may cause *the same valstack*
690 * to be resized recursively. This happens e.g. when mark-and-sweep
691 * finalizers are called. This is taken into account carefully in
692 * duk__resize_valstack().
694 * 'new_size' is known to be <= valstack_max, which ensures that
695 * size_t and pointer arithmetic won't wrap in duk__resize_valstack().
698 if (!duk__resize_valstack(ctx
, new_size
)) {
700 DUK_DD(DUK_DDPRINT("valstack resize failed, but is a shrink, ignore"));
704 DUK_DD(DUK_DDPRINT("valstack resize failed"));
707 DUK_ERROR_ALLOC_DEFMSG(thr
);
713 DUK_DDD(DUK_DDDPRINT("valstack resize successful"));
717 DUK_EXTERNAL duk_bool_t
duk_check_stack(duk_context
*ctx
, duk_idx_t extra
) {
718 duk_hthread
*thr
= (duk_hthread
*) ctx
;
719 duk_size_t min_new_size
;
721 DUK_ASSERT_CTX_VALID(ctx
);
722 DUK_ASSERT(thr
!= NULL
);
724 if (DUK_UNLIKELY(extra
< 0)) {
725 /* Clamping to zero makes the API more robust to calling code
726 * calculation errors.
731 min_new_size
= (thr
->valstack_top
- thr
->valstack
) + extra
+ DUK_VALSTACK_INTERNAL_EXTRA
;
732 return duk_valstack_resize_raw(ctx
,
733 min_new_size
, /* min_new_size */
734 0 /* no shrink */ | /* flags */
739 DUK_EXTERNAL
void duk_require_stack(duk_context
*ctx
, duk_idx_t extra
) {
740 duk_hthread
*thr
= (duk_hthread
*) ctx
;
741 duk_size_t min_new_size
;
743 DUK_ASSERT_CTX_VALID(ctx
);
744 DUK_ASSERT(thr
!= NULL
);
746 if (DUK_UNLIKELY(extra
< 0)) {
747 /* Clamping to zero makes the API more robust to calling code
748 * calculation errors.
753 min_new_size
= (thr
->valstack_top
- thr
->valstack
) + extra
+ DUK_VALSTACK_INTERNAL_EXTRA
;
754 (void) duk_valstack_resize_raw(ctx
,
755 min_new_size
, /* min_new_size */
756 0 /* no shrink */ | /* flags */
758 DUK_VSRESIZE_FLAG_THROW
);
761 DUK_EXTERNAL duk_bool_t
duk_check_stack_top(duk_context
*ctx
, duk_idx_t top
) {
763 duk_size_t min_new_size
;
765 DUK_ASSERT_CTX_VALID(ctx
);
766 thr
= (duk_hthread
*) ctx
;
768 if (DUK_UNLIKELY(top
< 0)) {
769 /* Clamping to zero makes the API more robust to calling code
770 * calculation errors.
775 min_new_size
= (thr
->valstack_bottom
- thr
->valstack
) + top
+ DUK_VALSTACK_INTERNAL_EXTRA
;
776 return duk_valstack_resize_raw(ctx
,
777 min_new_size
, /* min_new_size */
778 0 /* no shrink */ | /* flags */
783 DUK_EXTERNAL
void duk_require_stack_top(duk_context
*ctx
, duk_idx_t top
) {
785 duk_size_t min_new_size
;
787 DUK_ASSERT_CTX_VALID(ctx
);
788 thr
= (duk_hthread
*) ctx
;
790 if (DUK_UNLIKELY(top
< 0)) {
791 /* Clamping to zero makes the API more robust to calling code
792 * calculation errors.
797 min_new_size
= (thr
->valstack_bottom
- thr
->valstack
) + top
+ DUK_VALSTACK_INTERNAL_EXTRA
;
798 (void) duk_valstack_resize_raw(ctx
,
799 min_new_size
, /* min_new_size */
800 0 /* no shrink */ | /* flags */
802 DUK_VSRESIZE_FLAG_THROW
);
806 * Basic stack manipulation: swap, dup, insert, replace, etc
809 DUK_EXTERNAL
void duk_swap(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
814 DUK_ASSERT_CTX_VALID(ctx
);
816 tv1
= duk_require_tval(ctx
, index1
);
817 DUK_ASSERT(tv1
!= NULL
);
818 tv2
= duk_require_tval(ctx
, index2
);
819 DUK_ASSERT(tv2
!= NULL
);
821 /* If tv1==tv2 this is a NOP, no check is needed */
822 DUK_TVAL_SET_TVAL(&tv_tmp
, tv1
);
823 DUK_TVAL_SET_TVAL(tv1
, tv2
);
824 DUK_TVAL_SET_TVAL(tv2
, &tv_tmp
);
827 DUK_EXTERNAL
void duk_swap_top(duk_context
*ctx
, duk_idx_t index
) {
828 DUK_ASSERT_CTX_VALID(ctx
);
830 duk_swap(ctx
, index
, -1);
833 DUK_EXTERNAL
void duk_dup(duk_context
*ctx
, duk_idx_t from_index
) {
838 DUK_ASSERT_CTX_VALID(ctx
);
839 thr
= (duk_hthread
*) ctx
;
842 tv_from
= duk_require_tval(ctx
, from_index
);
843 tv_to
= thr
->valstack_top
++;
844 DUK_ASSERT(tv_from
!= NULL
);
845 DUK_ASSERT(tv_to
!= NULL
);
846 DUK_TVAL_SET_TVAL(tv_to
, tv_from
);
847 DUK_TVAL_INCREF(thr
, tv_to
); /* no side effects */
850 DUK_EXTERNAL
void duk_dup_top(duk_context
*ctx
) {
855 DUK_ASSERT_CTX_VALID(ctx
);
856 thr
= (duk_hthread
*) ctx
;
859 if (thr
->valstack_top
- thr
->valstack_bottom
<= 0) {
860 DUK_ERROR_API_INDEX(thr
, -1);
861 return; /* unreachable */
863 tv_from
= thr
->valstack_top
- 1;
864 tv_to
= thr
->valstack_top
++;
865 DUK_ASSERT(tv_from
!= NULL
);
866 DUK_ASSERT(tv_to
!= NULL
);
867 DUK_TVAL_SET_TVAL(tv_to
, tv_from
);
868 DUK_TVAL_INCREF(thr
, tv_to
); /* no side effects */
871 DUK_EXTERNAL
void duk_insert(duk_context
*ctx
, duk_idx_t to_index
) {
877 DUK_ASSERT_CTX_VALID(ctx
);
879 p
= duk_require_tval(ctx
, to_index
);
880 DUK_ASSERT(p
!= NULL
);
881 q
= duk_require_tval(ctx
, -1);
882 DUK_ASSERT(q
!= NULL
);
888 * [ ... | p | x | x | q ]
889 * => [ ... | q | p | x | x ]
892 nbytes
= (duk_size_t
) (((duk_uint8_t
*) q
) - ((duk_uint8_t
*) p
)); /* Note: 'q' is top-1 */
894 DUK_DDD(DUK_DDDPRINT("duk_insert: to_index=%ld, p=%p, q=%p, nbytes=%lu",
895 (long) to_index
, (void *) p
, (void *) q
, (unsigned long) nbytes
));
897 /* No net refcount changes. */
900 DUK_TVAL_SET_TVAL(&tv_tmp
, q
);
901 DUK_ASSERT(nbytes
> 0);
902 DUK_MEMMOVE((void *) (p
+ 1), (const void *) p
, (size_t) nbytes
);
903 DUK_TVAL_SET_TVAL(p
, &tv_tmp
);
905 /* nop: insert top to top */
906 DUK_ASSERT(nbytes
== 0);
911 DUK_EXTERNAL
void duk_replace(duk_context
*ctx
, duk_idx_t to_index
) {
912 duk_hthread
*thr
= (duk_hthread
*) ctx
;
917 DUK_ASSERT_CTX_VALID(ctx
);
919 tv1
= duk_require_tval(ctx
, -1);
920 DUK_ASSERT(tv1
!= NULL
);
921 tv2
= duk_require_tval(ctx
, to_index
);
922 DUK_ASSERT(tv2
!= NULL
);
924 /* For tv1 == tv2, both pointing to stack top, the end result
925 * is same as duk_pop(ctx).
927 DUK_TVAL_SET_TVAL(&tv_tmp
, tv2
);
928 DUK_TVAL_SET_TVAL(tv2
, tv1
);
929 DUK_TVAL_SET_UNDEFINED(tv1
);
931 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
934 DUK_EXTERNAL
void duk_copy(duk_context
*ctx
, duk_idx_t from_index
, duk_idx_t to_index
) {
935 duk_hthread
*thr
= (duk_hthread
*) ctx
;
939 DUK_ASSERT_CTX_VALID(ctx
);
940 DUK_UNREF(thr
); /* w/o refcounting */
942 tv1
= duk_require_tval(ctx
, from_index
);
943 DUK_ASSERT(tv1
!= NULL
);
944 tv2
= duk_require_tval(ctx
, to_index
);
945 DUK_ASSERT(tv2
!= NULL
);
947 /* For tv1 == tv2, this is a no-op (no explicit check needed). */
948 DUK_TVAL_SET_TVAL_UPDREF(thr
, tv2
, tv1
); /* side effects */
951 DUK_EXTERNAL
void duk_remove(duk_context
*ctx
, duk_idx_t index
) {
952 duk_hthread
*thr
= (duk_hthread
*) ctx
;
955 #ifdef DUK_USE_REFERENCE_COUNTING
960 DUK_ASSERT_CTX_VALID(ctx
);
962 p
= duk_require_tval(ctx
, index
);
963 DUK_ASSERT(p
!= NULL
);
964 q
= duk_require_tval(ctx
, -1);
965 DUK_ASSERT(q
!= NULL
);
969 /* nbytes zero size case
971 * [ ... | p | x | x | q ] [ ... | p==q ]
972 * => [ ... | x | x | q ] [ ... ]
975 #ifdef DUK_USE_REFERENCE_COUNTING
976 /* use a temp: decref only when valstack reachable values are correct */
977 DUK_TVAL_SET_TVAL(&tv_tmp
, p
);
980 nbytes
= (duk_size_t
) (((duk_uint8_t
*) q
) - ((duk_uint8_t
*) p
)); /* Note: 'q' is top-1 */
981 DUK_MEMMOVE((void *) p
, (const void *) (p
+ 1), (size_t) nbytes
); /* zero size not an issue: pointers are valid */
983 DUK_TVAL_SET_UNDEFINED(q
);
986 #ifdef DUK_USE_REFERENCE_COUNTING
987 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
992 * Stack slice primitives
995 DUK_EXTERNAL
void duk_xcopymove_raw(duk_context
*to_ctx
, duk_context
*from_ctx
, duk_idx_t count
, duk_bool_t is_copy
) {
996 duk_hthread
*to_thr
= (duk_hthread
*) to_ctx
;
997 duk_hthread
*from_thr
= (duk_hthread
*) from_ctx
;
1003 /* XXX: several pointer comparison issues here */
1005 DUK_ASSERT_CTX_VALID(to_ctx
);
1006 DUK_ASSERT_CTX_VALID(from_ctx
);
1007 DUK_ASSERT(to_ctx
!= NULL
);
1008 DUK_ASSERT(from_ctx
!= NULL
);
1010 if (to_ctx
== from_ctx
) {
1011 DUK_ERROR_API(to_thr
, DUK_STR_INVALID_CONTEXT
);
1015 (count
> (duk_idx_t
) to_thr
->valstack_max
)) {
1016 /* Maximum value check ensures 'nbytes' won't wrap below. */
1017 DUK_ERROR_API(to_thr
, DUK_STR_INVALID_COUNT
);
1021 nbytes
= sizeof(duk_tval
) * count
;
1025 DUK_ASSERT(to_thr
->valstack_top
<= to_thr
->valstack_end
);
1026 if ((duk_size_t
) ((duk_uint8_t
*) to_thr
->valstack_end
- (duk_uint8_t
*) to_thr
->valstack_top
) < nbytes
) {
1027 DUK_ERROR_API(to_thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
1029 src
= (void *) ((duk_uint8_t
*) from_thr
->valstack_top
- nbytes
);
1030 if (src
< (void *) from_thr
->valstack_bottom
) {
1031 DUK_ERROR_API(to_thr
, DUK_STR_INVALID_COUNT
);
1034 /* copy values (no overlap even if to_ctx == from_ctx; that's not
1035 * allowed now anyway)
1037 DUK_ASSERT(nbytes
> 0);
1038 DUK_MEMCPY((void *) to_thr
->valstack_top
, (const void *) src
, (size_t) nbytes
);
1040 p
= to_thr
->valstack_top
;
1041 to_thr
->valstack_top
= (duk_tval
*) (void *) (((duk_uint8_t
*) p
) + nbytes
);
1044 /* Incref copies, keep originals. */
1045 q
= to_thr
->valstack_top
;
1047 DUK_TVAL_INCREF(to_thr
, p
); /* no side effects */
1051 /* No net refcount change. */
1052 p
= from_thr
->valstack_top
;
1053 q
= (duk_tval
*) (void *) (((duk_uint8_t
*) p
) - nbytes
);
1054 from_thr
->valstack_top
= q
;
1058 DUK_TVAL_SET_UNDEFINED(p
);
1059 /* XXX: fast primitive to set a bunch of values to UNDEFINED */
1068 DUK_EXTERNAL
void duk_require_undefined(duk_context
*ctx
, duk_idx_t index
) {
1069 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1072 DUK_ASSERT_CTX_VALID(ctx
);
1074 tv
= duk_get_tval(ctx
, index
);
1075 if (tv
&& DUK_TVAL_IS_UNDEFINED(tv
)) {
1078 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "undefined", DUK_STR_NOT_UNDEFINED
);
1079 return; /* not reachable */
1082 DUK_EXTERNAL
void duk_require_null(duk_context
*ctx
, duk_idx_t index
) {
1083 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1086 DUK_ASSERT_CTX_VALID(ctx
);
1088 tv
= duk_get_tval(ctx
, index
);
1089 if (tv
&& DUK_TVAL_IS_NULL(tv
)) {
1092 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "null", DUK_STR_NOT_NULL
);
1093 return; /* not reachable */
1096 DUK_EXTERNAL duk_bool_t
duk_get_boolean(duk_context
*ctx
, duk_idx_t index
) {
1097 duk_bool_t ret
= 0; /* default: false */
1100 DUK_ASSERT_CTX_VALID(ctx
);
1102 tv
= duk_get_tval(ctx
, index
);
1103 if (tv
&& DUK_TVAL_IS_BOOLEAN(tv
)) {
1104 ret
= DUK_TVAL_GET_BOOLEAN(tv
);
1107 DUK_ASSERT(ret
== 0 || ret
== 1);
1111 DUK_EXTERNAL duk_bool_t
duk_require_boolean(duk_context
*ctx
, duk_idx_t index
) {
1112 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1115 DUK_ASSERT_CTX_VALID(ctx
);
1117 tv
= duk_get_tval(ctx
, index
);
1118 if (tv
&& DUK_TVAL_IS_BOOLEAN(tv
)) {
1119 duk_bool_t ret
= DUK_TVAL_GET_BOOLEAN(tv
);
1120 DUK_ASSERT(ret
== 0 || ret
== 1);
1123 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "boolean", DUK_STR_NOT_BOOLEAN
);
1124 return 0; /* not reachable */
1127 DUK_EXTERNAL duk_double_t
duk_get_number(duk_context
*ctx
, duk_idx_t index
) {
1128 duk_double_union ret
;
1131 DUK_ASSERT_CTX_VALID(ctx
);
1133 ret
.d
= DUK_DOUBLE_NAN
; /* default: NaN */
1134 tv
= duk_get_tval(ctx
, index
);
1135 if (tv
&& DUK_TVAL_IS_NUMBER(tv
)) {
1136 ret
.d
= DUK_TVAL_GET_NUMBER(tv
);
1140 * Number should already be in NaN-normalized form, but let's
1144 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret
);
1148 DUK_EXTERNAL duk_double_t
duk_require_number(duk_context
*ctx
, duk_idx_t index
) {
1149 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1152 DUK_ASSERT_CTX_VALID(ctx
);
1154 tv
= duk_get_tval(ctx
, index
);
1155 if (tv
&& DUK_TVAL_IS_NUMBER(tv
)) {
1156 duk_double_union ret
;
1157 ret
.d
= DUK_TVAL_GET_NUMBER(tv
);
1160 * Number should already be in NaN-normalized form,
1161 * but let's normalize anyway.
1164 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret
);
1167 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "number", DUK_STR_NOT_NUMBER
);
1168 return DUK_DOUBLE_NAN
; /* not reachable */
1171 DUK_EXTERNAL duk_int_t
duk_get_int(duk_context
*ctx
, duk_idx_t index
) {
1172 /* Custom coercion for API */
1173 DUK_ASSERT_CTX_VALID(ctx
);
1174 return (duk_int_t
) duk__api_coerce_d2i(ctx
, index
, 0 /*require*/);
1177 DUK_EXTERNAL duk_uint_t
duk_get_uint(duk_context
*ctx
, duk_idx_t index
) {
1178 /* Custom coercion for API */
1179 DUK_ASSERT_CTX_VALID(ctx
);
1180 return (duk_uint_t
) duk__api_coerce_d2ui(ctx
, index
, 0 /*require*/);
1183 DUK_EXTERNAL duk_int_t
duk_require_int(duk_context
*ctx
, duk_idx_t index
) {
1184 /* Custom coercion for API */
1185 DUK_ASSERT_CTX_VALID(ctx
);
1186 return (duk_int_t
) duk__api_coerce_d2i(ctx
, index
, 1 /*require*/);
1189 DUK_EXTERNAL duk_uint_t
duk_require_uint(duk_context
*ctx
, duk_idx_t index
) {
1190 /* Custom coercion for API */
1191 DUK_ASSERT_CTX_VALID(ctx
);
1192 return (duk_uint_t
) duk__api_coerce_d2ui(ctx
, index
, 1 /*require*/);
1195 DUK_EXTERNAL
const char *duk_get_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
1199 DUK_ASSERT_CTX_VALID(ctx
);
1201 /* default: NULL, length 0 */
1207 tv
= duk_get_tval(ctx
, index
);
1208 if (tv
&& DUK_TVAL_IS_STRING(tv
)) {
1209 /* Here we rely on duk_hstring instances always being zero
1210 * terminated even if the actual string is not.
1212 duk_hstring
*h
= DUK_TVAL_GET_STRING(tv
);
1213 DUK_ASSERT(h
!= NULL
);
1214 ret
= (const char *) DUK_HSTRING_GET_DATA(h
);
1216 *out_len
= DUK_HSTRING_GET_BYTELEN(h
);
1223 DUK_EXTERNAL
const char *duk_require_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
1224 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1227 DUK_ASSERT_CTX_VALID(ctx
);
1229 /* Note: this check relies on the fact that even a zero-size string
1230 * has a non-NULL pointer.
1232 ret
= duk_get_lstring(ctx
, index
, out_len
);
1236 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "string", DUK_STR_NOT_STRING
);
1237 return NULL
; /* not reachable */
1240 DUK_EXTERNAL
const char *duk_get_string(duk_context
*ctx
, duk_idx_t index
) {
1241 DUK_ASSERT_CTX_VALID(ctx
);
1243 return duk_get_lstring(ctx
, index
, NULL
);
1246 DUK_EXTERNAL
const char *duk_require_string(duk_context
*ctx
, duk_idx_t index
) {
1247 DUK_ASSERT_CTX_VALID(ctx
);
1249 return duk_require_lstring(ctx
, index
, NULL
);
1252 DUK_EXTERNAL
void *duk_get_pointer(duk_context
*ctx
, duk_idx_t index
) {
1255 DUK_ASSERT_CTX_VALID(ctx
);
1257 tv
= duk_get_tval(ctx
, index
);
1258 if (tv
&& DUK_TVAL_IS_POINTER(tv
)) {
1259 void *p
= DUK_TVAL_GET_POINTER(tv
); /* may be NULL */
1266 DUK_EXTERNAL
void *duk_require_pointer(duk_context
*ctx
, duk_idx_t index
) {
1267 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1270 DUK_ASSERT_CTX_VALID(ctx
);
1272 /* Note: here we must be wary of the fact that a pointer may be
1273 * valid and be a NULL.
1275 tv
= duk_get_tval(ctx
, index
);
1276 if (tv
&& DUK_TVAL_IS_POINTER(tv
)) {
1277 void *p
= DUK_TVAL_GET_POINTER(tv
); /* may be NULL */
1280 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "pointer", DUK_STR_NOT_POINTER
);
1281 return NULL
; /* not reachable */
1285 DUK_INTERNAL
void *duk_get_voidptr(duk_context
*ctx
, duk_idx_t index
) {
1288 DUK_ASSERT_CTX_VALID(ctx
);
1290 tv
= duk_get_tval(ctx
, index
);
1291 if (tv
&& DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
1292 duk_heaphdr
*h
= DUK_TVAL_GET_HEAPHDR(tv
);
1293 DUK_ASSERT(h
!= NULL
);
1301 DUK_LOCAL
void *duk__get_buffer_helper(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
, duk_bool_t throw_flag
) {
1302 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1305 DUK_ASSERT_CTX_VALID(ctx
);
1308 if (out_size
!= NULL
) {
1312 tv
= duk_get_tval(ctx
, index
);
1313 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
1314 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
1315 DUK_ASSERT(h
!= NULL
);
1317 *out_size
= DUK_HBUFFER_GET_SIZE(h
);
1319 return (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
); /* may be NULL (but only if size is 0) */
1323 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "buffer", DUK_STR_NOT_BUFFER
);
1328 DUK_EXTERNAL
void *duk_get_buffer(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1329 return duk__get_buffer_helper(ctx
, index
, out_size
, 0 /*throw_flag*/);
1332 DUK_EXTERNAL
void *duk_require_buffer(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1333 return duk__get_buffer_helper(ctx
, index
, out_size
, 1 /*throw_flag*/);
1336 DUK_LOCAL
void *duk__get_buffer_data_helper(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
, duk_bool_t throw_flag
) {
1337 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1340 DUK_ASSERT_CTX_VALID(ctx
);
1343 if (out_size
!= NULL
) {
1347 tv
= duk_get_tval(ctx
, index
);
1352 if (DUK_TVAL_IS_BUFFER(tv
)) {
1353 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
1354 DUK_ASSERT(h
!= NULL
);
1356 *out_size
= DUK_HBUFFER_GET_SIZE(h
);
1358 return (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
); /* may be NULL (but only if size is 0) */
1359 } else if (DUK_TVAL_IS_OBJECT(tv
)) {
1360 duk_hobject
*h
= DUK_TVAL_GET_OBJECT(tv
);
1361 DUK_ASSERT(h
!= NULL
);
1362 if (DUK_HOBJECT_IS_BUFFEROBJECT(h
)) {
1363 /* XXX: this is probably a useful shared helper: for a
1364 * duk_hbufferobject, get a validated buffer pointer/length.
1366 duk_hbufferobject
*h_bufobj
= (duk_hbufferobject
*) h
;
1367 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
1369 if (h_bufobj
->buf
!= NULL
&&
1370 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj
)) {
1373 p
= (duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_bufobj
->buf
);
1374 if (out_size
!= NULL
) {
1375 *out_size
= (duk_size_t
) h_bufobj
->length
;
1377 return (void *) (p
+ h_bufobj
->offset
);
1379 /* if slice not fully valid, treat as error */
1385 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "buffer", DUK_STR_NOT_BUFFER
);
1390 DUK_EXTERNAL
void *duk_get_buffer_data(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1391 return duk__get_buffer_data_helper(ctx
, index
, out_size
, 0 /*throw_flag*/);
1394 DUK_EXTERNAL
void *duk_require_buffer_data(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1395 return duk__get_buffer_data_helper(ctx
, index
, out_size
, 1 /*throw_flag*/);
1398 /* Raw helper for getting a value from the stack, checking its tag.
1399 * The tag cannot be a number because numbers don't have an internal
1400 * tag in the packed representation.
1403 DUK_LOCAL duk_heaphdr
*duk__get_tagged_heaphdr_raw(duk_context
*ctx
, duk_idx_t index
, duk_uint_t tag
) {
1406 DUK_ASSERT_CTX_VALID(ctx
);
1408 tv
= duk_get_tval(ctx
, index
);
1409 if (tv
&& (DUK_TVAL_GET_TAG(tv
) == tag
)) {
1411 ret
= DUK_TVAL_GET_HEAPHDR(tv
);
1412 DUK_ASSERT(ret
!= NULL
); /* tagged null pointers should never occur */
1416 return (duk_heaphdr
*) NULL
;
1419 DUK_INTERNAL duk_hstring
*duk_get_hstring(duk_context
*ctx
, duk_idx_t index
) {
1420 return (duk_hstring
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_STRING
);
1423 DUK_INTERNAL duk_hstring
*duk_require_hstring(duk_context
*ctx
, duk_idx_t index
) {
1425 h
= duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_STRING
);
1427 DUK_ERROR_REQUIRE_TYPE_INDEX(ctx
, index
, "string", DUK_STR_NOT_STRING
);
1429 return (duk_hstring
*) h
;
1432 DUK_INTERNAL duk_hobject
*duk_get_hobject(duk_context
*ctx
, duk_idx_t index
) {
1433 return (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1436 DUK_INTERNAL duk_hobject
*duk_require_hobject(duk_context
*ctx
, duk_idx_t index
) {
1438 h
= duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1440 DUK_ERROR_REQUIRE_TYPE_INDEX(ctx
, index
, "object", DUK_STR_NOT_OBJECT
);
1442 return (duk_hobject
*) h
;
1445 DUK_INTERNAL duk_hbuffer
*duk_get_hbuffer(duk_context
*ctx
, duk_idx_t index
) {
1446 return (duk_hbuffer
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_BUFFER
);
1449 DUK_INTERNAL duk_hbuffer
*duk_require_hbuffer(duk_context
*ctx
, duk_idx_t index
) {
1451 h
= duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_BUFFER
);
1453 DUK_ERROR_REQUIRE_TYPE_INDEX(ctx
, index
, "buffer", DUK_STR_NOT_BUFFER
);
1455 return (duk_hbuffer
*) h
;
1458 DUK_INTERNAL duk_hthread
*duk_get_hthread(duk_context
*ctx
, duk_idx_t index
) {
1459 duk_hobject
*h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1460 if (h
!= NULL
&& !DUK_HOBJECT_IS_THREAD(h
)) {
1463 return (duk_hthread
*) h
;
1466 DUK_INTERNAL duk_hthread
*duk_require_hthread(duk_context
*ctx
, duk_idx_t index
) {
1467 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1468 duk_hobject
*h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1469 if (!(h
!= NULL
&& DUK_HOBJECT_IS_THREAD(h
))) {
1470 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "thread", DUK_STR_NOT_THREAD
);
1472 return (duk_hthread
*) h
;
1475 DUK_INTERNAL duk_hcompiledfunction
*duk_get_hcompiledfunction(duk_context
*ctx
, duk_idx_t index
) {
1476 duk_hobject
*h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1477 if (h
!= NULL
&& !DUK_HOBJECT_IS_COMPILEDFUNCTION(h
)) {
1480 return (duk_hcompiledfunction
*) h
;
1483 DUK_INTERNAL duk_hcompiledfunction
*duk_require_hcompiledfunction(duk_context
*ctx
, duk_idx_t index
) {
1484 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1485 duk_hobject
*h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1486 if (!(h
!= NULL
&& DUK_HOBJECT_IS_COMPILEDFUNCTION(h
))) {
1487 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "compiledfunction", DUK_STR_NOT_COMPILEDFUNCTION
);
1489 return (duk_hcompiledfunction
*) h
;
1492 DUK_INTERNAL duk_hnativefunction
*duk_get_hnativefunction(duk_context
*ctx
, duk_idx_t index
) {
1493 duk_hobject
*h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1494 if (h
!= NULL
&& !DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
1497 return (duk_hnativefunction
*) h
;
1500 DUK_INTERNAL duk_hnativefunction
*duk_require_hnativefunction(duk_context
*ctx
, duk_idx_t index
) {
1501 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1502 duk_hobject
*h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1503 if (!(h
!= NULL
&& DUK_HOBJECT_IS_NATIVEFUNCTION(h
))) {
1504 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "nativefunction", DUK_STR_NOT_NATIVEFUNCTION
);
1506 return (duk_hnativefunction
*) h
;
1509 DUK_EXTERNAL duk_c_function
duk_get_c_function(duk_context
*ctx
, duk_idx_t index
) {
1512 duk_hnativefunction
*f
;
1514 DUK_ASSERT_CTX_VALID(ctx
);
1516 tv
= duk_get_tval(ctx
, index
);
1520 if (!DUK_TVAL_IS_OBJECT(tv
)) {
1523 h
= DUK_TVAL_GET_OBJECT(tv
);
1524 DUK_ASSERT(h
!= NULL
);
1526 if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
1529 DUK_ASSERT(DUK_HOBJECT_HAS_NATIVEFUNCTION(h
));
1530 f
= (duk_hnativefunction
*) h
;
1535 DUK_EXTERNAL duk_c_function
duk_require_c_function(duk_context
*ctx
, duk_idx_t index
) {
1536 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1539 DUK_ASSERT_CTX_VALID(ctx
);
1541 ret
= duk_get_c_function(ctx
, index
);
1543 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "nativefunction", DUK_STR_NOT_NATIVEFUNCTION
);
1548 DUK_EXTERNAL
void duk_require_function(duk_context
*ctx
, duk_idx_t index
) {
1549 if (!duk_is_function(ctx
, index
)) {
1550 DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread
*) ctx
, index
, "function", DUK_STR_NOT_FUNCTION
);
1554 DUK_EXTERNAL duk_context
*duk_get_context(duk_context
*ctx
, duk_idx_t index
) {
1555 DUK_ASSERT_CTX_VALID(ctx
);
1557 return (duk_context
*) duk_get_hthread(ctx
, index
);
1560 DUK_EXTERNAL duk_context
*duk_require_context(duk_context
*ctx
, duk_idx_t index
) {
1561 DUK_ASSERT_CTX_VALID(ctx
);
1563 return (duk_context
*) duk_require_hthread(ctx
, index
);
1566 DUK_EXTERNAL
void *duk_get_heapptr(duk_context
*ctx
, duk_idx_t index
) {
1570 DUK_ASSERT_CTX_VALID(ctx
);
1572 tv
= duk_get_tval(ctx
, index
);
1573 if (tv
&& DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
1574 ret
= (void *) DUK_TVAL_GET_HEAPHDR(tv
);
1575 DUK_ASSERT(ret
!= NULL
);
1579 return (void *) NULL
;
1582 DUK_EXTERNAL
void *duk_require_heapptr(duk_context
*ctx
, duk_idx_t index
) {
1583 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1587 DUK_ASSERT_CTX_VALID(ctx
);
1589 tv
= duk_require_tval(ctx
, index
);
1590 DUK_ASSERT(tv
!= NULL
);
1591 if (DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
1592 ret
= (void *) DUK_TVAL_GET_HEAPHDR(tv
);
1593 DUK_ASSERT(ret
!= NULL
);
1597 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "heapobject", DUK_STR_UNEXPECTED_TYPE
);
1598 return (void *) NULL
; /* not reachable */
1602 /* This would be pointless: we'd return NULL for both lightfuncs and
1605 DUK_INTERNAL duk_hobject
*duk_get_hobject_or_lfunc(duk_context
*ctx
, duk_idx_t index
) {
1609 /* Useful for internal call sites where we either expect an object (function)
1610 * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
1611 * to an object). Return value is NULL if value is neither an object nor a
1614 DUK_INTERNAL duk_hobject
*duk_get_hobject_or_lfunc_coerce(duk_context
*ctx
, duk_idx_t index
) {
1617 DUK_ASSERT_CTX_VALID(ctx
);
1619 tv
= duk_require_tval(ctx
, index
);
1620 DUK_ASSERT(tv
!= NULL
);
1621 if (DUK_TVAL_IS_OBJECT(tv
)) {
1622 return DUK_TVAL_GET_OBJECT(tv
);
1623 } else if (DUK_TVAL_IS_LIGHTFUNC(tv
)) {
1624 duk_to_object(ctx
, index
);
1625 return duk_require_hobject(ctx
, index
);
1631 /* Useful for internal call sites where we either expect an object (function)
1632 * or a lightfunc. Returns NULL for a lightfunc.
1634 DUK_INTERNAL duk_hobject
*duk_require_hobject_or_lfunc(duk_context
*ctx
, duk_idx_t index
) {
1635 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1638 DUK_ASSERT_CTX_VALID(ctx
);
1640 tv
= duk_require_tval(ctx
, index
);
1641 DUK_ASSERT(tv
!= NULL
);
1642 if (DUK_TVAL_IS_OBJECT(tv
)) {
1643 return DUK_TVAL_GET_OBJECT(tv
);
1644 } else if (DUK_TVAL_IS_LIGHTFUNC(tv
)) {
1647 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "object", DUK_STR_NOT_OBJECT
);
1648 return NULL
; /* not reachable */
1651 /* Useful for internal call sites where we either expect an object (function)
1652 * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
1653 * to an object). Return value is never NULL.
1655 DUK_INTERNAL duk_hobject
*duk_require_hobject_or_lfunc_coerce(duk_context
*ctx
, duk_idx_t index
) {
1656 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1659 DUK_ASSERT_CTX_VALID(ctx
);
1661 tv
= duk_require_tval(ctx
, index
);
1662 if (DUK_TVAL_IS_OBJECT(tv
)) {
1663 return DUK_TVAL_GET_OBJECT(tv
);
1664 } else if (DUK_TVAL_IS_LIGHTFUNC(tv
)) {
1665 duk_to_object(ctx
, index
);
1666 return duk_require_hobject(ctx
, index
);
1668 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, "object", DUK_STR_NOT_OBJECT
);
1669 return NULL
; /* not reachable */
1672 DUK_INTERNAL duk_hobject
*duk_get_hobject_with_class(duk_context
*ctx
, duk_idx_t index
, duk_small_uint_t classnum
) {
1675 DUK_ASSERT_CTX_VALID(ctx
);
1676 DUK_ASSERT_DISABLE(classnum
>= 0); /* unsigned */
1677 DUK_ASSERT(classnum
<= DUK_HOBJECT_CLASS_MAX
);
1679 h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1680 if (h
!= NULL
&& DUK_HOBJECT_GET_CLASS_NUMBER(h
) != classnum
) {
1686 DUK_INTERNAL duk_hobject
*duk_require_hobject_with_class(duk_context
*ctx
, duk_idx_t index
, duk_small_uint_t classnum
) {
1690 DUK_ASSERT_CTX_VALID(ctx
);
1691 DUK_ASSERT_DISABLE(classnum
>= 0); /* unsigned */
1692 DUK_ASSERT(classnum
<= DUK_HOBJECT_CLASS_MAX
);
1693 thr
= (duk_hthread
*) ctx
;
1695 h
= (duk_hobject
*) duk__get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1696 if (!(h
!= NULL
&& DUK_HOBJECT_GET_CLASS_NUMBER(h
) == classnum
)) {
1697 duk_hstring
*h_class
;
1698 h_class
= DUK_HTHREAD_GET_STRING(thr
, DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum
));
1701 DUK_ERROR_REQUIRE_TYPE_INDEX(thr
, index
, (const char *) DUK_HSTRING_GET_DATA(h_class
), DUK_STR_UNEXPECTED_TYPE
);
1706 DUK_EXTERNAL duk_size_t
duk_get_length(duk_context
*ctx
, duk_idx_t index
) {
1709 DUK_ASSERT_CTX_VALID(ctx
);
1711 tv
= duk_get_tval(ctx
, index
);
1716 switch (DUK_TVAL_GET_TAG(tv
)) {
1717 case DUK_TAG_UNDEFINED
:
1719 case DUK_TAG_BOOLEAN
:
1720 case DUK_TAG_POINTER
:
1722 case DUK_TAG_STRING
: {
1723 duk_hstring
*h
= DUK_TVAL_GET_STRING(tv
);
1724 DUK_ASSERT(h
!= NULL
);
1725 return (duk_size_t
) DUK_HSTRING_GET_CHARLEN(h
);
1727 case DUK_TAG_OBJECT
: {
1728 duk_hobject
*h
= DUK_TVAL_GET_OBJECT(tv
);
1729 DUK_ASSERT(h
!= NULL
);
1730 return (duk_size_t
) duk_hobject_get_length((duk_hthread
*) ctx
, h
);
1732 case DUK_TAG_BUFFER
: {
1733 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
1734 DUK_ASSERT(h
!= NULL
);
1735 return (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
);
1737 case DUK_TAG_LIGHTFUNC
: {
1738 duk_small_uint_t lf_flags
;
1739 lf_flags
= DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv
);
1740 return (duk_size_t
) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags
);
1742 #if defined(DUK_USE_FASTINT)
1743 case DUK_TAG_FASTINT
:
1747 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
1748 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
1755 DUK_INTERNAL
void duk_set_length(duk_context
*ctx
, duk_idx_t index
, duk_size_t length
) {
1756 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1759 DUK_ASSERT_CTX_VALID(ctx
);
1761 h
= duk_get_hobject(ctx
, index
);
1766 duk_hobject_set_length(thr
, h
, (duk_uint32_t
) length
); /* XXX: typing */
1770 * Conversions and coercions
1772 * The conversion/coercions are in-place operations on the value stack.
1773 * Some operations are implemented here directly, while others call a
1774 * helper in duk_js_ops.c after validating arguments.
1777 /* E5 Section 8.12.8 */
1779 DUK_LOCAL duk_bool_t
duk__defaultvalue_coerce_attempt(duk_context
*ctx
, duk_idx_t index
, duk_small_int_t func_stridx
) {
1780 if (duk_get_prop_stridx(ctx
, index
, func_stridx
)) {
1782 if (duk_is_callable(ctx
, -1)) {
1783 duk_dup(ctx
, index
); /* -> [ ... func this ] */
1784 duk_call_method(ctx
, 0); /* -> [ ... retval ] */
1785 if (duk_is_primitive(ctx
, -1)) {
1786 duk_replace(ctx
, index
);
1789 /* [ ... retval ]; popped below */
1792 duk_pop(ctx
); /* [ ... func/retval ] -> [ ... ] */
1796 DUK_EXTERNAL
void duk_to_defaultvalue(duk_context
*ctx
, duk_idx_t index
, duk_int_t hint
) {
1797 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1799 /* inline initializer for coercers[] is not allowed by old compilers like BCC */
1800 duk_small_int_t coercers
[2];
1802 DUK_ASSERT_CTX_VALID(ctx
);
1803 DUK_ASSERT(thr
!= NULL
);
1805 coercers
[0] = DUK_STRIDX_VALUE_OF
;
1806 coercers
[1] = DUK_STRIDX_TO_STRING
;
1808 index
= duk_require_normalize_index(ctx
, index
);
1809 obj
= duk_require_hobject_or_lfunc(ctx
, index
);
1811 if (hint
== DUK_HINT_NONE
) {
1812 if (obj
!= NULL
&& DUK_HOBJECT_GET_CLASS_NUMBER(obj
) == DUK_HOBJECT_CLASS_DATE
) {
1813 hint
= DUK_HINT_STRING
;
1815 hint
= DUK_HINT_NUMBER
;
1819 if (hint
== DUK_HINT_STRING
) {
1820 coercers
[0] = DUK_STRIDX_TO_STRING
;
1821 coercers
[1] = DUK_STRIDX_VALUE_OF
;
1824 if (duk__defaultvalue_coerce_attempt(ctx
, index
, coercers
[0])) {
1828 if (duk__defaultvalue_coerce_attempt(ctx
, index
, coercers
[1])) {
1832 DUK_ERROR_TYPE(thr
, DUK_STR_DEFAULTVALUE_COERCE_FAILED
);
1835 DUK_EXTERNAL
void duk_to_undefined(duk_context
*ctx
, duk_idx_t index
) {
1836 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1839 DUK_ASSERT_CTX_VALID(ctx
);
1842 tv
= duk_require_tval(ctx
, index
);
1843 DUK_ASSERT(tv
!= NULL
);
1844 DUK_TVAL_SET_UNDEFINED_UPDREF(thr
, tv
); /* side effects */
1847 DUK_EXTERNAL
void duk_to_null(duk_context
*ctx
, duk_idx_t index
) {
1848 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1851 DUK_ASSERT_CTX_VALID(ctx
);
1854 tv
= duk_require_tval(ctx
, index
);
1855 DUK_ASSERT(tv
!= NULL
);
1856 DUK_TVAL_SET_NULL_UPDREF(thr
, tv
); /* side effects */
1859 /* E5 Section 9.1 */
1860 DUK_EXTERNAL
void duk_to_primitive(duk_context
*ctx
, duk_idx_t index
, duk_int_t hint
) {
1861 DUK_ASSERT_CTX_VALID(ctx
);
1862 DUK_ASSERT(hint
== DUK_HINT_NONE
|| hint
== DUK_HINT_NUMBER
|| hint
== DUK_HINT_STRING
);
1864 index
= duk_require_normalize_index(ctx
, index
);
1866 if (!duk_check_type_mask(ctx
, index
, DUK_TYPE_MASK_OBJECT
|
1867 DUK_TYPE_MASK_LIGHTFUNC
)) {
1868 /* everything except object stay as is */
1871 duk_to_defaultvalue(ctx
, index
, hint
);
1874 /* E5 Section 9.2 */
1875 DUK_EXTERNAL duk_bool_t
duk_to_boolean(duk_context
*ctx
, duk_idx_t index
) {
1876 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1880 DUK_ASSERT_CTX_VALID(ctx
);
1883 index
= duk_require_normalize_index(ctx
, index
);
1885 tv
= duk_require_tval(ctx
, index
);
1886 DUK_ASSERT(tv
!= NULL
);
1888 val
= duk_js_toboolean(tv
);
1889 DUK_ASSERT(val
== 0 || val
== 1);
1891 /* Note: no need to re-lookup tv, conversion is side effect free */
1892 DUK_ASSERT(tv
!= NULL
);
1893 DUK_TVAL_SET_BOOLEAN_UPDREF(thr
, tv
, val
); /* side effects */
1897 DUK_EXTERNAL duk_double_t
duk_to_number(duk_context
*ctx
, duk_idx_t index
) {
1898 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1902 DUK_ASSERT_CTX_VALID(ctx
);
1904 tv
= duk_require_tval(ctx
, index
);
1905 DUK_ASSERT(tv
!= NULL
);
1907 d
= duk_js_tonumber(thr
, tv
);
1909 /* Note: need to re-lookup because ToNumber() may have side effects */
1910 tv
= duk_require_tval(ctx
, index
);
1911 DUK_TVAL_SET_NUMBER_UPDREF(thr
, tv
, d
); /* side effects */
1915 /* XXX: combine all the integer conversions: they share everything
1916 * but the helper function for coercion.
1919 typedef duk_double_t (*duk__toint_coercer
)(duk_hthread
*thr
, duk_tval
*tv
);
1921 DUK_LOCAL duk_double_t
duk__to_int_uint_helper(duk_context
*ctx
, duk_idx_t index
, duk__toint_coercer coerce_func
) {
1922 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1926 DUK_ASSERT_CTX_VALID(ctx
);
1928 tv
= duk_require_tval(ctx
, index
);
1929 DUK_ASSERT(tv
!= NULL
);
1930 d
= coerce_func(thr
, tv
);
1934 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1935 tv
= duk_require_tval(ctx
, index
);
1936 DUK_TVAL_SET_NUMBER_UPDREF(thr
, tv
, d
); /* side effects */
1940 DUK_EXTERNAL duk_int_t
duk_to_int(duk_context
*ctx
, duk_idx_t index
) {
1941 /* Value coercion (in stack): ToInteger(), E5 Section 9.4
1942 * API return value coercion: custom
1944 DUK_ASSERT_CTX_VALID(ctx
);
1945 (void) duk__to_int_uint_helper(ctx
, index
, duk_js_tointeger
);
1946 return (duk_int_t
) duk__api_coerce_d2i(ctx
, index
, 0 /*require*/);
1949 DUK_EXTERNAL duk_uint_t
duk_to_uint(duk_context
*ctx
, duk_idx_t index
) {
1950 /* Value coercion (in stack): ToInteger(), E5 Section 9.4
1951 * API return value coercion: custom
1953 DUK_ASSERT_CTX_VALID(ctx
);
1954 (void) duk__to_int_uint_helper(ctx
, index
, duk_js_tointeger
);
1955 return (duk_uint_t
) duk__api_coerce_d2ui(ctx
, index
, 0 /*require*/);
1958 DUK_EXTERNAL duk_int32_t
duk_to_int32(duk_context
*ctx
, duk_idx_t index
) {
1959 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1963 DUK_ASSERT_CTX_VALID(ctx
);
1965 tv
= duk_require_tval(ctx
, index
);
1966 DUK_ASSERT(tv
!= NULL
);
1967 ret
= duk_js_toint32(thr
, tv
);
1969 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1970 tv
= duk_require_tval(ctx
, index
);
1971 DUK_TVAL_SET_FASTINT_I32_UPDREF(thr
, tv
, ret
); /* side effects */
1975 DUK_EXTERNAL duk_uint32_t
duk_to_uint32(duk_context
*ctx
, duk_idx_t index
) {
1976 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1980 DUK_ASSERT_CTX_VALID(ctx
);
1982 tv
= duk_require_tval(ctx
, index
);
1983 DUK_ASSERT(tv
!= NULL
);
1984 ret
= duk_js_touint32(thr
, tv
);
1986 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1987 tv
= duk_require_tval(ctx
, index
);
1988 DUK_TVAL_SET_FASTINT_U32_UPDREF(thr
, tv
, ret
); /* side effects */
1992 DUK_EXTERNAL duk_uint16_t
duk_to_uint16(duk_context
*ctx
, duk_idx_t index
) {
1993 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1997 DUK_ASSERT_CTX_VALID(ctx
);
1999 tv
= duk_require_tval(ctx
, index
);
2000 DUK_ASSERT(tv
!= NULL
);
2001 ret
= duk_js_touint16(thr
, tv
);
2003 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
2004 tv
= duk_require_tval(ctx
, index
);
2005 DUK_TVAL_SET_FASTINT_U32_UPDREF(thr
, tv
, ret
); /* side effects */
2009 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2010 /* Special coercion for Uint8ClampedArray. */
2011 DUK_INTERNAL duk_uint8_t
duk_to_uint8clamped(duk_context
*ctx
, duk_idx_t index
) {
2016 /* XXX: Simplify this algorithm, should be possible to come up with
2017 * a shorter and faster algorithm by inspecting IEEE representation
2021 d
= duk_to_number(ctx
, index
);
2024 } else if (d
>= 255) {
2026 } else if (DUK_ISNAN(d
)) {
2027 /* Avoid NaN-to-integer coercion as it is compiler specific. */
2031 t
= d
- DUK_FLOOR(d
);
2033 /* Exact halfway, round to even. */
2034 ret
= (duk_uint8_t
) d
;
2035 ret
= (ret
+ 1) & 0xfe; /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4
2036 * Example: d=4.5, t=0.5 -> ret = (4 + 1) & 0xfe = 5 & 0xfe = 4
2039 /* Not halfway, round to nearest. */
2040 ret
= (duk_uint8_t
) (d
+ 0.5);
2044 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2046 DUK_EXTERNAL
const char *duk_to_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
2047 DUK_ASSERT_CTX_VALID(ctx
);
2049 (void) duk_to_string(ctx
, index
);
2050 return duk_require_lstring(ctx
, index
, out_len
);
2053 DUK_LOCAL duk_ret_t
duk__safe_to_string_raw(duk_context
*ctx
) {
2054 DUK_ASSERT_CTX_VALID(ctx
);
2056 duk_to_string(ctx
, -1);
2060 DUK_EXTERNAL
const char *duk_safe_to_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
2061 DUK_ASSERT_CTX_VALID(ctx
);
2063 index
= duk_require_normalize_index(ctx
, index
);
2065 /* We intentionally ignore the duk_safe_call() return value and only
2066 * check the output type. This way we don't also need to check that
2067 * the returned value is indeed a string in the success case.
2070 duk_dup(ctx
, index
);
2071 (void) duk_safe_call(ctx
, duk__safe_to_string_raw
, 1 /*nargs*/, 1 /*nrets*/);
2072 if (!duk_is_string(ctx
, -1)) {
2073 /* Error: try coercing error to string once. */
2074 (void) duk_safe_call(ctx
, duk__safe_to_string_raw
, 1 /*nargs*/, 1 /*nrets*/);
2075 if (!duk_is_string(ctx
, -1)) {
2078 duk_push_hstring_stridx(ctx
, DUK_STRIDX_UC_ERROR
);
2085 DUK_ASSERT(duk_is_string(ctx
, -1));
2086 DUK_ASSERT(duk_get_string(ctx
, -1) != NULL
);
2088 duk_replace(ctx
, index
);
2089 return duk_get_lstring(ctx
, index
, out_len
);
2092 #if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */
2093 DUK_INTERNAL duk_hstring
*duk_safe_to_hstring(duk_context
*ctx
, duk_idx_t index
) {
2094 (void) duk_safe_to_string(ctx
, index
);
2095 DUK_ASSERT(duk_is_string(ctx
, index
));
2096 DUK_ASSERT(duk_get_hstring(ctx
, index
) != NULL
);
2097 return duk_get_hstring(ctx
, index
);
2101 /* Coerce top into Object.prototype.toString() output. */
2102 DUK_INTERNAL
void duk_to_object_class_string_top(duk_context
*ctx
) {
2104 duk_uint_t typemask
;
2105 duk_hstring
*h_strclass
;
2107 DUK_ASSERT_CTX_VALID(ctx
);
2108 thr
= (duk_hthread
*) ctx
;
2111 typemask
= duk_get_type_mask(ctx
, -1);
2112 if (typemask
& DUK_TYPE_MASK_UNDEFINED
) {
2113 h_strclass
= DUK_HTHREAD_STRING_UC_UNDEFINED(thr
);
2114 } else if (typemask
& DUK_TYPE_MASK_NULL
) {
2115 h_strclass
= DUK_HTHREAD_STRING_UC_NULL(thr
);
2119 duk_to_object(ctx
, -1);
2120 h_obj
= duk_get_hobject(ctx
, -1);
2121 DUK_ASSERT(h_obj
!= NULL
);
2123 h_strclass
= DUK_HOBJECT_GET_CLASS_STRING(thr
->heap
, h_obj
);
2125 DUK_ASSERT(h_strclass
!= NULL
);
2128 duk_push_sprintf(ctx
, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass
));
2131 #if !defined(DUK_USE_PARANOID_ERRORS)
2132 DUK_INTERNAL
void duk_push_hobject_class_string(duk_context
*ctx
, duk_hobject
*h
) {
2134 duk_hstring
*h_strclass
;
2136 DUK_ASSERT_CTX_VALID(ctx
);
2137 DUK_ASSERT(h
!= NULL
);
2138 thr
= (duk_hthread
*) ctx
;
2141 h_strclass
= DUK_HOBJECT_GET_CLASS_STRING(thr
->heap
, h
);
2142 DUK_ASSERT(h_strclass
!= NULL
);
2143 duk_push_sprintf(ctx
, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass
));
2145 #endif /* !DUK_USE_PARANOID_ERRORS */
2147 /* XXX: other variants like uint, u32 etc */
2148 DUK_INTERNAL duk_int_t
duk_to_int_clamped_raw(duk_context
*ctx
, duk_idx_t index
, duk_int_t minval
, duk_int_t maxval
, duk_bool_t
*out_clamped
) {
2149 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2152 duk_double_t d
, dmin
, dmax
;
2154 duk_bool_t clamped
= 0;
2156 DUK_ASSERT_CTX_VALID(ctx
);
2158 tv
= duk_require_tval(ctx
, index
);
2159 DUK_ASSERT(tv
!= NULL
);
2160 d
= duk_js_tointeger(thr
, tv
); /* E5 Section 9.4, ToInteger() */
2162 dmin
= (duk_double_t
) minval
;
2163 dmax
= (duk_double_t
) maxval
;
2169 } else if (d
> dmax
) {
2174 res
= (duk_int_t
) d
;
2176 DUK_UNREF(d
); /* SCANBUILD: with suitable dmin/dmax limits 'd' is unused */
2177 /* 'd' and 'res' agree here */
2179 /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */
2180 tv
= duk_get_tval(ctx
, index
);
2181 DUK_ASSERT(tv
!= NULL
); /* not popped by side effect */
2182 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
2183 #if defined(DUK_USE_FASTINT)
2184 #if (DUK_INT_MAX <= 0x7fffffffL)
2185 DUK_TVAL_SET_FASTINT_I32(tv
, res
);
2187 /* Clamping needed if duk_int_t is 64 bits. */
2188 if (res
>= DUK_FASTINT_MIN
&& res
<= DUK_FASTINT_MAX
) {
2189 DUK_TVAL_SET_FASTINT(tv
, res
);
2191 DUK_TVAL_SET_NUMBER(tv
, d
);
2195 DUK_TVAL_SET_NUMBER(tv
, d
); /* no need to incref */
2197 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
2200 *out_clamped
= clamped
;
2202 /* coerced value is updated to value stack even when RangeError thrown */
2204 DUK_ERROR_RANGE(thr
, DUK_STR_NUMBER_OUTSIDE_RANGE
);
2211 DUK_INTERNAL duk_int_t
duk_to_int_clamped(duk_context
*ctx
, duk_idx_t index
, duk_idx_t minval
, duk_idx_t maxval
) {
2213 return duk_to_int_clamped_raw(ctx
, index
, minval
, maxval
, &dummy
);
2216 DUK_INTERNAL duk_int_t
duk_to_int_check_range(duk_context
*ctx
, duk_idx_t index
, duk_int_t minval
, duk_int_t maxval
) {
2217 return duk_to_int_clamped_raw(ctx
, index
, minval
, maxval
, NULL
); /* out_clamped==NULL -> RangeError if outside range */
2220 DUK_EXTERNAL
const char *duk_to_string(duk_context
*ctx
, duk_idx_t index
) {
2221 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2224 DUK_ASSERT_CTX_VALID(ctx
);
2227 index
= duk_require_normalize_index(ctx
, index
);
2229 tv
= duk_require_tval(ctx
, index
);
2230 DUK_ASSERT(tv
!= NULL
);
2232 switch (DUK_TVAL_GET_TAG(tv
)) {
2233 case DUK_TAG_UNDEFINED
: {
2234 duk_push_hstring_stridx(ctx
, DUK_STRIDX_LC_UNDEFINED
);
2237 case DUK_TAG_NULL
: {
2238 duk_push_hstring_stridx(ctx
, DUK_STRIDX_LC_NULL
);
2241 case DUK_TAG_BOOLEAN
: {
2242 if (DUK_TVAL_GET_BOOLEAN(tv
)) {
2243 duk_push_hstring_stridx(ctx
, DUK_STRIDX_TRUE
);
2245 duk_push_hstring_stridx(ctx
, DUK_STRIDX_FALSE
);
2249 case DUK_TAG_STRING
: {
2253 case DUK_TAG_OBJECT
: {
2254 duk_to_primitive(ctx
, index
, DUK_HINT_STRING
);
2255 return duk_to_string(ctx
, index
); /* Note: recursive call */
2257 case DUK_TAG_BUFFER
: {
2258 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2260 /* Note: this allows creation of internal strings. */
2262 DUK_ASSERT(h
!= NULL
);
2263 duk_push_lstring(ctx
,
2264 (const char *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
),
2265 (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
));
2268 case DUK_TAG_POINTER
: {
2269 void *ptr
= DUK_TVAL_GET_POINTER(tv
);
2271 duk_push_sprintf(ctx
, DUK_STR_FMT_PTR
, (void *) ptr
);
2273 /* Represent a null pointer as 'null' to be consistent with
2274 * the JX format variant. Native '%p' format for a NULL
2275 * pointer may be e.g. '(nil)'.
2277 duk_push_hstring_stridx(ctx
, DUK_STRIDX_LC_NULL
);
2281 case DUK_TAG_LIGHTFUNC
: {
2282 /* Should match Function.prototype.toString() */
2283 duk_push_lightfunc_tostring(ctx
, tv
);
2286 #if defined(DUK_USE_FASTINT)
2287 case DUK_TAG_FASTINT
:
2291 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
2292 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2293 duk_push_tval(ctx
, tv
);
2294 duk_numconv_stringify(ctx
,
2296 0 /*precision:shortest*/,
2297 0 /*force_exponential*/);
2302 duk_replace(ctx
, index
);
2305 return duk_require_string(ctx
, index
);
2308 DUK_INTERNAL duk_hstring
*duk_to_hstring(duk_context
*ctx
, duk_idx_t index
) {
2310 DUK_ASSERT_CTX_VALID(ctx
);
2311 duk_to_string(ctx
, index
);
2312 ret
= duk_get_hstring(ctx
, index
);
2313 DUK_ASSERT(ret
!= NULL
);
2317 DUK_EXTERNAL
void *duk_to_buffer_raw(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
, duk_uint_t mode
) {
2318 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2320 const duk_uint8_t
*src_data
;
2321 duk_size_t src_size
;
2322 duk_uint8_t
*dst_data
;
2324 DUK_ASSERT_CTX_VALID(ctx
);
2327 index
= duk_require_normalize_index(ctx
, index
);
2329 h_buf
= duk_get_hbuffer(ctx
, index
);
2330 if (h_buf
!= NULL
) {
2331 /* Buffer is kept as is, with the fixed/dynamic nature of the
2332 * buffer only changed if requested. An external buffer
2333 * is converted into a non-external dynamic buffer in a
2334 * duk_to_dynamic_buffer() call.
2337 duk_uint8_t
*tmp_ptr
;
2339 tmp_ptr
= (duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_buf
);
2340 src_data
= (const duk_uint8_t
*) tmp_ptr
;
2341 src_size
= DUK_HBUFFER_GET_SIZE(h_buf
);
2343 tmp
= (DUK_HBUFFER_HAS_DYNAMIC(h_buf
) ? DUK_BUF_MODE_DYNAMIC
: DUK_BUF_MODE_FIXED
);
2344 if ((tmp
== mode
&& !DUK_HBUFFER_HAS_EXTERNAL(h_buf
)) ||
2345 mode
== DUK_BUF_MODE_DONTCARE
) {
2346 /* Note: src_data may be NULL if input is a zero-size
2353 /* Non-buffer value is first ToString() coerced, then converted
2354 * to a buffer (fixed buffer is used unless a dynamic buffer is
2355 * explicitly requested).
2358 src_data
= (const duk_uint8_t
*) duk_to_lstring(ctx
, index
, &src_size
);
2361 dst_data
= (duk_uint8_t
*) duk_push_buffer(ctx
, src_size
, (mode
== DUK_BUF_MODE_DYNAMIC
) /*dynamic*/);
2362 if (DUK_LIKELY(src_size
> 0)) {
2363 /* When src_size == 0, src_data may be NULL (if source
2364 * buffer is dynamic), and dst_data may be NULL (if
2365 * target buffer is dynamic). Avoid zero-size memcpy()
2366 * with an invalid pointer.
2368 DUK_MEMCPY((void *) dst_data
, (const void *) src_data
, (size_t) src_size
);
2370 duk_replace(ctx
, index
);
2374 *out_size
= src_size
;
2379 DUK_EXTERNAL
void *duk_to_pointer(duk_context
*ctx
, duk_idx_t index
) {
2383 DUK_ASSERT_CTX_VALID(ctx
);
2385 index
= duk_require_normalize_index(ctx
, index
);
2387 tv
= duk_require_tval(ctx
, index
);
2388 DUK_ASSERT(tv
!= NULL
);
2390 switch (DUK_TVAL_GET_TAG(tv
)) {
2391 case DUK_TAG_UNDEFINED
:
2393 case DUK_TAG_BOOLEAN
:
2396 case DUK_TAG_POINTER
:
2397 res
= DUK_TVAL_GET_POINTER(tv
);
2399 case DUK_TAG_STRING
:
2400 case DUK_TAG_OBJECT
:
2401 case DUK_TAG_BUFFER
:
2402 /* Heap allocated: return heap pointer which is NOT useful
2403 * for the caller, except for debugging.
2405 res
= (void *) DUK_TVAL_GET_HEAPHDR(tv
);
2407 case DUK_TAG_LIGHTFUNC
:
2408 /* Function pointers do not always cast correctly to void *
2409 * (depends on memory and segmentation model for instance),
2410 * so they coerce to NULL.
2414 #if defined(DUK_USE_FASTINT)
2415 case DUK_TAG_FASTINT
:
2419 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
2420 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2425 duk_push_pointer(ctx
, res
);
2426 duk_replace(ctx
, index
);
2430 DUK_EXTERNAL
void duk_to_object(duk_context
*ctx
, duk_idx_t index
) {
2431 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2433 duk_uint_t flags
= 0; /* shared flags for a subset of types */
2434 duk_small_int_t proto
= 0;
2436 DUK_ASSERT_CTX_VALID(ctx
);
2438 index
= duk_require_normalize_index(ctx
, index
);
2440 tv
= duk_require_tval(ctx
, index
);
2441 DUK_ASSERT(tv
!= NULL
);
2443 switch (DUK_TVAL_GET_TAG(tv
)) {
2444 case DUK_TAG_UNDEFINED
:
2445 case DUK_TAG_NULL
: {
2446 DUK_ERROR_TYPE(thr
, DUK_STR_NOT_OBJECT_COERCIBLE
);
2449 case DUK_TAG_BOOLEAN
: {
2450 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2451 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN
);
2452 proto
= DUK_BIDX_BOOLEAN_PROTOTYPE
;
2455 case DUK_TAG_STRING
: {
2456 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2457 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ
|
2458 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING
);
2459 proto
= DUK_BIDX_STRING_PROTOTYPE
;
2462 case DUK_TAG_OBJECT
: {
2466 case DUK_TAG_BUFFER
: {
2467 /* A plain buffer coerces to a Duktape.Buffer because it's the
2468 * object counterpart of the plain buffer value. But it might
2469 * still make more sense to produce an ArrayBuffer here?
2472 duk_hbufferobject
*h_bufobj
;
2475 h_val
= DUK_TVAL_GET_BUFFER(tv
);
2476 DUK_ASSERT(h_val
!= NULL
);
2478 h_bufobj
= duk_push_bufferobject_raw(ctx
,
2479 DUK_HOBJECT_FLAG_EXTENSIBLE
|
2480 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
2481 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER
),
2482 DUK_BIDX_BUFFER_PROTOTYPE
);
2483 DUK_ASSERT(h_bufobj
!= NULL
);
2484 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE((duk_hobject
*) h_bufobj
));
2485 DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject
*) h_bufobj
));
2487 h_bufobj
->buf
= h_val
;
2488 DUK_HBUFFER_INCREF(thr
, h_val
);
2489 DUK_ASSERT(h_bufobj
->offset
== 0);
2490 h_bufobj
->length
= (duk_uint_t
) DUK_HBUFFER_GET_SIZE(h_val
);
2491 DUK_ASSERT(h_bufobj
->shift
== 0);
2492 DUK_ASSERT(h_bufobj
->elem_type
== DUK_HBUFFEROBJECT_ELEM_UINT8
);
2494 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
2497 case DUK_TAG_POINTER
: {
2498 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2499 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER
);
2500 proto
= DUK_BIDX_POINTER_PROTOTYPE
;
2503 case DUK_TAG_LIGHTFUNC
: {
2504 /* Lightfunc coerces to a Function instance with concrete
2505 * properties. Since 'length' is virtual for Duktape/C
2506 * functions, don't need to define that.
2508 * The result is made extensible to mimic what happens to
2510 * > Object.isExtensible(Object('foo'))
2513 duk_small_uint_t lf_flags
;
2515 duk_small_uint_t lf_len
;
2516 duk_c_function func
;
2517 duk_hnativefunction
*nf
;
2519 DUK_TVAL_GET_LIGHTFUNC(tv
, func
, lf_flags
);
2521 nargs
= (duk_idx_t
) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags
);
2522 if (nargs
== DUK_LFUNC_NARGS_VARARGS
) {
2523 nargs
= (duk_idx_t
) DUK_VARARGS
;
2525 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2526 DUK_HOBJECT_FLAG_CONSTRUCTABLE
|
2527 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
2528 DUK_HOBJECT_FLAG_NEWENV
|
2529 DUK_HOBJECT_FLAG_STRICT
|
2530 DUK_HOBJECT_FLAG_NOTAIL
|
2531 /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */
2532 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
2533 (void) duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
2535 lf_len
= DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags
);
2536 if ((duk_idx_t
) lf_len
!= nargs
) {
2537 /* Explicit length is only needed if it differs from 'nargs'. */
2538 duk_push_int(ctx
, (duk_int_t
) lf_len
);
2539 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LENGTH
, DUK_PROPDESC_FLAGS_NONE
);
2541 duk_push_lightfunc_name(ctx
, tv
);
2542 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
, DUK_PROPDESC_FLAGS_NONE
);
2544 nf
= duk_get_hnativefunction(ctx
, -1);
2545 DUK_ASSERT(nf
!= NULL
);
2546 nf
->magic
= (duk_int16_t
) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags
);
2548 /* Enable DUKFUNC exotic behavior once properties are set up. */
2549 DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject
*) nf
);
2552 #if defined(DUK_USE_FASTINT)
2553 case DUK_TAG_FASTINT
:
2556 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
2557 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2558 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2559 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER
);
2560 proto
= DUK_BIDX_NUMBER_PROTOTYPE
;
2567 (void) duk_push_object_helper(ctx
, flags
, proto
);
2569 /* Note: Boolean prototype's internal value property is not writable,
2570 * but duk_xdef_prop_stridx() disregards the write protection. Boolean
2571 * instances are immutable.
2573 * String and buffer special behaviors are already enabled which is not
2574 * ideal, but a write to the internal value is not affected by them.
2576 duk_dup(ctx
, index
);
2577 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_INT_VALUE
, DUK_PROPDESC_FLAGS_NONE
);
2580 duk_replace(ctx
, index
);
2587 DUK_LOCAL duk_bool_t
duk__tag_check(duk_context
*ctx
, duk_idx_t index
, duk_small_uint_t tag
) {
2590 tv
= duk_get_tval(ctx
, index
);
2594 return (DUK_TVAL_GET_TAG(tv
) == tag
);
2597 DUK_LOCAL duk_bool_t
duk__obj_flag_any_default_false(duk_context
*ctx
, duk_idx_t index
, duk_uint_t flag_mask
) {
2600 DUK_ASSERT_CTX_VALID(ctx
);
2602 obj
= duk_get_hobject(ctx
, index
);
2604 return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr
*) obj
, flag_mask
) ? 1 : 0);
2609 DUK_EXTERNAL duk_int_t
duk_get_type(duk_context
*ctx
, duk_idx_t index
) {
2612 DUK_ASSERT_CTX_VALID(ctx
);
2614 tv
= duk_get_tval(ctx
, index
);
2616 return DUK_TYPE_NONE
;
2618 switch (DUK_TVAL_GET_TAG(tv
)) {
2619 case DUK_TAG_UNDEFINED
:
2620 return DUK_TYPE_UNDEFINED
;
2622 return DUK_TYPE_NULL
;
2623 case DUK_TAG_BOOLEAN
:
2624 return DUK_TYPE_BOOLEAN
;
2625 case DUK_TAG_STRING
:
2626 return DUK_TYPE_STRING
;
2627 case DUK_TAG_OBJECT
:
2628 return DUK_TYPE_OBJECT
;
2629 case DUK_TAG_BUFFER
:
2630 return DUK_TYPE_BUFFER
;
2631 case DUK_TAG_POINTER
:
2632 return DUK_TYPE_POINTER
;
2633 case DUK_TAG_LIGHTFUNC
:
2634 return DUK_TYPE_LIGHTFUNC
;
2635 #if defined(DUK_USE_FASTINT)
2636 case DUK_TAG_FASTINT
:
2639 /* Note: number has no explicit tag (in 8-byte representation) */
2640 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
2641 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2642 return DUK_TYPE_NUMBER
;
2647 #if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS)
2648 DUK_LOCAL
const char *duk__type_names
[] = {
2661 DUK_INTERNAL
const char *duk_get_type_name(duk_context
*ctx
, duk_idx_t index
) {
2664 type_tag
= duk_get_type(ctx
, index
);
2665 DUK_ASSERT(type_tag
>= DUK_TYPE_MIN
&& type_tag
<= DUK_TYPE_MAX
);
2666 DUK_ASSERT(DUK_TYPE_MIN
== 0 && sizeof(duk__type_names
) / sizeof(const char *) == DUK_TYPE_MAX
+ 1);
2668 return duk__type_names
[type_tag
];
2672 DUK_EXTERNAL duk_bool_t
duk_check_type(duk_context
*ctx
, duk_idx_t index
, duk_int_t type
) {
2673 DUK_ASSERT_CTX_VALID(ctx
);
2675 return (duk_get_type(ctx
, index
) == type
) ? 1 : 0;
2678 DUK_EXTERNAL duk_uint_t
duk_get_type_mask(duk_context
*ctx
, duk_idx_t index
) {
2681 DUK_ASSERT_CTX_VALID(ctx
);
2683 tv
= duk_get_tval(ctx
, index
);
2685 return DUK_TYPE_MASK_NONE
;
2687 switch (DUK_TVAL_GET_TAG(tv
)) {
2688 case DUK_TAG_UNDEFINED
:
2689 return DUK_TYPE_MASK_UNDEFINED
;
2691 return DUK_TYPE_MASK_NULL
;
2692 case DUK_TAG_BOOLEAN
:
2693 return DUK_TYPE_MASK_BOOLEAN
;
2694 case DUK_TAG_STRING
:
2695 return DUK_TYPE_MASK_STRING
;
2696 case DUK_TAG_OBJECT
:
2697 return DUK_TYPE_MASK_OBJECT
;
2698 case DUK_TAG_BUFFER
:
2699 return DUK_TYPE_MASK_BUFFER
;
2700 case DUK_TAG_POINTER
:
2701 return DUK_TYPE_MASK_POINTER
;
2702 case DUK_TAG_LIGHTFUNC
:
2703 return DUK_TYPE_MASK_LIGHTFUNC
;
2704 #if defined(DUK_USE_FASTINT)
2705 case DUK_TAG_FASTINT
:
2708 /* Note: number has no explicit tag (in 8-byte representation) */
2709 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
2710 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2711 return DUK_TYPE_MASK_NUMBER
;
2716 DUK_EXTERNAL duk_bool_t
duk_check_type_mask(duk_context
*ctx
, duk_idx_t index
, duk_uint_t mask
) {
2717 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2719 DUK_ASSERT_CTX_VALID(ctx
);
2721 if (duk_get_type_mask(ctx
, index
) & mask
) {
2724 if (mask
& DUK_TYPE_MASK_THROW
) {
2725 DUK_ERROR_TYPE(thr
, DUK_STR_UNEXPECTED_TYPE
);
2731 DUK_EXTERNAL duk_bool_t
duk_is_undefined(duk_context
*ctx
, duk_idx_t index
) {
2732 DUK_ASSERT_CTX_VALID(ctx
);
2733 return duk__tag_check(ctx
, index
, DUK_TAG_UNDEFINED
);
2736 DUK_EXTERNAL duk_bool_t
duk_is_null(duk_context
*ctx
, duk_idx_t index
) {
2737 DUK_ASSERT_CTX_VALID(ctx
);
2738 return duk__tag_check(ctx
, index
, DUK_TAG_NULL
);
2741 DUK_EXTERNAL duk_bool_t
duk_is_null_or_undefined(duk_context
*ctx
, duk_idx_t index
) {
2743 duk_small_uint_t tag
;
2745 DUK_ASSERT_CTX_VALID(ctx
);
2747 tv
= duk_get_tval(ctx
, index
);
2751 tag
= DUK_TVAL_GET_TAG(tv
);
2752 return (tag
== DUK_TAG_UNDEFINED
) || (tag
== DUK_TAG_NULL
);
2755 DUK_EXTERNAL duk_bool_t
duk_is_boolean(duk_context
*ctx
, duk_idx_t index
) {
2756 DUK_ASSERT_CTX_VALID(ctx
);
2757 return duk__tag_check(ctx
, index
, DUK_TAG_BOOLEAN
);
2760 DUK_EXTERNAL duk_bool_t
duk_is_number(duk_context
*ctx
, duk_idx_t index
) {
2763 DUK_ASSERT_CTX_VALID(ctx
);
2766 * Number is special because it doesn't have a specific
2767 * tag in the 8-byte representation.
2770 /* XXX: shorter version for 12-byte representation? */
2772 tv
= duk_get_tval(ctx
, index
);
2776 return DUK_TVAL_IS_NUMBER(tv
);
2779 DUK_EXTERNAL duk_bool_t
duk_is_nan(duk_context
*ctx
, duk_idx_t index
) {
2780 /* XXX: This will now return false for non-numbers, even though they would
2781 * coerce to NaN (as a general rule). In particular, duk_get_number()
2782 * returns a NaN for non-numbers, so should this function also return
2783 * true for non-numbers?
2788 DUK_ASSERT_CTX_VALID(ctx
);
2790 tv
= duk_get_tval(ctx
, index
);
2791 if (!tv
|| !DUK_TVAL_IS_NUMBER(tv
)) {
2794 return DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv
));
2797 DUK_EXTERNAL duk_bool_t
duk_is_string(duk_context
*ctx
, duk_idx_t index
) {
2798 DUK_ASSERT_CTX_VALID(ctx
);
2799 return duk__tag_check(ctx
, index
, DUK_TAG_STRING
);
2802 DUK_EXTERNAL duk_bool_t
duk_is_object(duk_context
*ctx
, duk_idx_t index
) {
2803 DUK_ASSERT_CTX_VALID(ctx
);
2804 return duk__tag_check(ctx
, index
, DUK_TAG_OBJECT
);
2807 DUK_EXTERNAL duk_bool_t
duk_is_buffer(duk_context
*ctx
, duk_idx_t index
) {
2808 DUK_ASSERT_CTX_VALID(ctx
);
2809 return duk__tag_check(ctx
, index
, DUK_TAG_BUFFER
);
2812 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2813 DUK_EXTERNAL duk_bool_t
duk_is_buffer_data(duk_context
*ctx
, duk_idx_t idx
) {
2816 DUK_ASSERT_CTX_VALID(ctx
);
2818 tv
= duk_get_tval(ctx
, idx
);
2822 if (DUK_TVAL_IS_BUFFER(tv
)) {
2824 } else if (DUK_TVAL_IS_OBJECT(tv
)) {
2825 duk_hobject
*h
= DUK_TVAL_GET_OBJECT(tv
);
2826 DUK_ASSERT(h
!= NULL
);
2827 if (DUK_HOBJECT_IS_BUFFEROBJECT(h
)) {
2833 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
2834 DUK_EXTERNAL duk_bool_t
duk_is_buffer_data(duk_context
*ctx
, duk_idx_t idx
) {
2835 DUK_ASSERT_CTX_VALID(ctx
);
2837 return duk_is_buffer(ctx
, idx
);
2839 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2841 DUK_EXTERNAL duk_bool_t
duk_is_pointer(duk_context
*ctx
, duk_idx_t index
) {
2842 DUK_ASSERT_CTX_VALID(ctx
);
2843 return duk__tag_check(ctx
, index
, DUK_TAG_POINTER
);
2846 DUK_EXTERNAL duk_bool_t
duk_is_lightfunc(duk_context
*ctx
, duk_idx_t index
) {
2847 DUK_ASSERT_CTX_VALID(ctx
);
2848 return duk__tag_check(ctx
, index
, DUK_TAG_LIGHTFUNC
);
2851 DUK_EXTERNAL duk_bool_t
duk_is_array(duk_context
*ctx
, duk_idx_t index
) {
2854 DUK_ASSERT_CTX_VALID(ctx
);
2856 obj
= duk_get_hobject(ctx
, index
);
2858 return (DUK_HOBJECT_GET_CLASS_NUMBER(obj
) == DUK_HOBJECT_CLASS_ARRAY
? 1 : 0);
2863 DUK_EXTERNAL duk_bool_t
duk_is_function(duk_context
*ctx
, duk_idx_t index
) {
2866 DUK_ASSERT_CTX_VALID(ctx
);
2868 tv
= duk_get_tval(ctx
, index
);
2869 if (tv
&& DUK_TVAL_IS_LIGHTFUNC(tv
)) {
2872 return duk__obj_flag_any_default_false(ctx
,
2874 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
|
2875 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
2876 DUK_HOBJECT_FLAG_BOUND
);
2879 DUK_EXTERNAL duk_bool_t
duk_is_c_function(duk_context
*ctx
, duk_idx_t index
) {
2880 DUK_ASSERT_CTX_VALID(ctx
);
2881 return duk__obj_flag_any_default_false(ctx
,
2883 DUK_HOBJECT_FLAG_NATIVEFUNCTION
);
2886 DUK_EXTERNAL duk_bool_t
duk_is_ecmascript_function(duk_context
*ctx
, duk_idx_t index
) {
2887 DUK_ASSERT_CTX_VALID(ctx
);
2888 return duk__obj_flag_any_default_false(ctx
,
2890 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
);
2893 DUK_EXTERNAL duk_bool_t
duk_is_bound_function(duk_context
*ctx
, duk_idx_t index
) {
2894 DUK_ASSERT_CTX_VALID(ctx
);
2895 return duk__obj_flag_any_default_false(ctx
,
2897 DUK_HOBJECT_FLAG_BOUND
);
2900 DUK_EXTERNAL duk_bool_t
duk_is_thread(duk_context
*ctx
, duk_idx_t index
) {
2901 DUK_ASSERT_CTX_VALID(ctx
);
2902 return duk__obj_flag_any_default_false(ctx
,
2904 DUK_HOBJECT_FLAG_THREAD
);
2907 DUK_EXTERNAL duk_bool_t
duk_is_fixed_buffer(duk_context
*ctx
, duk_idx_t index
) {
2910 DUK_ASSERT_CTX_VALID(ctx
);
2912 tv
= duk_get_tval(ctx
, index
);
2913 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
2914 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2915 DUK_ASSERT(h
!= NULL
);
2916 return (DUK_HBUFFER_HAS_DYNAMIC(h
) ? 0 : 1);
2921 DUK_EXTERNAL duk_bool_t
duk_is_dynamic_buffer(duk_context
*ctx
, duk_idx_t index
) {
2924 DUK_ASSERT_CTX_VALID(ctx
);
2926 tv
= duk_get_tval(ctx
, index
);
2927 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
2928 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2929 DUK_ASSERT(h
!= NULL
);
2930 return (DUK_HBUFFER_HAS_DYNAMIC(h
) && !DUK_HBUFFER_HAS_EXTERNAL(h
) ? 1 : 0);
2935 DUK_EXTERNAL duk_bool_t
duk_is_external_buffer(duk_context
*ctx
, duk_idx_t index
) {
2938 DUK_ASSERT_CTX_VALID(ctx
);
2940 tv
= duk_get_tval(ctx
, index
);
2941 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
2942 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2943 DUK_ASSERT(h
!= NULL
);
2944 return (DUK_HBUFFER_HAS_DYNAMIC(h
) && DUK_HBUFFER_HAS_EXTERNAL(h
) ? 1 : 0);
2949 DUK_EXTERNAL duk_errcode_t
duk_get_error_code(duk_context
*ctx
, duk_idx_t index
) {
2950 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2954 DUK_ASSERT_CTX_VALID(ctx
);
2956 h
= duk_get_hobject(ctx
, index
);
2958 sanity
= DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY
;
2961 return DUK_ERR_NONE
;
2963 if (h
== thr
->builtins
[DUK_BIDX_EVAL_ERROR_PROTOTYPE
]) {
2964 return DUK_ERR_EVAL_ERROR
;
2966 if (h
== thr
->builtins
[DUK_BIDX_RANGE_ERROR_PROTOTYPE
]) {
2967 return DUK_ERR_RANGE_ERROR
;
2969 if (h
== thr
->builtins
[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE
]) {
2970 return DUK_ERR_REFERENCE_ERROR
;
2972 if (h
== thr
->builtins
[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE
]) {
2973 return DUK_ERR_SYNTAX_ERROR
;
2975 if (h
== thr
->builtins
[DUK_BIDX_TYPE_ERROR_PROTOTYPE
]) {
2976 return DUK_ERR_TYPE_ERROR
;
2978 if (h
== thr
->builtins
[DUK_BIDX_URI_ERROR_PROTOTYPE
]) {
2979 return DUK_ERR_URI_ERROR
;
2981 if (h
== thr
->builtins
[DUK_BIDX_ERROR_PROTOTYPE
]) {
2982 return DUK_ERR_ERROR
;
2985 h
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
);
2986 } while (--sanity
> 0);
2988 return DUK_ERR_NONE
;
2995 DUK_INTERNAL
void duk_push_tval(duk_context
*ctx
, duk_tval
*tv
) {
2999 DUK_ASSERT_CTX_VALID(ctx
);
3000 DUK_ASSERT(tv
!= NULL
);
3001 thr
= (duk_hthread
*) ctx
;
3003 tv_slot
= thr
->valstack_top
++;
3004 DUK_TVAL_SET_TVAL(tv_slot
, tv
);
3005 DUK_TVAL_INCREF(thr
, tv
); /* no side effects */
3008 DUK_EXTERNAL
void duk_push_undefined(duk_context
*ctx
) {
3011 DUK_ASSERT_CTX_VALID(ctx
);
3012 thr
= (duk_hthread
*) ctx
;
3015 /* Because value stack init policy is 'undefined above top',
3016 * we don't need to write, just assert.
3018 thr
->valstack_top
++;
3019 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr
->valstack_top
- 1));
3022 DUK_EXTERNAL
void duk_push_null(duk_context
*ctx
) {
3026 DUK_ASSERT_CTX_VALID(ctx
);
3027 thr
= (duk_hthread
*) ctx
;
3029 tv_slot
= thr
->valstack_top
++;
3030 DUK_TVAL_SET_NULL(tv_slot
);
3033 DUK_EXTERNAL
void duk_push_boolean(duk_context
*ctx
, duk_bool_t val
) {
3038 DUK_ASSERT_CTX_VALID(ctx
);
3039 thr
= (duk_hthread
*) ctx
;
3041 b
= (val
? 1 : 0); /* ensure value is 1 or 0 (not other non-zero) */
3042 tv_slot
= thr
->valstack_top
++;
3043 DUK_TVAL_SET_BOOLEAN(tv_slot
, b
);
3046 DUK_EXTERNAL
void duk_push_true(duk_context
*ctx
) {
3050 DUK_ASSERT_CTX_VALID(ctx
);
3051 thr
= (duk_hthread
*) ctx
;
3053 tv_slot
= thr
->valstack_top
++;
3054 DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot
);
3057 DUK_EXTERNAL
void duk_push_false(duk_context
*ctx
) {
3061 DUK_ASSERT_CTX_VALID(ctx
);
3062 thr
= (duk_hthread
*) ctx
;
3064 tv_slot
= thr
->valstack_top
++;
3065 DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot
);
3068 /* normalize NaN which may not match our canonical internal NaN */
3069 DUK_EXTERNAL
void duk_push_number(duk_context
*ctx
, duk_double_t val
) {
3072 duk_double_union du
;
3074 DUK_ASSERT_CTX_VALID(ctx
);
3075 thr
= (duk_hthread
*) ctx
;
3078 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du
);
3079 tv_slot
= thr
->valstack_top
++;
3080 DUK_TVAL_SET_NUMBER(tv_slot
, du
.d
);
3083 DUK_EXTERNAL
void duk_push_int(duk_context
*ctx
, duk_int_t val
) {
3084 #if defined(DUK_USE_FASTINT)
3088 DUK_ASSERT_CTX_VALID(ctx
);
3089 thr
= (duk_hthread
*) ctx
;
3091 tv_slot
= thr
->valstack_top
++;
3092 #if DUK_INT_MAX <= 0x7fffffffL
3093 DUK_TVAL_SET_FASTINT_I32(tv_slot
, (duk_int32_t
) val
);
3095 if (val
>= DUK_FASTINT_MIN
&& val
<= DUK_FASTINT_MAX
) {
3096 DUK_TVAL_SET_FASTINT(tv_slot
, (duk_int64_t
) val
);
3098 duk_double_t
= (duk_double_t
) val
;
3099 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3102 #else /* DUK_USE_FASTINT */
3107 DUK_ASSERT_CTX_VALID(ctx
);
3108 thr
= (duk_hthread
*) ctx
;
3110 d
= (duk_double_t
) val
;
3111 tv_slot
= thr
->valstack_top
++;
3112 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3113 #endif /* DUK_USE_FASTINT */
3116 DUK_EXTERNAL
void duk_push_uint(duk_context
*ctx
, duk_uint_t val
) {
3117 #if defined(DUK_USE_FASTINT)
3121 DUK_ASSERT_CTX_VALID(ctx
);
3122 thr
= (duk_hthread
*) ctx
;
3124 tv_slot
= thr
->valstack_top
++;
3125 #if DUK_UINT_MAX <= 0xffffffffUL
3126 DUK_TVAL_SET_FASTINT_U32(tv_slot
, (duk_uint32_t
) val
);
3128 if (val
<= DUK_FASTINT_MAX
) { /* val is unsigned so >= 0 */
3129 /* XXX: take advantage of val being unsigned, no need to mask */
3130 DUK_TVAL_SET_FASTINT(tv_slot
, (duk_int64_t
) val
);
3132 duk_double_t
= (duk_double_t
) val
;
3133 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3136 #else /* DUK_USE_FASTINT */
3141 DUK_ASSERT_CTX_VALID(ctx
);
3142 thr
= (duk_hthread
*) ctx
;
3144 d
= (duk_double_t
) val
;
3145 tv_slot
= thr
->valstack_top
++;
3146 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3147 #endif /* DUK_USE_FASTINT */
3150 DUK_EXTERNAL
void duk_push_nan(duk_context
*ctx
) {
3153 duk_double_union du
;
3155 DUK_ASSERT_CTX_VALID(ctx
);
3156 thr
= (duk_hthread
*) ctx
;
3158 DUK_DBLUNION_SET_NAN(&du
);
3159 DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du
));
3160 tv_slot
= thr
->valstack_top
++;
3161 DUK_TVAL_SET_NUMBER(tv_slot
, du
.d
);
3164 DUK_EXTERNAL
const char *duk_push_lstring(duk_context
*ctx
, const char *str
, duk_size_t len
) {
3165 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3169 DUK_ASSERT_CTX_VALID(ctx
);
3171 /* check stack before interning (avoid hanging temp) */
3172 if (thr
->valstack_top
>= thr
->valstack_end
) {
3173 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3176 /* NULL with zero length represents an empty string; NULL with higher
3177 * length is also now trated like an empty string although it is
3178 * a bit dubious. This is unlike duk_push_string() which pushes a
3179 * 'null' if the input string is a NULL.
3185 /* Check for maximum string length */
3186 if (len
> DUK_HSTRING_MAX_BYTELEN
) {
3187 DUK_ERROR_RANGE(thr
, DUK_STR_STRING_TOO_LONG
);
3190 h
= duk_heap_string_intern_checked(thr
, (const duk_uint8_t
*) str
, (duk_uint32_t
) len
);
3191 DUK_ASSERT(h
!= NULL
);
3193 tv_slot
= thr
->valstack_top
++;
3194 DUK_TVAL_SET_STRING(tv_slot
, h
);
3195 DUK_HSTRING_INCREF(thr
, h
); /* no side effects */
3197 return (const char *) DUK_HSTRING_GET_DATA(h
);
3200 DUK_EXTERNAL
const char *duk_push_string(duk_context
*ctx
, const char *str
) {
3201 DUK_ASSERT_CTX_VALID(ctx
);
3204 return duk_push_lstring(ctx
, str
, DUK_STRLEN(str
));
3211 #ifdef DUK_USE_FILE_IO
3212 /* This is a bit clunky because it is ANSI C portable. Should perhaps
3213 * relocate to another file because this is potentially platform
3216 DUK_EXTERNAL
const char *duk_push_string_file_raw(duk_context
*ctx
, const char *path
, duk_uint_t flags
) {
3217 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3220 long sz
; /* ANSI C typing */
3222 DUK_ASSERT_CTX_VALID(ctx
);
3227 f
= DUK_FOPEN(path
, "rb");
3231 if (DUK_FSEEK(f
, 0, SEEK_END
) < 0) {
3238 if (DUK_FSEEK(f
, 0, SEEK_SET
) < 0) {
3241 buf
= (char *) duk_push_fixed_buffer(ctx
, (duk_size_t
) sz
);
3242 DUK_ASSERT(buf
!= NULL
);
3243 if ((duk_size_t
) DUK_FREAD(buf
, 1, (size_t) sz
, f
) != (duk_size_t
) sz
) {
3246 (void) DUK_FCLOSE(f
); /* ignore fclose() error */
3248 return duk_to_string(ctx
, -1);
3256 DUK_ASSERT(flags
== DUK_STRING_PUSH_SAFE
); /* only flag now */
3257 duk_push_undefined(ctx
);
3259 /* XXX: string not shared because it is conditional */
3260 DUK_ERROR_TYPE(thr
, "read file error");
3265 DUK_EXTERNAL
const char *duk_push_string_file_raw(duk_context
*ctx
, const char *path
, duk_uint_t flags
) {
3266 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3267 DUK_ASSERT_CTX_VALID(ctx
);
3271 DUK_ASSERT(flags
== DUK_STRING_PUSH_SAFE
); /* only flag now */
3272 duk_push_undefined(ctx
);
3274 /* XXX: string not shared because it is conditional */
3275 DUK_ERROR_UNSUPPORTED(thr
, "file I/O disabled");
3279 #endif /* DUK_USE_FILE_IO */
3281 DUK_EXTERNAL
void duk_push_pointer(duk_context
*ctx
, void *val
) {
3285 DUK_ASSERT_CTX_VALID(ctx
);
3286 thr
= (duk_hthread
*) ctx
;
3288 tv_slot
= thr
->valstack_top
++;
3289 DUK_TVAL_SET_POINTER(tv_slot
, val
);
3292 DUK_LOCAL
void duk__push_this_helper(duk_context
*ctx
, duk_small_uint_t check_object_coercible
) {
3296 DUK_ASSERT_CTX_VALID(ctx
);
3297 DUK_ASSERT_DISABLE(thr
->callstack_top
>= 0); /* avoid warning (unsigned) */
3298 thr
= (duk_hthread
*) ctx
;
3299 DUK_ASSERT(thr
->callstack_top
<= thr
->callstack_size
);
3302 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr
->valstack_top
)); /* because of valstack init policy */
3303 tv_slot
= thr
->valstack_top
++;
3305 if (DUK_UNLIKELY(thr
->callstack_top
== 0)) {
3306 if (check_object_coercible
) {
3309 /* 'undefined' already on stack top */
3313 /* 'this' binding is just before current activation's bottom */
3314 DUK_ASSERT(thr
->valstack_bottom
> thr
->valstack
);
3315 tv
= thr
->valstack_bottom
- 1;
3316 if (check_object_coercible
&&
3317 (DUK_TVAL_IS_UNDEFINED(tv
) || DUK_TVAL_IS_NULL(tv
))) {
3318 /* XXX: better macro for DUK_TVAL_IS_UNDEFINED_OR_NULL(tv) */
3322 DUK_TVAL_SET_TVAL(tv_slot
, tv
);
3323 DUK_TVAL_INCREF(thr
, tv
);
3328 DUK_ERROR_TYPE(thr
, DUK_STR_NOT_OBJECT_COERCIBLE
);
3331 DUK_EXTERNAL
void duk_push_this(duk_context
*ctx
) {
3332 DUK_ASSERT_CTX_VALID(ctx
);
3334 duk__push_this_helper(ctx
, 0 /*check_object_coercible*/);
3337 DUK_INTERNAL
void duk_push_this_check_object_coercible(duk_context
*ctx
) {
3338 DUK_ASSERT_CTX_VALID(ctx
);
3340 duk__push_this_helper(ctx
, 1 /*check_object_coercible*/);
3343 DUK_INTERNAL duk_hobject
*duk_push_this_coercible_to_object(duk_context
*ctx
) {
3346 DUK_ASSERT_CTX_VALID(ctx
);
3348 duk__push_this_helper(ctx
, 1 /*check_object_coercible*/);
3349 duk_to_object(ctx
, -1);
3350 h
= duk_get_hobject(ctx
, -1);
3351 DUK_ASSERT(h
!= NULL
);
3355 DUK_INTERNAL duk_hstring
*duk_push_this_coercible_to_string(duk_context
*ctx
) {
3358 DUK_ASSERT_CTX_VALID(ctx
);
3360 duk__push_this_helper(ctx
, 1 /*check_object_coercible*/);
3361 duk_to_string(ctx
, -1);
3362 h
= duk_get_hstring(ctx
, -1);
3363 DUK_ASSERT(h
!= NULL
);
3367 DUK_INTERNAL duk_tval
*duk_get_borrowed_this_tval(duk_context
*ctx
) {
3370 DUK_ASSERT(ctx
!= NULL
);
3371 thr
= (duk_hthread
*) ctx
;
3373 DUK_ASSERT(thr
->callstack_top
> 0); /* caller required to know */
3374 DUK_ASSERT(thr
->valstack_bottom
> thr
->valstack
); /* consequence of above */
3375 DUK_ASSERT(thr
->valstack_bottom
- 1 >= thr
->valstack
); /* 'this' binding exists */
3377 return thr
->valstack_bottom
- 1;
3380 DUK_EXTERNAL
void duk_push_current_function(duk_context
*ctx
) {
3381 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3382 duk_activation
*act
;
3384 DUK_ASSERT_CTX_VALID(ctx
);
3385 DUK_ASSERT(thr
!= NULL
);
3386 DUK_ASSERT_DISABLE(thr
->callstack_top
>= 0);
3387 DUK_ASSERT(thr
->callstack_top
<= thr
->callstack_size
);
3389 act
= duk_hthread_get_current_activation(thr
);
3391 duk_push_tval(ctx
, &act
->tv_func
);
3393 duk_push_undefined(ctx
);
3397 DUK_EXTERNAL
void duk_push_current_thread(duk_context
*ctx
) {
3398 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3400 DUK_ASSERT_CTX_VALID(ctx
);
3401 DUK_ASSERT(thr
!= NULL
);
3403 if (thr
->heap
->curr_thread
) {
3404 duk_push_hobject(ctx
, (duk_hobject
*) thr
->heap
->curr_thread
);
3406 duk_push_undefined(ctx
);
3410 DUK_EXTERNAL
void duk_push_global_object(duk_context
*ctx
) {
3411 DUK_ASSERT_CTX_VALID(ctx
);
3413 duk_push_hobject_bidx(ctx
, DUK_BIDX_GLOBAL
);
3416 /* XXX: size optimize */
3417 DUK_LOCAL
void duk__push_stash(duk_context
*ctx
) {
3418 DUK_ASSERT_CTX_VALID(ctx
);
3419 if (!duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_VALUE
)) {
3420 DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use"));
3422 duk_push_object_internal(ctx
);
3424 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_INT_VALUE
, DUK_PROPDESC_FLAGS_C
); /* [ ... parent stash stash ] -> [ ... parent stash ] */
3426 duk_remove(ctx
, -2);
3429 DUK_EXTERNAL
void duk_push_heap_stash(duk_context
*ctx
) {
3430 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3432 DUK_ASSERT_CTX_VALID(ctx
);
3434 DUK_ASSERT(heap
->heap_object
!= NULL
);
3435 duk_push_hobject(ctx
, heap
->heap_object
);
3436 duk__push_stash(ctx
);
3439 DUK_EXTERNAL
void duk_push_global_stash(duk_context
*ctx
) {
3440 DUK_ASSERT_CTX_VALID(ctx
);
3441 duk_push_global_object(ctx
);
3442 duk__push_stash(ctx
);
3445 DUK_EXTERNAL
void duk_push_thread_stash(duk_context
*ctx
, duk_context
*target_ctx
) {
3446 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3447 DUK_ASSERT_CTX_VALID(ctx
);
3449 DUK_ERROR_API(thr
, DUK_STR_INVALID_CALL_ARGS
);
3450 return; /* not reached */
3452 duk_push_hobject(ctx
, (duk_hobject
*) target_ctx
);
3453 duk__push_stash(ctx
);
3456 /* XXX: duk_ssize_t would be useful here */
3457 DUK_LOCAL duk_int_t
duk__try_push_vsprintf(duk_context
*ctx
, void *buf
, duk_size_t sz
, const char *fmt
, va_list ap
) {
3460 DUK_ASSERT_CTX_VALID(ctx
);
3463 /* NUL terminator handling doesn't matter here */
3464 len
= DUK_VSNPRINTF((char *) buf
, sz
, fmt
, ap
);
3465 if (len
< (duk_int_t
) sz
) {
3466 /* Return value of 'sz' or more indicates output was (potentially)
3469 return (duk_int_t
) len
;
3474 DUK_EXTERNAL
const char *duk_push_vsprintf(duk_context
*ctx
, const char *fmt
, va_list ap
) {
3475 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3476 duk_uint8_t stack_buf
[DUK_PUSH_SPRINTF_INITIAL_SIZE
];
3477 duk_size_t sz
= DUK_PUSH_SPRINTF_INITIAL_SIZE
;
3478 duk_bool_t pushed_buf
= 0;
3480 duk_int_t len
; /* XXX: duk_ssize_t */
3483 DUK_ASSERT_CTX_VALID(ctx
);
3485 /* special handling of fmt==NULL */
3488 duk_push_hstring_stridx(ctx
, DUK_STRIDX_EMPTY_STRING
);
3489 h_str
= DUK_HTHREAD_STRING_EMPTY_STRING(thr
); /* rely on interning, must be this string */
3490 return (const char *) DUK_HSTRING_GET_DATA(h_str
);
3493 /* initial estimate based on format string */
3494 sz
= DUK_STRLEN(fmt
) + 16; /* format plus something to avoid just missing */
3495 if (sz
< DUK_PUSH_SPRINTF_INITIAL_SIZE
) {
3496 sz
= DUK_PUSH_SPRINTF_INITIAL_SIZE
;
3500 /* Try to make do with a stack buffer to avoid allocating a temporary buffer.
3501 * This works 99% of the time which is quite nice.
3504 va_list ap_copy
; /* copied so that 'ap' can be reused */
3506 if (sz
<= sizeof(stack_buf
)) {
3508 } else if (!pushed_buf
) {
3510 buf
= duk_push_dynamic_buffer(ctx
, sz
);
3512 buf
= duk_resize_buffer(ctx
, -1, sz
);
3514 DUK_ASSERT(buf
!= NULL
);
3516 DUK_VA_COPY(ap_copy
, ap
);
3517 len
= duk__try_push_vsprintf(ctx
, buf
, sz
, fmt
, ap_copy
);
3523 /* failed, resize and try again */
3525 if (sz
>= DUK_PUSH_SPRINTF_SANITY_LIMIT
) {
3526 DUK_ERROR_API(thr
, DUK_STR_SPRINTF_TOO_LONG
);
3530 /* Cannot use duk_to_string() on the buffer because it is usually
3531 * larger than 'len'. Also, 'buf' is usually a stack buffer.
3533 res
= duk_push_lstring(ctx
, (const char *) buf
, (duk_size_t
) len
); /* [ buf? res ] */
3535 duk_remove(ctx
, -2);
3540 DUK_EXTERNAL
const char *duk_push_sprintf(duk_context
*ctx
, const char *fmt
, ...) {
3544 DUK_ASSERT_CTX_VALID(ctx
);
3546 /* allow fmt==NULL */
3548 ret
= duk_push_vsprintf(ctx
, fmt
, ap
);
3554 DUK_INTERNAL duk_idx_t
duk_push_object_helper(duk_context
*ctx
, duk_uint_t hobject_flags_and_class
, duk_small_int_t prototype_bidx
) {
3555 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3560 DUK_ASSERT_CTX_VALID(ctx
);
3561 DUK_ASSERT(prototype_bidx
== -1 ||
3562 (prototype_bidx
>= 0 && prototype_bidx
< DUK_NUM_BUILTINS
));
3564 /* check stack first */
3565 if (thr
->valstack_top
>= thr
->valstack_end
) {
3566 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3569 h
= duk_hobject_alloc(thr
->heap
, hobject_flags_and_class
);
3571 DUK_ERROR_ALLOC_DEFMSG(thr
);
3574 DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h
->hdr
.h_flags
));
3576 tv_slot
= thr
->valstack_top
;
3577 DUK_TVAL_SET_OBJECT(tv_slot
, h
);
3578 DUK_HOBJECT_INCREF(thr
, h
); /* no side effects */
3579 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3580 thr
->valstack_top
++;
3582 /* object is now reachable */
3584 if (prototype_bidx
>= 0) {
3585 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, h
, thr
->builtins
[prototype_bidx
]);
3587 DUK_ASSERT(prototype_bidx
== -1);
3588 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
) == NULL
);
3594 DUK_INTERNAL duk_idx_t
duk_push_object_helper_proto(duk_context
*ctx
, duk_uint_t hobject_flags_and_class
, duk_hobject
*proto
) {
3595 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3599 DUK_ASSERT_CTX_VALID(ctx
);
3601 ret
= duk_push_object_helper(ctx
, hobject_flags_and_class
, -1);
3602 h
= duk_get_hobject(ctx
, -1);
3603 DUK_ASSERT(h
!= NULL
);
3604 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
) == NULL
);
3605 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, h
, proto
);
3609 DUK_EXTERNAL duk_idx_t
duk_push_object(duk_context
*ctx
) {
3610 DUK_ASSERT_CTX_VALID(ctx
);
3612 return duk_push_object_helper(ctx
,
3613 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3614 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT
),
3615 DUK_BIDX_OBJECT_PROTOTYPE
);
3618 DUK_EXTERNAL duk_idx_t
duk_push_array(duk_context
*ctx
) {
3619 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3623 DUK_ASSERT_CTX_VALID(ctx
);
3625 ret
= duk_push_object_helper(ctx
,
3626 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3627 DUK_HOBJECT_FLAG_ARRAY_PART
|
3628 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY
),
3629 DUK_BIDX_ARRAY_PROTOTYPE
);
3631 obj
= duk_require_hobject(ctx
, ret
);
3634 * An array must have a 'length' property (E5 Section 15.4.5.2).
3635 * The special array behavior flag must only be enabled once the
3636 * length property has been added.
3638 * The internal property must be a number (and preferably a
3639 * fastint if fastint support is enabled).
3642 duk_push_int(ctx
, 0);
3643 #if defined(DUK_USE_FASTINT)
3644 DUK_ASSERT(DUK_TVAL_IS_FASTINT(duk_require_tval(ctx
, -1)));
3647 duk_hobject_define_property_internal(thr
,
3649 DUK_HTHREAD_STRING_LENGTH(thr
),
3650 DUK_PROPDESC_FLAGS_W
);
3651 DUK_HOBJECT_SET_EXOTIC_ARRAY(obj
);
3656 DUK_EXTERNAL duk_idx_t
duk_push_thread_raw(duk_context
*ctx
, duk_uint_t flags
) {
3657 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3662 DUK_ASSERT_CTX_VALID(ctx
);
3664 /* check stack first */
3665 if (thr
->valstack_top
>= thr
->valstack_end
) {
3666 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3669 obj
= duk_hthread_alloc(thr
->heap
,
3670 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3671 DUK_HOBJECT_FLAG_THREAD
|
3672 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD
));
3674 DUK_ERROR_ALLOC_DEFMSG(thr
);
3676 obj
->state
= DUK_HTHREAD_STATE_INACTIVE
;
3677 #if defined(DUK_USE_ROM_STRINGS)
3678 /* Nothing to initialize, strs[] is in ROM. */
3680 #if defined(DUK_USE_HEAPPTR16)
3681 obj
->strs16
= thr
->strs16
;
3683 obj
->strs
= thr
->strs
;
3686 DUK_DDD(DUK_DDDPRINT("created thread object with flags: 0x%08lx", (unsigned long) obj
->obj
.hdr
.h_flags
));
3688 /* make the new thread reachable */
3689 tv_slot
= thr
->valstack_top
;
3690 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3691 DUK_HTHREAD_INCREF(thr
, obj
);
3692 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3693 thr
->valstack_top
++;
3695 /* important to do this *after* pushing, to make the thread reachable for gc */
3696 if (!duk_hthread_init_stacks(thr
->heap
, obj
)) {
3697 DUK_ERROR_ALLOC_DEFMSG(thr
);
3700 /* initialize built-ins - either by copying or creating new ones */
3701 if (flags
& DUK_THREAD_NEW_GLOBAL_ENV
) {
3702 duk_hthread_create_builtin_objects(obj
);
3704 duk_hthread_copy_builtin_objects(thr
, obj
);
3707 /* default prototype (Note: 'obj' must be reachable) */
3708 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, obj
->builtins
[DUK_BIDX_THREAD_PROTOTYPE
]);
3710 /* Initial stack size satisfies the stack spare constraints so there
3711 * is no need to require stack here.
3713 DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE
>=
3714 DUK_VALSTACK_API_ENTRY_MINIMUM
+ DUK_VALSTACK_INTERNAL_EXTRA
);
3719 DUK_INTERNAL duk_idx_t
duk_push_compiledfunction(duk_context
*ctx
) {
3720 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3721 duk_hcompiledfunction
*obj
;
3725 DUK_ASSERT_CTX_VALID(ctx
);
3727 /* check stack first */
3728 if (thr
->valstack_top
>= thr
->valstack_end
) {
3729 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3732 /* Template functions are not strictly constructable (they don't
3733 * have a "prototype" property for instance), so leave the
3734 * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here.
3737 obj
= duk_hcompiledfunction_alloc(thr
->heap
,
3738 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3739 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
|
3740 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
));
3742 DUK_ERROR_ALLOC_DEFMSG(thr
);
3745 DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj
->obj
.hdr
.h_flags
));
3747 tv_slot
= thr
->valstack_top
;
3748 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3749 DUK_HOBJECT_INCREF(thr
, obj
);
3750 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3751 thr
->valstack_top
++;
3753 /* default prototype (Note: 'obj' must be reachable) */
3754 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, thr
->builtins
[DUK_BIDX_FUNCTION_PROTOTYPE
]);
3759 DUK_LOCAL duk_idx_t
duk__push_c_function_raw(duk_context
*ctx
, duk_c_function func
, duk_idx_t nargs
, duk_uint_t flags
) {
3760 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3761 duk_hnativefunction
*obj
;
3764 duk_int16_t func_nargs
;
3766 DUK_ASSERT_CTX_VALID(ctx
);
3768 /* check stack first */
3769 if (thr
->valstack_top
>= thr
->valstack_end
) {
3770 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3775 if (nargs
>= 0 && nargs
< DUK_HNATIVEFUNCTION_NARGS_MAX
) {
3776 func_nargs
= (duk_int16_t
) nargs
;
3777 } else if (nargs
== DUK_VARARGS
) {
3778 func_nargs
= DUK_HNATIVEFUNCTION_NARGS_VARARGS
;
3783 obj
= duk_hnativefunction_alloc(thr
->heap
, flags
);
3785 DUK_ERROR_ALLOC_DEFMSG(thr
);
3789 obj
->nargs
= func_nargs
;
3791 DUK_DDD(DUK_DDDPRINT("created native function object with flags: 0x%08lx, nargs=%ld",
3792 (unsigned long) obj
->obj
.hdr
.h_flags
, (long) obj
->nargs
));
3794 tv_slot
= thr
->valstack_top
;
3795 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3796 DUK_HOBJECT_INCREF(thr
, obj
);
3797 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3798 thr
->valstack_top
++;
3800 /* default prototype (Note: 'obj' must be reachable) */
3801 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, thr
->builtins
[DUK_BIDX_FUNCTION_PROTOTYPE
]);
3806 DUK_ERROR_API(thr
, DUK_STR_INVALID_CALL_ARGS
);
3807 return 0; /* not reached */
3810 DUK_EXTERNAL duk_idx_t
duk_push_c_function(duk_context
*ctx
, duk_c_function func
, duk_int_t nargs
) {
3813 DUK_ASSERT_CTX_VALID(ctx
);
3815 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
3816 DUK_HOBJECT_FLAG_CONSTRUCTABLE
|
3817 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
3818 DUK_HOBJECT_FLAG_NEWENV
|
3819 DUK_HOBJECT_FLAG_STRICT
|
3820 DUK_HOBJECT_FLAG_NOTAIL
|
3821 DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC
|
3822 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
3824 return duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
3827 DUK_INTERNAL
void duk_push_c_function_noexotic(duk_context
*ctx
, duk_c_function func
, duk_int_t nargs
) {
3830 DUK_ASSERT_CTX_VALID(ctx
);
3832 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
3833 DUK_HOBJECT_FLAG_CONSTRUCTABLE
|
3834 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
3835 DUK_HOBJECT_FLAG_NEWENV
|
3836 DUK_HOBJECT_FLAG_STRICT
|
3837 DUK_HOBJECT_FLAG_NOTAIL
|
3838 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
3840 (void) duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
3843 DUK_INTERNAL
void duk_push_c_function_noconstruct_noexotic(duk_context
*ctx
, duk_c_function func
, duk_int_t nargs
) {
3846 DUK_ASSERT_CTX_VALID(ctx
);
3848 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
3849 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
3850 DUK_HOBJECT_FLAG_NEWENV
|
3851 DUK_HOBJECT_FLAG_STRICT
|
3852 DUK_HOBJECT_FLAG_NOTAIL
|
3853 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
3855 (void) duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
3858 DUK_EXTERNAL duk_idx_t
duk_push_c_lightfunc(duk_context
*ctx
, duk_c_function func
, duk_idx_t nargs
, duk_idx_t length
, duk_int_t magic
) {
3859 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3861 duk_small_uint_t lf_flags
;
3863 DUK_ASSERT_CTX_VALID(ctx
);
3865 /* check stack first */
3866 if (thr
->valstack_top
>= thr
->valstack_end
) {
3867 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3870 if (nargs
>= DUK_LFUNC_NARGS_MIN
&& nargs
<= DUK_LFUNC_NARGS_MAX
) {
3872 } else if (nargs
== DUK_VARARGS
) {
3873 nargs
= DUK_LFUNC_NARGS_VARARGS
;
3877 if (!(length
>= DUK_LFUNC_LENGTH_MIN
&& length
<= DUK_LFUNC_LENGTH_MAX
)) {
3880 if (!(magic
>= DUK_LFUNC_MAGIC_MIN
&& magic
<= DUK_LFUNC_MAGIC_MAX
)) {
3884 lf_flags
= DUK_LFUNC_FLAGS_PACK(magic
, length
, nargs
);
3885 DUK_TVAL_SET_LIGHTFUNC(&tv_tmp
, func
, lf_flags
);
3886 duk_push_tval(ctx
, &tv_tmp
); /* XXX: direct valstack write */
3887 DUK_ASSERT(thr
->valstack_top
!= thr
->valstack_bottom
);
3888 return ((duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
)) - 1;
3891 DUK_ERROR_API(thr
, DUK_STR_INVALID_CALL_ARGS
);
3892 return 0; /* not reached */
3895 DUK_INTERNAL duk_hbufferobject
*duk_push_bufferobject_raw(duk_context
*ctx
, duk_uint_t hobject_flags_and_class
, duk_small_int_t prototype_bidx
) {
3896 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3897 duk_hbufferobject
*obj
;
3900 DUK_ASSERT(ctx
!= NULL
);
3901 DUK_ASSERT(prototype_bidx
>= 0);
3903 /* check stack first */
3904 if (thr
->valstack_top
>= thr
->valstack_end
) {
3905 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3908 obj
= duk_hbufferobject_alloc(thr
->heap
, hobject_flags_and_class
);
3910 DUK_ERROR_ALLOC_DEFMSG(thr
);
3913 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, thr
->builtins
[prototype_bidx
]);
3914 DUK_ASSERT_HBUFFEROBJECT_VALID(obj
);
3916 tv_slot
= thr
->valstack_top
;
3917 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3918 DUK_HOBJECT_INCREF(thr
, obj
);
3919 thr
->valstack_top
++;
3924 /* XXX: There's quite a bit of overlap with buffer creation handling in
3925 * duk_bi_buffer.c. Look for overlap and refactor.
3927 #define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,isview) \
3928 (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (isview))
3930 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
3931 static const duk_uint32_t duk__bufobj_flags_lookup
[] = {
3932 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER
, DUK_BIDX_BUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0), /* DUK_BUFOBJ_DUKTAPE_BUFFER */
3933 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER
, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0), /* DUK_BUFOBJ_NODEJS_BUFFER */
3934 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER
, DUK_BIDX_ARRAYBUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */
3935 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW
, DUK_BIDX_DATAVIEW_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 1), /* DUK_BUFOBJ_DATAVIEW */
3936 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY
, DUK_BIDX_INT8ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_INT8
, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */
3937 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY
, DUK_BIDX_UINT8ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */
3938 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY
, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */
3939 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY
, DUK_BIDX_INT16ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_INT16
, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */
3940 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY
, DUK_BIDX_UINT16ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT16
, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */
3941 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY
, DUK_BIDX_INT32ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_INT32
, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */
3942 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY
, DUK_BIDX_UINT32ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT32
, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */
3943 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY
, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_FLOAT32
, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */
3944 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY
, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_FLOAT64
, 3, 1) /* DUK_BUFOBJ_FLOAT64ARRAY */
3946 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
3947 /* Only allow Duktape.Buffer when support disabled. */
3948 static const duk_uint32_t duk__bufobj_flags_lookup
[] = {
3949 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER
, DUK_BIDX_BUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0) /* DUK_BUFOBJ_DUKTAPE_BUFFER */
3951 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
3952 #undef DUK__PACK_ARGS
3954 DUK_EXTERNAL
void duk_push_buffer_object(duk_context
*ctx
, duk_idx_t idx_buffer
, duk_size_t byte_offset
, duk_size_t byte_length
, duk_uint_t flags
) {
3956 duk_hbufferobject
*h_bufobj
;
3959 duk_uint_t classnum
;
3960 duk_uint_t protobidx
;
3961 duk_uint_t lookupidx
;
3962 duk_uint_t uint_offset
, uint_length
, uint_added
;
3964 DUK_ASSERT_CTX_VALID(ctx
);
3965 thr
= (duk_hthread
*) ctx
;
3968 /* The underlying types for offset/length in duk_hbufferobject is
3969 * duk_uint_t; make sure argument values fit and that offset + length
3972 uint_offset
= (duk_uint_t
) byte_offset
;
3973 uint_length
= (duk_uint_t
) byte_length
;
3974 if (sizeof(duk_size_t
) != sizeof(duk_uint_t
)) {
3975 if ((duk_size_t
) uint_offset
!= byte_offset
|| (duk_size_t
) uint_length
!= byte_length
) {
3979 uint_added
= uint_offset
+ uint_length
;
3980 if (uint_added
< uint_offset
) {
3983 DUK_ASSERT(uint_added
>= uint_offset
&& uint_added
>= uint_length
);
3985 DUK_ASSERT_DISABLE(flags
>= 0); /* flags is unsigned */
3986 lookupidx
= flags
& 0x0f; /* 4 low bits */
3987 if (lookupidx
>= sizeof(duk__bufobj_flags_lookup
) / sizeof(duk_uint32_t
)) {
3990 tmp
= duk__bufobj_flags_lookup
[lookupidx
];
3991 classnum
= tmp
>> 24;
3992 protobidx
= (tmp
>> 16) & 0xff;
3994 h_val
= duk_require_hbuffer(ctx
, idx_buffer
);
3995 DUK_ASSERT(h_val
!= NULL
);
3997 h_bufobj
= duk_push_bufferobject_raw(ctx
,
3998 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3999 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
4000 DUK_HOBJECT_CLASS_AS_FLAGS(classnum
),
4002 DUK_ASSERT(h_bufobj
!= NULL
);
4004 h_bufobj
->buf
= h_val
;
4005 DUK_HBUFFER_INCREF(thr
, h_val
);
4006 h_bufobj
->offset
= uint_offset
;
4007 h_bufobj
->length
= uint_length
;
4008 h_bufobj
->shift
= (tmp
>> 4) & 0x0f;
4009 h_bufobj
->elem_type
= (tmp
>> 8) & 0xff;
4010 h_bufobj
->is_view
= tmp
& 0x0f;
4011 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
4013 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
4014 /* TypedArray views need an automatic ArrayBuffer which must be
4015 * provided as .buffer property of the view. Just create a new
4016 * ArrayBuffer sharing the same underlying buffer.
4018 * The ArrayBuffer offset is always set to zero, so that if one
4019 * accesses the ArrayBuffer at the view's .byteOffset, the value
4020 * matches the view at index 0.
4022 if (flags
& DUK_BUFOBJ_CREATE_ARRBUF
) {
4023 h_bufobj
= duk_push_bufferobject_raw(ctx
,
4024 DUK_HOBJECT_FLAG_EXTENSIBLE
|
4025 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
4026 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER
),
4027 DUK_BIDX_ARRAYBUFFER_PROTOTYPE
);
4029 DUK_ASSERT(h_bufobj
!= NULL
);
4031 h_bufobj
->buf
= h_val
;
4032 DUK_HBUFFER_INCREF(thr
, h_val
);
4033 h_bufobj
->offset
= 0;
4034 h_bufobj
->length
= uint_offset
+ uint_length
; /* Wrap checked above. */
4035 DUK_ASSERT(h_bufobj
->shift
== 0);
4036 h_bufobj
->elem_type
= DUK_HBUFFEROBJECT_ELEM_UINT8
;
4037 DUK_ASSERT(h_bufobj
->is_view
== 0);
4038 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
4040 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LC_BUFFER
, DUK_PROPDESC_FLAGS_NONE
);
4041 duk_compact(ctx
, -1);
4043 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
4048 DUK_ERROR_RANGE(thr
, DUK_STR_INVALID_CALL_ARGS
);
4049 return; /* not reached */
4052 DUK_ERROR_TYPE(thr
, DUK_STR_INVALID_CALL_ARGS
);
4053 return; /* not reached */
4056 DUK_EXTERNAL duk_idx_t
duk_push_error_object_va_raw(duk_context
*ctx
, duk_errcode_t err_code
, const char *filename
, duk_int_t line
, const char *fmt
, va_list ap
) {
4057 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4060 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
4061 duk_bool_t noblame_fileline
;
4064 DUK_ASSERT_CTX_VALID(ctx
);
4065 DUK_ASSERT(thr
!= NULL
);
4066 DUK_UNREF(filename
);
4069 /* Error code also packs a tracedata related flag. */
4070 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
4071 noblame_fileline
= err_code
& DUK_ERRCODE_FLAG_NOBLAME_FILELINE
;
4073 err_code
= err_code
& (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE
);
4075 /* error gets its 'name' from the prototype */
4076 proto
= duk_error_prototype_from_code(thr
, err_code
);
4077 ret
= duk_push_object_helper_proto(ctx
,
4078 DUK_HOBJECT_FLAG_EXTENSIBLE
|
4079 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR
),
4082 /* ... and its 'message' from an instance property */
4084 duk_push_vsprintf(ctx
, fmt
, ap
);
4085 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_MESSAGE
, DUK_PROPDESC_FLAGS_WC
);
4087 /* If no explicit message given, put error code into message field
4088 * (as a number). This is not fully in keeping with the Ecmascript
4089 * error model because messages are supposed to be strings (Error
4090 * constructors use ToString() on their argument). However, it's
4091 * probably more useful than having a separate 'code' property.
4093 duk_push_int(ctx
, err_code
);
4094 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_MESSAGE
, DUK_PROPDESC_FLAGS_WC
);
4097 /* XXX: .code = err_code disabled, not sure if useful */
4099 /* Creation time error augmentation */
4100 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
4101 /* filename may be NULL in which case file/line is not recorded */
4102 duk_err_augment_error_create(thr
, thr
, filename
, line
, noblame_fileline
); /* may throw an error */
4108 DUK_EXTERNAL duk_idx_t
duk_push_error_object_raw(duk_context
*ctx
, duk_errcode_t err_code
, const char *filename
, duk_int_t line
, const char *fmt
, ...) {
4112 DUK_ASSERT_CTX_VALID(ctx
);
4115 ret
= duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4120 #if !defined(DUK_USE_VARIADIC_MACROS)
4121 DUK_EXTERNAL duk_idx_t
duk_push_error_object_stash(duk_context
*ctx
, duk_errcode_t err_code
, const char *fmt
, ...) {
4122 const char *filename
= duk_api_global_filename
;
4123 duk_int_t line
= duk_api_global_line
;
4127 DUK_ASSERT_CTX_VALID(ctx
);
4129 duk_api_global_filename
= NULL
;
4130 duk_api_global_line
= 0;
4132 ret
= duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4136 #endif /* DUK_USE_VARIADIC_MACROS */
4138 DUK_EXTERNAL
void *duk_push_buffer_raw(duk_context
*ctx
, duk_size_t size
, duk_small_uint_t flags
) {
4139 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4144 DUK_ASSERT_CTX_VALID(ctx
);
4146 /* check stack first */
4147 if (thr
->valstack_top
>= thr
->valstack_end
) {
4148 DUK_ERROR_API(thr
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
4151 /* Check for maximum buffer length. */
4152 if (size
> DUK_HBUFFER_MAX_BYTELEN
) {
4153 DUK_ERROR_RANGE(thr
, DUK_STR_BUFFER_TOO_LONG
);
4156 h
= duk_hbuffer_alloc(thr
->heap
, size
, flags
, &buf_data
);
4158 DUK_ERROR_ALLOC_DEFMSG(thr
);
4161 tv_slot
= thr
->valstack_top
;
4162 DUK_TVAL_SET_BUFFER(tv_slot
, h
);
4163 DUK_HBUFFER_INCREF(thr
, h
);
4164 thr
->valstack_top
++;
4166 return (void *) buf_data
;
4169 #if defined(DUK_USE_ASSERTIONS)
4170 DUK_LOCAL
void duk__validate_push_heapptr(duk_context
*ctx
, void *ptr
) {
4174 duk_bool_t found
= 0;
4176 thr
= (duk_hthread
*) ctx
;
4177 h
= (duk_heaphdr
*) ptr
;
4182 DUK_ASSERT(h
!= NULL
);
4183 DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h
));
4185 /* One particular problem case is where an object has been
4186 * queued for finalization but the finalizer hasn't yet been
4189 * Corner case: we're running in a finalizer for object X, and
4190 * user code calls duk_push_heapptr() for X itself. In this
4191 * case X will be in finalize_list, and we can detect the case
4192 * by seeing that X's FINALIZED flag is set (which is done before
4193 * the finalizer starts executing).
4195 for (curr
= thr
->heap
->finalize_list
;
4197 curr
= DUK_HEAPHDR_GET_NEXT(thr
->heap
, curr
)) {
4199 if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr
*) h
)) {
4200 /* Object is currently being finalized. */
4201 DUK_ASSERT(found
== 0); /* Would indicate corrupted lists. */
4209 /* Also check for the refzero_list; must not be there unless it is
4210 * being finalized when duk_push_heapptr() is called.
4212 * Corner case: similar to finalize_list.
4214 #if defined(DUK_USE_REFERENCE_COUNTING)
4215 for (curr
= thr
->heap
->refzero_list
;
4217 curr
= DUK_HEAPHDR_GET_NEXT(thr
->heap
, curr
)) {
4219 if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr
*) h
)) {
4220 /* Object is currently being finalized. */
4221 DUK_ASSERT(found
== 0); /* Would indicate corrupted lists. */
4230 /* If not present in finalize_list or refzero_list, the pointer
4231 * must be either in heap_allocated or the string table.
4233 if (DUK_HEAPHDR_GET_TYPE(h
) == DUK_HTYPE_STRING
) {
4234 /* String table assert check omitted from 1.x branch
4238 for (curr
= thr
->heap
->heap_allocated
;
4240 curr
= DUK_HEAPHDR_GET_NEXT(thr
->heap
, curr
)) {
4242 DUK_ASSERT(found
== 0); /* Would indicate corrupted lists. */
4246 DUK_ASSERT(found
!= 0);
4249 #endif /* DUK_USE_ASSERTIONS */
4251 DUK_EXTERNAL duk_idx_t
duk_push_heapptr(duk_context
*ctx
, void *ptr
) {
4252 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4255 DUK_ASSERT_CTX_VALID(ctx
);
4257 /* Reviving an object using a heap pointer is a dangerous API
4258 * operation: if the application doesn't guarantee that the
4259 * pointer target is always reachable, difficult-to-diagnose
4260 * problems may ensue. Try to validate the 'ptr' argument to
4261 * the extent possible.
4264 #if defined(DUK_USE_ASSERTIONS)
4265 duk__validate_push_heapptr(ctx
, ptr
);
4268 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
4271 goto push_undefined
;
4274 switch ((int) DUK_HEAPHDR_GET_TYPE((duk_heaphdr
*) ptr
)) {
4275 case DUK_HTYPE_STRING
:
4276 duk_push_hstring(ctx
, (duk_hstring
*) ptr
);
4278 case DUK_HTYPE_OBJECT
:
4279 duk_push_hobject(ctx
, (duk_hobject
*) ptr
);
4281 case DUK_HTYPE_BUFFER
:
4282 duk_push_hbuffer(ctx
, (duk_hbuffer
*) ptr
);
4285 goto push_undefined
;
4290 duk_push_undefined(ctx
);
4294 DUK_INTERNAL duk_idx_t
duk_push_object_internal(duk_context
*ctx
) {
4295 return duk_push_object_helper(ctx
,
4296 DUK_HOBJECT_FLAG_EXTENSIBLE
|
4297 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT
),
4298 -1); /* no prototype */
4301 DUK_INTERNAL
void duk_push_hstring(duk_context
*ctx
, duk_hstring
*h
) {
4303 DUK_ASSERT_CTX_VALID(ctx
);
4304 DUK_ASSERT(h
!= NULL
);
4305 DUK_TVAL_SET_STRING(&tv
, h
);
4306 duk_push_tval(ctx
, &tv
);
4309 DUK_INTERNAL
void duk_push_hstring_stridx(duk_context
*ctx
, duk_small_int_t stridx
) {
4310 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4312 DUK_ASSERT(stridx
>= 0 && stridx
< DUK_HEAP_NUM_STRINGS
);
4313 duk_push_hstring(ctx
, DUK_HTHREAD_GET_STRING(thr
, stridx
));
4316 DUK_INTERNAL
void duk_push_hobject(duk_context
*ctx
, duk_hobject
*h
) {
4318 DUK_ASSERT_CTX_VALID(ctx
);
4319 DUK_ASSERT(h
!= NULL
);
4320 DUK_TVAL_SET_OBJECT(&tv
, h
);
4321 duk_push_tval(ctx
, &tv
);
4324 DUK_INTERNAL
void duk_push_hbuffer(duk_context
*ctx
, duk_hbuffer
*h
) {
4326 DUK_ASSERT_CTX_VALID(ctx
);
4327 DUK_ASSERT(h
!= NULL
);
4328 DUK_TVAL_SET_BUFFER(&tv
, h
);
4329 duk_push_tval(ctx
, &tv
);
4332 DUK_INTERNAL
void duk_push_hobject_bidx(duk_context
*ctx
, duk_small_int_t builtin_idx
) {
4333 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4334 DUK_ASSERT_CTX_VALID(ctx
);
4335 DUK_ASSERT(thr
!= NULL
);
4336 DUK_ASSERT(builtin_idx
>= 0 && builtin_idx
< DUK_NUM_BUILTINS
);
4337 DUK_ASSERT(thr
->builtins
[builtin_idx
] != NULL
);
4338 duk_push_hobject(ctx
, thr
->builtins
[builtin_idx
]);
4345 DUK_EXTERNAL
void duk_pop_n(duk_context
*ctx
, duk_idx_t count
) {
4346 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4349 DUK_ASSERT_CTX_VALID(ctx
);
4351 if (DUK_UNLIKELY(count
< 0)) {
4352 DUK_ERROR_API(thr
, DUK_STR_INVALID_COUNT
);
4356 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4357 if (DUK_UNLIKELY((duk_size_t
) (thr
->valstack_top
- thr
->valstack_bottom
) < (duk_size_t
) count
)) {
4358 DUK_ERROR_API(thr
, DUK_STR_POP_TOO_MANY
);
4362 * Must be very careful here, every DECREF may cause reallocation
4366 /* XXX: inlined DECREF macro would be nice here: no NULL check,
4367 * refzero queueing but no refzero algorithm run (= no pointer
4368 * instability), inline code.
4371 /* XXX: optimize loops */
4373 #if defined(DUK_USE_REFERENCE_COUNTING)
4376 tv
= --thr
->valstack_top
; /* tv points to element just below prev top */
4377 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
4378 DUK_TVAL_SET_UNDEFINED_UPDREF(thr
, tv
); /* side effects */
4381 tv
= thr
->valstack_top
;
4385 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
4386 DUK_TVAL_SET_UNDEFINED(tv
);
4388 thr
->valstack_top
= tv
;
4391 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4394 /* Popping one element is called so often that when footprint is not an issue,
4395 * compile a specialized function for it.
4397 #if defined(DUK_USE_PREFER_SIZE)
4398 DUK_EXTERNAL
void duk_pop(duk_context
*ctx
) {
4399 DUK_ASSERT_CTX_VALID(ctx
);
4403 DUK_EXTERNAL
void duk_pop(duk_context
*ctx
) {
4404 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4406 DUK_ASSERT_CTX_VALID(ctx
);
4408 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4409 if (DUK_UNLIKELY(thr
->valstack_top
== thr
->valstack_bottom
)) {
4410 DUK_ERROR_API(thr
, DUK_STR_POP_TOO_MANY
);
4413 tv
= --thr
->valstack_top
; /* tv points to element just below prev top */
4414 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
4415 #ifdef DUK_USE_REFERENCE_COUNTING
4416 DUK_TVAL_SET_UNDEFINED_UPDREF(thr
, tv
); /* side effects */
4418 DUK_TVAL_SET_UNDEFINED(tv
);
4420 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4422 #endif /* !DUK_USE_PREFER_SIZE */
4424 DUK_EXTERNAL
void duk_pop_2(duk_context
*ctx
) {
4425 DUK_ASSERT_CTX_VALID(ctx
);
4429 DUK_EXTERNAL
void duk_pop_3(duk_context
*ctx
) {
4430 DUK_ASSERT_CTX_VALID(ctx
);
4438 DUK_EXTERNAL
void duk_throw(duk_context
*ctx
) {
4439 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4441 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
4442 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4443 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
4445 if (thr
->valstack_top
== thr
->valstack_bottom
) {
4446 DUK_ERROR_API(thr
, DUK_STR_INVALID_CALL_ARGS
);
4449 /* Errors are augmented when they are created, not when they are
4450 * thrown or re-thrown. The current error handler, however, runs
4451 * just before an error is thrown.
4454 /* Sync so that augmentation sees up-to-date activations, NULL
4455 * thr->ptr_curr_pc so that it's not used if side effects occur
4456 * in augmentation or longjmp handling.
4458 duk_hthread_sync_and_null_currpc(thr
);
4460 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
4461 DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval
*) duk_get_tval(ctx
, -1)));
4462 duk_err_augment_error_throw(thr
);
4464 DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval
*) duk_get_tval(ctx
, -1)));
4466 duk_err_setup_heap_ljstate(thr
, DUK_LJ_TYPE_THROW
);
4468 /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't
4469 * need to check that here. If the value is NULL, a panic occurs because
4473 duk_err_longjmp(thr
);
4477 DUK_EXTERNAL
void duk_fatal(duk_context
*ctx
, duk_errcode_t err_code
, const char *err_msg
) {
4478 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4480 DUK_ASSERT_CTX_VALID(ctx
);
4481 DUK_ASSERT(thr
!= NULL
);
4482 DUK_ASSERT(thr
->heap
!= NULL
);
4483 DUK_ASSERT(thr
->heap
->fatal_func
!= NULL
);
4485 DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s",
4486 (long) err_code
, (const char *) err_msg
));
4488 /* fatal_func should be noreturn, but noreturn declarations on function
4489 * pointers has a very spotty support apparently so it's not currently
4492 thr
->heap
->fatal_func(ctx
, err_code
, err_msg
);
4494 DUK_PANIC(DUK_ERR_API_ERROR
, "fatal handler returned");
4497 DUK_EXTERNAL
void duk_error_va_raw(duk_context
*ctx
, duk_errcode_t err_code
, const char *filename
, duk_int_t line
, const char *fmt
, va_list ap
) {
4498 DUK_ASSERT_CTX_VALID(ctx
);
4500 duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4504 DUK_EXTERNAL
void duk_error_raw(duk_context
*ctx
, duk_errcode_t err_code
, const char *filename
, duk_int_t line
, const char *fmt
, ...) {
4507 DUK_ASSERT_CTX_VALID(ctx
);
4510 duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4515 #if !defined(DUK_USE_VARIADIC_MACROS)
4516 DUK_EXTERNAL
void duk_error_stash(duk_context
*ctx
, duk_errcode_t err_code
, const char *fmt
, ...) {
4517 const char *filename
;
4521 DUK_ASSERT_CTX_VALID(ctx
);
4523 filename
= duk_api_global_filename
;
4524 line
= duk_api_global_line
;
4525 duk_api_global_filename
= NULL
;
4526 duk_api_global_line
= 0;
4529 duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4533 #endif /* DUK_USE_VARIADIC_MACROS */
4539 DUK_EXTERNAL duk_bool_t
duk_equals(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
4540 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4541 duk_tval
*tv1
, *tv2
;
4543 DUK_ASSERT_CTX_VALID(ctx
);
4545 tv1
= duk_get_tval(ctx
, index1
);
4546 tv2
= duk_get_tval(ctx
, index2
);
4547 if ((tv1
== NULL
) || (tv2
== NULL
)) {
4551 /* Coercion may be needed, the helper handles that by pushing the
4552 * tagged values to the stack.
4554 return duk_js_equals(thr
, tv1
, tv2
);
4557 DUK_EXTERNAL duk_bool_t
duk_strict_equals(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
4558 duk_tval
*tv1
, *tv2
;
4560 DUK_ASSERT_CTX_VALID(ctx
);
4562 tv1
= duk_get_tval(ctx
, index1
);
4563 tv2
= duk_get_tval(ctx
, index2
);
4564 if ((tv1
== NULL
) || (tv2
== NULL
)) {
4568 /* No coercions or other side effects, so safe */
4569 return duk_js_strict_equals(tv1
, tv2
);
4576 DUK_EXTERNAL duk_bool_t
duk_instanceof(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
4577 duk_tval
*tv1
, *tv2
;
4579 DUK_ASSERT_CTX_VALID(ctx
);
4581 /* Index validation is strict, which differs from duk_equals().
4582 * The strict behavior mimics how instanceof itself works, e.g.
4583 * it is a TypeError if rval is not a -callable- object. It would
4584 * be somewhat inconsistent if rval would be allowed to be
4585 * non-existent without a TypeError.
4587 tv1
= duk_require_tval(ctx
, index1
);
4588 DUK_ASSERT(tv1
!= NULL
);
4589 tv2
= duk_require_tval(ctx
, index2
);
4590 DUK_ASSERT(tv2
!= NULL
);
4592 return duk_js_instanceof((duk_hthread
*) ctx
, tv1
, tv2
);
4599 DUK_INTERNAL
void duk_push_lightfunc_name(duk_context
*ctx
, duk_tval
*tv
) {
4600 duk_c_function func
;
4602 DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv
));
4604 /* Lightfunc name, includes Duktape/C native function pointer, which
4605 * can often be used to locate the function from a symbol table.
4606 * The name also includes the 16-bit duk_tval flags field because it
4607 * includes the magic value. Because a single native function often
4608 * provides different functionality depending on the magic value, it
4609 * seems reasonably to include it in the name.
4611 * On the other hand, a complicated name increases string table
4612 * pressure in low memory environments (but only when function name
4616 func
= DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv
);
4617 duk_push_sprintf(ctx
, "light_");
4618 duk_push_string_funcptr(ctx
, (duk_uint8_t
*) &func
, sizeof(func
));
4619 duk_push_sprintf(ctx
, "_%04x", (unsigned int) DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv
));
4623 DUK_INTERNAL
void duk_push_lightfunc_tostring(duk_context
*ctx
, duk_tval
*tv
) {
4624 DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv
));
4626 duk_push_string(ctx
, "function ");
4627 duk_push_lightfunc_name(ctx
, tv
);
4628 duk_push_string(ctx
, "() {\"light\"}");
4635 * Printing function pointers is non-portable, so we do that by hex printing
4636 * bytes from memory.
4639 DUK_INTERNAL
void duk_push_string_funcptr(duk_context
*ctx
, duk_uint8_t
*ptr
, duk_size_t sz
) {
4640 duk_uint8_t buf
[32 * 2];
4645 DUK_ASSERT(sz
<= 32); /* sanity limit for function pointer size */
4648 #if defined(DUK_USE_INTEGER_LE)
4653 for (i
= 0; i
< sz
; i
++) {
4654 #if defined(DUK_USE_INTEGER_LE)
4659 *p
++ = duk_lc_digits
[t
>> 4];
4660 *p
++ = duk_lc_digits
[t
& 0x0f];
4663 duk_push_lstring(ctx
, (const char *) buf
, sz
* 2);
4666 #if !defined(DUK_USE_PARANOID_ERRORS)
4668 * Push readable string summarizing duk_tval. The operation is side effect
4669 * free and will only throw from internal errors (e.g. out of memory).
4670 * This is used by e.g. property access code to summarize a key/base safely,
4671 * and is not intended to be fast (but small and safe).
4674 #define DUK__READABLE_STRING_MAXCHARS 32
4676 /* String sanitizer which escapes ASCII control characters and a few other
4677 * ASCII characters, passes Unicode as is, and replaces invalid UTF-8 with
4678 * question marks. No errors are thrown for any input string, except in out
4679 * of memory situations.
4681 DUK_LOCAL
void duk__push_hstring_readable_unicode(duk_context
*ctx
, duk_hstring
*h_input
) {
4683 const duk_uint8_t
*p
, *p_start
, *p_end
;
4684 duk_uint8_t buf
[DUK_UNICODE_MAX_XUTF8_LENGTH
* DUK__READABLE_STRING_MAXCHARS
+
4685 2 /*quotes*/ + 3 /*periods*/];
4687 duk_ucodepoint_t cp
;
4688 duk_small_uint_t nchars
;
4690 DUK_ASSERT_CTX_VALID(ctx
);
4691 DUK_ASSERT(h_input
!= NULL
);
4692 thr
= (duk_hthread
*) ctx
;
4694 p_start
= (const duk_uint8_t
*) DUK_HSTRING_GET_DATA(h_input
);
4695 p_end
= p_start
+ DUK_HSTRING_GET_BYTELEN(h_input
);
4700 *q
++ = (duk_uint8_t
) DUK_ASC_SINGLEQUOTE
;
4705 if (nchars
== DUK__READABLE_STRING_MAXCHARS
) {
4706 *q
++ = (duk_uint8_t
) DUK_ASC_PERIOD
;
4707 *q
++ = (duk_uint8_t
) DUK_ASC_PERIOD
;
4708 *q
++ = (duk_uint8_t
) DUK_ASC_PERIOD
;
4711 if (duk_unicode_decode_xutf8(thr
, &p
, p_start
, p_end
, &cp
)) {
4712 if (cp
< 0x20 || cp
== 0x7f || cp
== DUK_ASC_SINGLEQUOTE
|| cp
== DUK_ASC_BACKSLASH
) {
4713 DUK_ASSERT(DUK_UNICODE_MAX_XUTF8_LENGTH
>= 4); /* estimate is valid */
4714 DUK_ASSERT((cp
>> 4) <= 0x0f);
4715 *q
++ = (duk_uint8_t
) DUK_ASC_BACKSLASH
;
4716 *q
++ = (duk_uint8_t
) DUK_ASC_LC_X
;
4717 *q
++ = (duk_uint8_t
) duk_lc_digits
[cp
>> 4];
4718 *q
++ = (duk_uint8_t
) duk_lc_digits
[cp
& 0x0f];
4720 q
+= duk_unicode_encode_xutf8(cp
, q
);
4723 p
++; /* advance manually */
4724 *q
++ = (duk_uint8_t
) DUK_ASC_QUESTION
;
4728 *q
++ = (duk_uint8_t
) DUK_ASC_SINGLEQUOTE
;
4730 duk_push_lstring(ctx
, (const char *) buf
, (duk_size_t
) (q
- buf
));
4733 DUK_INTERNAL
const char *duk_push_string_tval_readable(duk_context
*ctx
, duk_tval
*tv
) {
4736 DUK_ASSERT_CTX_VALID(ctx
);
4737 thr
= (duk_hthread
*) ctx
;
4741 duk_push_string(ctx
, "none");
4743 switch (DUK_TVAL_GET_TAG(tv
)) {
4744 case DUK_TAG_STRING
: {
4745 duk__push_hstring_readable_unicode(ctx
, DUK_TVAL_GET_STRING(tv
));
4748 case DUK_TAG_OBJECT
: {
4749 duk_hobject
*h
= DUK_TVAL_GET_OBJECT(tv
);
4750 DUK_ASSERT(h
!= NULL
);
4751 duk_push_hobject_class_string(ctx
, h
);
4754 case DUK_TAG_BUFFER
: {
4755 /* XXX: Hex encoded, length limited buffer summary here? */
4756 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
4757 DUK_ASSERT(h
!= NULL
);
4758 duk_push_sprintf(ctx
, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h
));
4761 case DUK_TAG_POINTER
: {
4762 /* Surround with parentheses like in JX, ensures NULL pointer
4763 * is distinguishable from null value ("(null)" vs "null").
4765 duk_push_tval(ctx
, tv
);
4766 duk_push_sprintf(ctx
, "(%s)", duk_to_string(ctx
, -1));
4767 duk_remove(ctx
, -2);
4771 duk_push_tval(ctx
, tv
);
4777 return duk_to_string(ctx
, -1);
4780 DUK_INTERNAL
const char *duk_push_string_readable(duk_context
*ctx
, duk_idx_t index
) {
4781 DUK_ASSERT_CTX_VALID(ctx
);
4782 return duk_push_string_tval_readable(ctx
, duk_get_tval(ctx
, index
));
4784 #endif /* !DUK_USE_PARANOID_ERRORS */
4786 #undef DUK__CHECK_SPACE
4787 #undef DUK__PACK_ARGS
4788 #undef DUK__READABLE_STRING_MAXCHARS