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 #if defined(DUK_USE_VALSTACK_UNSAFE)
35 /* Faster but value stack overruns are memory unsafe. */
36 #define DUK__CHECK_SPACE() do { \
37 DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
40 #define DUK__CHECK_SPACE() do { \
41 if (thr->valstack_top >= thr->valstack_end) { \
42 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK); \
47 DUK_LOCAL duk_int_t
duk__api_coerce_d2i(duk_context
*ctx
, duk_idx_t index
, duk_bool_t require
) {
53 thr
= (duk_hthread
*) ctx
;
55 tv
= duk_get_tval(ctx
, index
);
61 * Special cases like NaN and +/- Infinity are handled explicitly
62 * because a plain C coercion from double to int handles these cases
63 * in undesirable ways. For instance, NaN may coerce to INT_MIN
64 * (not zero), and INT_MAX + 1 may coerce to INT_MIN (not INT_MAX).
66 * This double-to-int coercion differs from ToInteger() because it
67 * has a finite range (ToInteger() allows e.g. +/- Infinity). It
68 * also differs from ToInt32() because the INT_MIN/INT_MAX clamping
69 * depends on the size of the int type on the platform. In particular,
70 * on platforms with a 64-bit int type, the full range is allowed.
73 #if defined(DUK_USE_FASTINT)
74 if (DUK_TVAL_IS_FASTINT(tv
)) {
75 duk_int64_t t
= DUK_TVAL_GET_FASTINT(tv
);
76 #if (DUK_INT_MAX <= 0x7fffffffL)
77 /* Clamping only necessary for 32-bit ints. */
78 if (t
< DUK_INT_MIN
) {
80 } else if (t
> DUK_INT_MAX
) {
88 if (DUK_TVAL_IS_NUMBER(tv
)) {
89 d
= DUK_TVAL_GET_NUMBER(tv
);
90 c
= (duk_small_int_t
) DUK_FPCLASSIFY(d
);
91 if (c
== DUK_FP_NAN
) {
93 } else if (d
< (duk_double_t
) DUK_INT_MIN
) {
94 /* covers -Infinity */
96 } else if (d
> (duk_double_t
) DUK_INT_MAX
) {
97 /* covers +Infinity */
100 /* coerce towards zero */
101 return (duk_int_t
) d
;
108 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_NUMBER
);
114 DUK_LOCAL duk_uint_t
duk__api_coerce_d2ui(duk_context
*ctx
, duk_idx_t index
, duk_bool_t require
) {
120 /* Same as above but for unsigned int range. */
122 thr
= (duk_hthread
*) ctx
;
124 tv
= duk_get_tval(ctx
, index
);
126 goto error_notnumber
;
129 #if defined(DUK_USE_FASTINT)
130 if (DUK_TVAL_IS_FASTINT(tv
)) {
131 duk_int64_t t
= DUK_TVAL_GET_FASTINT(tv
);
135 #if (DUK_UINT_MAX <= 0xffffffffUL)
136 /* Clamping only necessary for 32-bit ints. */
137 else if (t
> DUK_UINT_MAX
) {
141 return (duk_uint_t
) t
;
145 if (DUK_TVAL_IS_NUMBER(tv
)) {
146 d
= DUK_TVAL_GET_NUMBER(tv
);
147 c
= (duk_small_int_t
) DUK_FPCLASSIFY(d
);
148 if (c
== DUK_FP_NAN
) {
150 } else if (d
< 0.0) {
151 /* covers -Infinity */
152 return (duk_uint_t
) 0;
153 } else if (d
> (duk_double_t
) DUK_UINT_MAX
) {
154 /* covers +Infinity */
155 return (duk_uint_t
) DUK_UINT_MAX
;
157 /* coerce towards zero */
158 return (duk_uint_t
) d
;
165 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_NUMBER
);
172 * Stack index validation/normalization and getting a stack duk_tval ptr.
174 * These are called by many API entrypoints so the implementations must be
175 * fast and "inlined".
177 * There's some repetition because of this; keep the functions in sync.
180 DUK_EXTERNAL duk_idx_t
duk_normalize_index(duk_context
*ctx
, duk_idx_t index
) {
181 duk_hthread
*thr
= (duk_hthread
*) ctx
;
184 DUK_ASSERT_CTX_VALID(ctx
);
185 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
187 /* Care must be taken to avoid pointer wrapping in the index
188 * validation. For instance, on a 32-bit platform with 8-byte
189 * duk_tval the index 0x20000000UL would wrap the memory space
193 /* Assume value stack sizes (in elements) fits into duk_idx_t. */
194 vs_size
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
195 DUK_ASSERT(vs_size
>= 0);
198 index
= vs_size
+ index
;
199 if (DUK_UNLIKELY(index
< 0)) {
200 /* Also catches index == DUK_INVALID_INDEX: vs_size >= 0
201 * so that vs_size + DUK_INVALID_INDEX cannot underflow
202 * and will always be negative.
204 return DUK_INVALID_INDEX
;
207 /* since index non-negative */
208 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
210 if (DUK_UNLIKELY(index
>= vs_size
)) {
211 return DUK_INVALID_INDEX
;
215 DUK_ASSERT(index
>= 0);
216 DUK_ASSERT(index
< vs_size
);
220 DUK_EXTERNAL duk_idx_t
duk_require_normalize_index(duk_context
*ctx
, duk_idx_t index
) {
221 duk_hthread
*thr
= (duk_hthread
*) ctx
;
224 DUK_ASSERT_CTX_VALID(ctx
);
225 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
227 vs_size
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
228 DUK_ASSERT(vs_size
>= 0);
231 index
= vs_size
+ index
;
232 if (DUK_UNLIKELY(index
< 0)) {
236 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
237 if (DUK_UNLIKELY(index
>= vs_size
)) {
242 DUK_ASSERT(index
>= 0);
243 DUK_ASSERT(index
< vs_size
);
247 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_INDEX
);
248 return 0; /* unreachable */
251 DUK_INTERNAL duk_tval
*duk_get_tval(duk_context
*ctx
, duk_idx_t index
) {
252 duk_hthread
*thr
= (duk_hthread
*) ctx
;
255 DUK_ASSERT_CTX_VALID(ctx
);
256 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
258 vs_size
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
259 DUK_ASSERT(vs_size
>= 0);
262 index
= vs_size
+ index
;
263 if (DUK_UNLIKELY(index
< 0)) {
267 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
268 if (DUK_UNLIKELY(index
>= vs_size
)) {
273 DUK_ASSERT(index
>= 0);
274 DUK_ASSERT(index
< vs_size
);
275 return thr
->valstack_bottom
+ index
;
278 DUK_INTERNAL duk_tval
*duk_require_tval(duk_context
*ctx
, duk_idx_t index
) {
279 duk_hthread
*thr
= (duk_hthread
*) ctx
;
282 DUK_ASSERT_CTX_VALID(ctx
);
283 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
285 vs_size
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
286 DUK_ASSERT(vs_size
>= 0);
289 index
= vs_size
+ index
;
290 if (DUK_UNLIKELY(index
< 0)) {
294 DUK_ASSERT(index
!= DUK_INVALID_INDEX
);
295 if (DUK_UNLIKELY(index
>= vs_size
)) {
300 DUK_ASSERT(index
>= 0);
301 DUK_ASSERT(index
< vs_size
);
302 return thr
->valstack_bottom
+ index
;
305 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_INDEX
);
310 DUK_EXTERNAL duk_bool_t
duk_is_valid_index(duk_context
*ctx
, duk_idx_t index
) {
311 DUK_ASSERT_CTX_VALID(ctx
);
312 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
314 return (duk_normalize_index(ctx
, index
) >= 0);
318 DUK_EXTERNAL
void duk_require_valid_index(duk_context
*ctx
, duk_idx_t index
) {
319 duk_hthread
*thr
= (duk_hthread
*) ctx
;
321 DUK_ASSERT_CTX_VALID(ctx
);
322 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
324 if (duk_normalize_index(ctx
, index
) < 0) {
325 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_INDEX
);
330 * Value stack top handling
333 DUK_EXTERNAL duk_idx_t
duk_get_top(duk_context
*ctx
) {
334 duk_hthread
*thr
= (duk_hthread
*) ctx
;
336 DUK_ASSERT_CTX_VALID(ctx
);
338 return (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
341 /* set stack top within currently allocated range, but don't reallocate */
342 DUK_EXTERNAL
void duk_set_top(duk_context
*ctx
, duk_idx_t index
) {
343 duk_hthread
*thr
= (duk_hthread
*) ctx
;
350 DUK_ASSERT_CTX_VALID(ctx
);
351 DUK_ASSERT(DUK_INVALID_INDEX
< 0);
353 vs_size
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
354 vs_limit
= (duk_idx_t
) (thr
->valstack_end
- thr
->valstack_bottom
);
357 /* Negative indices are always within allocated stack but
358 * must not go below zero index.
360 index
= vs_size
+ index
;
362 /* Also catches index == DUK_INVALID_INDEX. */
366 /* Positive index can be higher than valstack top but must
367 * not go above allocated stack (equality is OK).
369 if (index
> vs_limit
) {
373 DUK_ASSERT(index
>= 0);
374 DUK_ASSERT(index
<= vs_limit
);
376 if (index
>= vs_size
) {
377 /* Stack size increases or stays the same. Fill the new
378 * entries (if any) with undefined. No pointer stability
379 * issues here so we can use a running pointer.
382 tv
= thr
->valstack_top
;
383 count
= index
- vs_size
;
384 DUK_ASSERT(count
>= 0);
386 /* no need to decref previous or new value */
388 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED_UNUSED(tv
));
389 DUK_TVAL_SET_UNDEFINED_ACTUAL(tv
);
392 thr
->valstack_top
= tv
;
394 /* Stack size decreases, DECREF entries which are above the
395 * new top. Each DECREF potentially invalidates valstack
396 * pointers, so don't hold on to pointers. The valstack top
397 * must also be updated on every loop in case a GC is triggered.
400 /* XXX: Here it would be useful to have a DECREF macro which
401 * doesn't need a NULL check, and does refzero queueing without
402 * running the refzero algorithm. There would be no pointer
403 * instability in this case, and code could be inlined. After
404 * the loop, one call to refzero would be needed.
407 count
= vs_size
- index
;
408 DUK_ASSERT(count
> 0);
412 tv
= --thr
->valstack_top
; /* tv -> value just before prev top value */
413 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
414 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
415 DUK_TVAL_SET_UNDEFINED_UNUSED(tv
);
416 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
418 /* XXX: fast primitive to set a bunch of values to UNDEFINED_UNUSED */
425 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_INDEX
);
428 DUK_EXTERNAL duk_idx_t
duk_get_top_index(duk_context
*ctx
) {
429 duk_hthread
*thr
= (duk_hthread
*) ctx
;
432 DUK_ASSERT_CTX_VALID(ctx
);
434 ret
= ((duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
)) - 1;
435 if (DUK_UNLIKELY(ret
< 0)) {
436 /* Return invalid index; if caller uses this without checking
437 * in another API call, the index won't map to a valid stack
440 return DUK_INVALID_INDEX
;
445 DUK_EXTERNAL duk_idx_t
duk_require_top_index(duk_context
*ctx
) {
446 duk_hthread
*thr
= (duk_hthread
*) ctx
;
449 DUK_ASSERT_CTX_VALID(ctx
);
451 ret
= ((duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
)) - 1;
452 if (DUK_UNLIKELY(ret
< 0)) {
453 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_INDEX
);
459 * Value stack resizing.
461 * This resizing happens above the current "top": the value stack can be
462 * grown or shrunk, but the "top" is not affected. The value stack cannot
463 * be resized to a size below the current "top".
465 * The low level reallocation primitive must carefully recompute all value
466 * stack pointers, and must also work if ALL pointers are NULL. The resize
467 * is quite tricky because the valstack realloc may cause a mark-and-sweep,
468 * which may run finalizers. Running finalizers may resize the valstack
469 * recursively (the same value stack we're working on). So, after realloc
470 * returns, we know that the valstack "top" should still be the same (there
471 * should not be live values above the "top"), but its underlying size and
472 * pointer may have changed.
475 /* XXX: perhaps refactor this to allow caller to specify some parameters, or
476 * at least a 'compact' flag which skips any spare or round-up .. useful for
480 DUK_LOCAL duk_bool_t
duk__resize_valstack(duk_context
*ctx
, duk_size_t new_size
) {
481 duk_hthread
*thr
= (duk_hthread
*) ctx
;
482 duk_ptrdiff_t old_bottom_offset
;
483 duk_ptrdiff_t old_top_offset
;
484 duk_ptrdiff_t old_end_offset_post
;
486 duk_ptrdiff_t old_end_offset_pre
;
487 duk_tval
*old_valstack_pre
;
488 duk_tval
*old_valstack_post
;
490 duk_tval
*new_valstack
;
492 duk_size_t new_alloc_size
;
494 DUK_ASSERT_CTX_VALID(ctx
);
495 DUK_ASSERT(thr
!= NULL
);
496 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
497 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
498 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
499 DUK_ASSERT((duk_size_t
) (thr
->valstack_top
- thr
->valstack
) <= new_size
); /* can't resize below 'top' */
500 DUK_ASSERT(new_size
<= thr
->valstack_max
); /* valstack limit caller has check, prevents wrapping */
501 DUK_ASSERT(new_size
<= DUK_SIZE_MAX
/ sizeof(duk_tval
)); /* specific assert for wrapping */
503 /* get pointer offsets for tweaking below */
504 old_bottom_offset
= (((duk_uint8_t
*) thr
->valstack_bottom
) - ((duk_uint8_t
*) thr
->valstack
));
505 old_top_offset
= (((duk_uint8_t
*) thr
->valstack_top
) - ((duk_uint8_t
*) thr
->valstack
));
507 old_end_offset_pre
= (((duk_uint8_t
*) thr
->valstack_end
) - ((duk_uint8_t
*) thr
->valstack
)); /* not very useful, used for debugging */
508 old_valstack_pre
= thr
->valstack
;
511 /* Allocate a new valstack.
513 * Note: cannot use a plain DUK_REALLOC() because a mark-and-sweep may
514 * invalidate the original thr->valstack base pointer inside the realloc
515 * process. See doc/memory-management.rst.
518 new_alloc_size
= sizeof(duk_tval
) * new_size
;
519 new_valstack
= (duk_tval
*) DUK_REALLOC_INDIRECT(thr
->heap
, duk_hthread_get_valstack_ptr
, (void *) thr
, new_alloc_size
);
521 /* Because new_size != 0, if condition doesn't need to be
522 * (new_valstack != NULL || new_size == 0).
524 DUK_ASSERT(new_size
!= 0);
525 DUK_D(DUK_DPRINT("failed to resize valstack to %lu entries (%lu bytes)",
526 (unsigned long) new_size
, (unsigned long) new_alloc_size
));
530 /* Note: the realloc may have triggered a mark-and-sweep which may
531 * have resized our valstack internally. However, the mark-and-sweep
532 * MUST NOT leave the stack bottom/top in a different state. Particular
533 * assumptions and facts:
535 * - The thr->valstack pointer may be different after realloc,
536 * and the offset between thr->valstack_end <-> thr->valstack
538 * - The offset between thr->valstack_bottom <-> thr->valstack
539 * and thr->valstack_top <-> thr->valstack MUST NOT have changed,
540 * because mark-and-sweep must adhere to a strict stack policy.
541 * In other words, logical bottom and top MUST NOT have changed.
542 * - All values above the top are unreachable but are initialized
543 * to UNDEFINED_UNUSED, up to the post-realloc valstack_end.
544 * - 'old_end_offset' must be computed after realloc to be correct.
547 DUK_ASSERT((((duk_uint8_t
*) thr
->valstack_bottom
) - ((duk_uint8_t
*) thr
->valstack
)) == old_bottom_offset
);
548 DUK_ASSERT((((duk_uint8_t
*) thr
->valstack_top
) - ((duk_uint8_t
*) thr
->valstack
)) == old_top_offset
);
550 /* success, fixup pointers */
551 old_end_offset_post
= (((duk_uint8_t
*) thr
->valstack_end
) - ((duk_uint8_t
*) thr
->valstack
)); /* must be computed after realloc */
553 old_valstack_post
= thr
->valstack
;
555 thr
->valstack
= new_valstack
;
556 thr
->valstack_end
= new_valstack
+ new_size
;
557 thr
->valstack_bottom
= (duk_tval
*) (void *) ((duk_uint8_t
*) new_valstack
+ old_bottom_offset
);
558 thr
->valstack_top
= (duk_tval
*) (void *) ((duk_uint8_t
*) new_valstack
+ old_top_offset
);
560 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
561 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
562 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
564 /* useful for debugging */
566 if (old_end_offset_pre
!= old_end_offset_post
) {
567 DUK_D(DUK_DPRINT("valstack was resized during valstack_resize(), probably by mark-and-sweep; "
568 "end offset changed: %lu -> %lu",
569 (unsigned long) old_end_offset_pre
,
570 (unsigned long) old_end_offset_post
));
572 if (old_valstack_pre
!= old_valstack_post
) {
573 DUK_D(DUK_DPRINT("valstack pointer changed during valstack_resize(), probably by mark-and-sweep: %p -> %p",
574 (void *) old_valstack_pre
,
575 (void *) old_valstack_post
));
579 DUK_DD(DUK_DDPRINT("resized valstack to %lu elements (%lu bytes), bottom=%ld, top=%ld, "
580 "new pointers: start=%p end=%p bottom=%p top=%p",
581 (unsigned long) new_size
, (unsigned long) new_alloc_size
,
582 (long) (thr
->valstack_bottom
- thr
->valstack
),
583 (long) (thr
->valstack_top
- thr
->valstack
),
584 (void *) thr
->valstack
, (void *) thr
->valstack_end
,
585 (void *) thr
->valstack_bottom
, (void *) thr
->valstack_top
));
587 /* init newly allocated slots (only) */
588 p
= (duk_tval
*) (void *) ((duk_uint8_t
*) thr
->valstack
+ old_end_offset_post
);
589 while (p
< thr
->valstack_end
) {
590 /* never executed if new size is smaller */
591 DUK_TVAL_SET_UNDEFINED_UNUSED(p
);
595 /* assertion check: we maintain elements above top in known state */
596 #ifdef DUK_USE_ASSERTIONS
597 p
= thr
->valstack_top
;
598 while (p
< thr
->valstack_end
) {
599 /* everything above old valstack top should be preinitialized now */
600 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED_UNUSED(p
));
608 duk_bool_t
duk_valstack_resize_raw(duk_context
*ctx
,
609 duk_size_t min_new_size
,
610 duk_small_uint_t flags
) {
611 duk_hthread
*thr
= (duk_hthread
*) ctx
;
614 duk_bool_t is_shrink
= 0;
615 duk_small_uint_t shrink_flag
= (flags
& DUK_VSRESIZE_FLAG_SHRINK
);
616 duk_small_uint_t compact_flag
= (flags
& DUK_VSRESIZE_FLAG_COMPACT
);
617 duk_small_uint_t throw_flag
= (flags
& DUK_VSRESIZE_FLAG_THROW
);
619 DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, "
620 "curr_bottom=%ld, shrink=%d, compact=%d, throw=%d",
621 (unsigned long) min_new_size
,
622 (long) (thr
->valstack_end
- thr
->valstack
),
623 (long) (thr
->valstack_top
- thr
->valstack
),
624 (long) (thr
->valstack_bottom
- thr
->valstack
),
625 (int) shrink_flag
, (int) compact_flag
, (int) throw_flag
));
627 DUK_ASSERT_CTX_VALID(ctx
);
628 DUK_ASSERT(thr
!= NULL
);
629 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
630 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
631 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
633 old_size
= (duk_size_t
) (thr
->valstack_end
- thr
->valstack
);
635 if (min_new_size
<= old_size
) {
638 old_size
- min_new_size
< DUK_VALSTACK_SHRINK_THRESHOLD
) {
639 DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack"));
644 new_size
= min_new_size
;
647 /* shrink case; leave some spare */
648 new_size
+= DUK_VALSTACK_SHRINK_SPARE
;
651 /* round up roughly to next 'grow step' */
652 new_size
= (new_size
/ DUK_VALSTACK_GROW_STEP
+ 1) * DUK_VALSTACK_GROW_STEP
;
655 DUK_DD(DUK_DDPRINT("want to %s valstack: %lu -> %lu elements (min_new_size %lu)",
656 (const char *) (new_size
> old_size
? "grow" : "shrink"),
657 (unsigned long) old_size
, (unsigned long) new_size
,
658 (unsigned long) min_new_size
));
660 if (new_size
> thr
->valstack_max
) {
661 /* Note: may be triggered even if minimal new_size would not reach the limit,
662 * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account).
665 DUK_ERROR(thr
, DUK_ERR_RANGE_ERROR
, DUK_STR_VALSTACK_LIMIT
);
672 * When resizing the valstack, a mark-and-sweep may be triggered for
673 * the allocation of the new valstack. If the mark-and-sweep needs
674 * to use our thread for something, it may cause *the same valstack*
675 * to be resized recursively. This happens e.g. when mark-and-sweep
676 * finalizers are called. This is taken into account carefully in
677 * duk__resize_valstack().
679 * 'new_size' is known to be <= valstack_max, which ensures that
680 * size_t and pointer arithmetic won't wrap in duk__resize_valstack().
683 if (!duk__resize_valstack(ctx
, new_size
)) {
685 DUK_DD(DUK_DDPRINT("valstack resize failed, but is a shrink, ignore"));
689 DUK_DD(DUK_DDPRINT("valstack resize failed"));
692 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_FAILED_TO_EXTEND_VALSTACK
);
698 DUK_DDD(DUK_DDDPRINT("valstack resize successful"));
702 DUK_EXTERNAL duk_bool_t
duk_check_stack(duk_context
*ctx
, duk_idx_t extra
) {
703 duk_hthread
*thr
= (duk_hthread
*) ctx
;
704 duk_size_t min_new_size
;
706 DUK_ASSERT_CTX_VALID(ctx
);
707 DUK_ASSERT(thr
!= NULL
);
709 if (DUK_UNLIKELY(extra
< 0)) {
710 /* Clamping to zero makes the API more robust to calling code
711 * calculation errors.
716 min_new_size
= (thr
->valstack_top
- thr
->valstack
) + extra
+ DUK_VALSTACK_INTERNAL_EXTRA
;
717 return duk_valstack_resize_raw(ctx
,
718 min_new_size
, /* min_new_size */
719 0 /* no shrink */ | /* flags */
724 DUK_EXTERNAL
void duk_require_stack(duk_context
*ctx
, duk_idx_t extra
) {
725 duk_hthread
*thr
= (duk_hthread
*) ctx
;
726 duk_size_t min_new_size
;
728 DUK_ASSERT_CTX_VALID(ctx
);
729 DUK_ASSERT(thr
!= NULL
);
731 if (DUK_UNLIKELY(extra
< 0)) {
732 /* Clamping to zero makes the API more robust to calling code
733 * calculation errors.
738 min_new_size
= (thr
->valstack_top
- thr
->valstack
) + extra
+ DUK_VALSTACK_INTERNAL_EXTRA
;
739 (void) duk_valstack_resize_raw(ctx
,
740 min_new_size
, /* min_new_size */
741 0 /* no shrink */ | /* flags */
743 DUK_VSRESIZE_FLAG_THROW
);
746 DUK_EXTERNAL duk_bool_t
duk_check_stack_top(duk_context
*ctx
, duk_idx_t top
) {
747 duk_size_t min_new_size
;
749 DUK_ASSERT_CTX_VALID(ctx
);
751 if (DUK_UNLIKELY(top
< 0)) {
752 /* Clamping to zero makes the API more robust to calling code
753 * calculation errors.
758 min_new_size
= top
+ DUK_VALSTACK_INTERNAL_EXTRA
;
759 return duk_valstack_resize_raw(ctx
,
760 min_new_size
, /* min_new_size */
761 0 /* no shrink */ | /* flags */
766 DUK_EXTERNAL
void duk_require_stack_top(duk_context
*ctx
, duk_idx_t top
) {
767 duk_size_t min_new_size
;
769 DUK_ASSERT_CTX_VALID(ctx
);
771 if (DUK_UNLIKELY(top
< 0)) {
772 /* Clamping to zero makes the API more robust to calling code
773 * calculation errors.
778 min_new_size
= top
+ DUK_VALSTACK_INTERNAL_EXTRA
;
779 (void) duk_valstack_resize_raw(ctx
,
780 min_new_size
, /* min_new_size */
781 0 /* no shrink */ | /* flags */
783 DUK_VSRESIZE_FLAG_THROW
);
787 * Basic stack manipulation: swap, dup, insert, replace, etc
790 DUK_EXTERNAL
void duk_swap(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
795 DUK_ASSERT_CTX_VALID(ctx
);
797 tv1
= duk_require_tval(ctx
, index1
);
798 DUK_ASSERT(tv1
!= NULL
);
799 tv2
= duk_require_tval(ctx
, index2
);
800 DUK_ASSERT(tv2
!= NULL
);
802 /* If tv1==tv2 this is a NOP, no check is needed */
803 DUK_TVAL_SET_TVAL(&tv_tmp
, tv1
);
804 DUK_TVAL_SET_TVAL(tv1
, tv2
);
805 DUK_TVAL_SET_TVAL(tv2
, &tv_tmp
);
808 DUK_EXTERNAL
void duk_swap_top(duk_context
*ctx
, duk_idx_t index
) {
809 DUK_ASSERT_CTX_VALID(ctx
);
811 duk_swap(ctx
, index
, -1);
814 DUK_EXTERNAL
void duk_dup(duk_context
*ctx
, duk_idx_t from_index
) {
819 DUK_ASSERT_CTX_VALID(ctx
);
820 thr
= (duk_hthread
*) ctx
;
823 tv_from
= duk_require_tval(ctx
, from_index
);
824 tv_to
= thr
->valstack_top
++;
825 DUK_ASSERT(tv_from
!= NULL
);
826 DUK_ASSERT(tv_to
!= NULL
);
827 DUK_TVAL_SET_TVAL(tv_to
, tv_from
);
828 DUK_TVAL_INCREF(thr
, tv_to
); /* no side effects */
831 DUK_EXTERNAL
void duk_dup_top(duk_context
*ctx
) {
836 DUK_ASSERT_CTX_VALID(ctx
);
837 thr
= (duk_hthread
*) ctx
;
840 if (thr
->valstack_top
- thr
->valstack_bottom
<= 0) {
841 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_INDEX
);
843 tv_from
= thr
->valstack_top
- 1;
844 tv_to
= thr
->valstack_top
++;
845 DUK_ASSERT(tv_from
!= NULL
);
846 DUK_ASSERT(tv_to
!= NULL
);
847 DUK_TVAL_SET_TVAL(tv_to
, tv_from
);
848 DUK_TVAL_INCREF(thr
, tv_to
); /* no side effects */
851 DUK_EXTERNAL
void duk_insert(duk_context
*ctx
, duk_idx_t to_index
) {
857 DUK_ASSERT_CTX_VALID(ctx
);
859 p
= duk_require_tval(ctx
, to_index
);
860 DUK_ASSERT(p
!= NULL
);
861 q
= duk_require_tval(ctx
, -1);
862 DUK_ASSERT(q
!= NULL
);
868 * [ ... | p | x | x | q ]
869 * => [ ... | q | p | x | x ]
872 nbytes
= (duk_size_t
) (((duk_uint8_t
*) q
) - ((duk_uint8_t
*) p
)); /* Note: 'q' is top-1 */
874 DUK_DDD(DUK_DDDPRINT("duk_insert: to_index=%ld, p=%p, q=%p, nbytes=%lu",
875 (long) to_index
, (void *) p
, (void *) q
, (unsigned long) nbytes
));
877 /* No net refcount changes. */
880 DUK_TVAL_SET_TVAL(&tv_tmp
, q
);
881 DUK_ASSERT(nbytes
> 0);
882 DUK_MEMMOVE((void *) (p
+ 1), (void *) p
, nbytes
);
883 DUK_TVAL_SET_TVAL(p
, &tv_tmp
);
885 /* nop: insert top to top */
886 DUK_ASSERT(nbytes
== 0);
891 DUK_EXTERNAL
void duk_replace(duk_context
*ctx
, duk_idx_t to_index
) {
892 duk_hthread
*thr
= (duk_hthread
*) ctx
;
897 DUK_ASSERT_CTX_VALID(ctx
);
899 tv1
= duk_require_tval(ctx
, -1);
900 DUK_ASSERT(tv1
!= NULL
);
901 tv2
= duk_require_tval(ctx
, to_index
);
902 DUK_ASSERT(tv2
!= NULL
);
904 /* For tv1 == tv2, both pointing to stack top, the end result
905 * is same as duk_pop(ctx).
908 DUK_TVAL_SET_TVAL(&tv_tmp
, tv2
);
909 DUK_TVAL_SET_TVAL(tv2
, tv1
);
910 DUK_TVAL_SET_UNDEFINED_UNUSED(tv1
);
912 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
915 DUK_EXTERNAL
void duk_copy(duk_context
*ctx
, duk_idx_t from_index
, duk_idx_t to_index
) {
916 duk_hthread
*thr
= (duk_hthread
*) ctx
;
921 DUK_ASSERT_CTX_VALID(ctx
);
922 DUK_UNREF(thr
); /* w/o refcounting */
924 tv1
= duk_require_tval(ctx
, from_index
);
925 DUK_ASSERT(tv1
!= NULL
);
926 tv2
= duk_require_tval(ctx
, to_index
);
927 DUK_ASSERT(tv2
!= NULL
);
929 /* For tv1 == tv2, this is a no-op (no explicit check needed). */
931 DUK_TVAL_SET_TVAL(&tv_tmp
, tv2
);
932 DUK_TVAL_SET_TVAL(tv2
, tv1
);
933 DUK_TVAL_INCREF(thr
, tv2
); /* no side effects */
934 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
937 DUK_EXTERNAL
void duk_remove(duk_context
*ctx
, duk_idx_t index
) {
938 duk_hthread
*thr
= (duk_hthread
*) ctx
;
941 #ifdef DUK_USE_REFERENCE_COUNTING
946 DUK_ASSERT_CTX_VALID(ctx
);
948 p
= duk_require_tval(ctx
, index
);
949 DUK_ASSERT(p
!= NULL
);
950 q
= duk_require_tval(ctx
, -1);
951 DUK_ASSERT(q
!= NULL
);
955 /* nbytes zero size case
957 * [ ... | p | x | x | q ] [ ... | p==q ]
958 * => [ ... | x | x | q ] [ ... ]
961 #ifdef DUK_USE_REFERENCE_COUNTING
962 /* use a temp: decref only when valstack reachable values are correct */
963 DUK_TVAL_SET_TVAL(&tv_tmp
, p
);
966 nbytes
= (duk_size_t
) (((duk_uint8_t
*) q
) - ((duk_uint8_t
*) p
)); /* Note: 'q' is top-1 */
967 DUK_MEMMOVE(p
, p
+ 1, nbytes
); /* zero size not an issue: pointers are valid */
969 DUK_TVAL_SET_UNDEFINED_UNUSED(q
);
972 #ifdef DUK_USE_REFERENCE_COUNTING
973 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
978 * Stack slice primitives
981 DUK_EXTERNAL
void duk_xcopymove_raw(duk_context
*to_ctx
, duk_context
*from_ctx
, duk_idx_t count
, duk_bool_t is_copy
) {
982 duk_hthread
*to_thr
= (duk_hthread
*) to_ctx
;
983 duk_hthread
*from_thr
= (duk_hthread
*) from_ctx
;
989 /* XXX: several pointer comparison issues here */
991 DUK_ASSERT_CTX_VALID(to_ctx
);
992 DUK_ASSERT_CTX_VALID(from_ctx
);
993 DUK_ASSERT(to_ctx
!= NULL
);
994 DUK_ASSERT(from_ctx
!= NULL
);
996 if (to_ctx
== from_ctx
) {
997 DUK_ERROR(to_thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_CONTEXT
);
1001 (count
> (duk_idx_t
) to_thr
->valstack_max
)) {
1002 /* Maximum value check ensures 'nbytes' won't wrap below. */
1003 DUK_ERROR(to_thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_COUNT
);
1007 nbytes
= sizeof(duk_tval
) * count
;
1011 DUK_ASSERT(to_thr
->valstack_top
<= to_thr
->valstack_end
);
1012 if ((duk_size_t
) ((duk_uint8_t
*) to_thr
->valstack_end
- (duk_uint8_t
*) to_thr
->valstack_top
) < nbytes
) {
1013 DUK_ERROR(to_thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
1015 src
= (void *) ((duk_uint8_t
*) from_thr
->valstack_top
- nbytes
);
1016 if (src
< (void *) from_thr
->valstack_bottom
) {
1017 DUK_ERROR(to_thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_COUNT
);
1020 /* copy values (no overlap even if to_ctx == from_ctx; that's not
1021 * allowed now anyway)
1023 DUK_ASSERT(nbytes
> 0);
1024 DUK_MEMCPY((void *) to_thr
->valstack_top
, src
, nbytes
);
1026 p
= to_thr
->valstack_top
;
1027 to_thr
->valstack_top
= (duk_tval
*) (void *) (((duk_uint8_t
*) p
) + nbytes
);
1030 /* incref copies, keep originals */
1031 q
= to_thr
->valstack_top
;
1033 DUK_TVAL_INCREF(to_thr
, p
); /* no side effects */
1037 /* no net refcount change */
1038 p
= from_thr
->valstack_top
;
1039 q
= (duk_tval
*) (void *) (((duk_uint8_t
*) p
) - nbytes
);
1040 from_thr
->valstack_top
= q
;
1042 /* elements above stack top are kept UNUSED */
1045 DUK_TVAL_SET_UNDEFINED_UNUSED(p
);
1047 /* XXX: fast primitive to set a bunch of values to UNDEFINED_UNUSED */
1056 DUK_EXTERNAL
void duk_require_undefined(duk_context
*ctx
, duk_idx_t index
) {
1057 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1060 DUK_ASSERT_CTX_VALID(ctx
);
1062 tv
= duk_get_tval(ctx
, index
);
1063 if (tv
&& DUK_TVAL_IS_UNDEFINED(tv
)) {
1064 /* Note: accept both 'actual' and 'unused' undefined */
1067 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_UNDEFINED
);
1070 DUK_EXTERNAL
void duk_require_null(duk_context
*ctx
, duk_idx_t index
) {
1071 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1074 DUK_ASSERT_CTX_VALID(ctx
);
1076 tv
= duk_get_tval(ctx
, index
);
1077 if (tv
&& DUK_TVAL_IS_NULL(tv
)) {
1080 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_NULL
);
1081 return; /* not reachable */
1084 DUK_EXTERNAL duk_bool_t
duk_get_boolean(duk_context
*ctx
, duk_idx_t index
) {
1085 duk_bool_t ret
= 0; /* default: false */
1088 DUK_ASSERT_CTX_VALID(ctx
);
1090 tv
= duk_get_tval(ctx
, index
);
1091 if (tv
&& DUK_TVAL_IS_BOOLEAN(tv
)) {
1092 ret
= DUK_TVAL_GET_BOOLEAN(tv
);
1095 DUK_ASSERT(ret
== 0 || ret
== 1);
1099 DUK_EXTERNAL duk_bool_t
duk_require_boolean(duk_context
*ctx
, duk_idx_t index
) {
1100 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1103 DUK_ASSERT_CTX_VALID(ctx
);
1105 tv
= duk_get_tval(ctx
, index
);
1106 if (tv
&& DUK_TVAL_IS_BOOLEAN(tv
)) {
1107 duk_bool_t ret
= DUK_TVAL_GET_BOOLEAN(tv
);
1108 DUK_ASSERT(ret
== 0 || ret
== 1);
1112 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_BOOLEAN
);
1113 return 0; /* not reachable */
1116 DUK_EXTERNAL duk_double_t
duk_get_number(duk_context
*ctx
, duk_idx_t index
) {
1117 duk_double_union ret
;
1120 DUK_ASSERT_CTX_VALID(ctx
);
1122 ret
.d
= DUK_DOUBLE_NAN
; /* default: NaN */
1123 tv
= duk_get_tval(ctx
, index
);
1124 if (tv
&& DUK_TVAL_IS_NUMBER(tv
)) {
1125 ret
.d
= DUK_TVAL_GET_NUMBER(tv
);
1129 * Number should already be in NaN-normalized form, but let's
1133 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret
);
1137 DUK_EXTERNAL duk_double_t
duk_require_number(duk_context
*ctx
, duk_idx_t index
) {
1138 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1141 DUK_ASSERT_CTX_VALID(ctx
);
1143 tv
= duk_get_tval(ctx
, index
);
1144 if (tv
&& DUK_TVAL_IS_NUMBER(tv
)) {
1145 duk_double_union ret
;
1146 ret
.d
= DUK_TVAL_GET_NUMBER(tv
);
1149 * Number should already be in NaN-normalized form,
1150 * but let's normalize anyway.
1153 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret
);
1157 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_NUMBER
);
1158 return DUK_DOUBLE_NAN
; /* not reachable */
1161 DUK_EXTERNAL duk_int_t
duk_get_int(duk_context
*ctx
, duk_idx_t index
) {
1162 /* Custom coercion for API */
1163 DUK_ASSERT_CTX_VALID(ctx
);
1164 return (duk_int_t
) duk__api_coerce_d2i(ctx
, index
, 0 /*require*/);
1167 DUK_EXTERNAL duk_uint_t
duk_get_uint(duk_context
*ctx
, duk_idx_t index
) {
1168 /* Custom coercion for API */
1169 DUK_ASSERT_CTX_VALID(ctx
);
1170 return (duk_uint_t
) duk__api_coerce_d2ui(ctx
, index
, 0 /*require*/);
1173 DUK_EXTERNAL duk_int_t
duk_require_int(duk_context
*ctx
, duk_idx_t index
) {
1174 /* Custom coercion for API */
1175 DUK_ASSERT_CTX_VALID(ctx
);
1176 return (duk_int_t
) duk__api_coerce_d2i(ctx
, index
, 1 /*require*/);
1179 DUK_EXTERNAL duk_uint_t
duk_require_uint(duk_context
*ctx
, duk_idx_t index
) {
1180 /* Custom coercion for API */
1181 DUK_ASSERT_CTX_VALID(ctx
);
1182 return (duk_uint_t
) duk__api_coerce_d2ui(ctx
, index
, 1 /*require*/);
1185 DUK_EXTERNAL
const char *duk_get_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
1189 DUK_ASSERT_CTX_VALID(ctx
);
1191 /* default: NULL, length 0 */
1197 tv
= duk_get_tval(ctx
, index
);
1198 if (tv
&& DUK_TVAL_IS_STRING(tv
)) {
1199 /* Here we rely on duk_hstring instances always being zero
1200 * terminated even if the actual string is not.
1202 duk_hstring
*h
= DUK_TVAL_GET_STRING(tv
);
1203 DUK_ASSERT(h
!= NULL
);
1204 ret
= (const char *) DUK_HSTRING_GET_DATA(h
);
1206 *out_len
= DUK_HSTRING_GET_BYTELEN(h
);
1213 DUK_EXTERNAL
const char *duk_require_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
1214 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1217 DUK_ASSERT_CTX_VALID(ctx
);
1219 /* Note: this check relies on the fact that even a zero-size string
1220 * has a non-NULL pointer.
1222 ret
= duk_get_lstring(ctx
, index
, out_len
);
1227 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_STRING
);
1228 return NULL
; /* not reachable */
1231 DUK_EXTERNAL
const char *duk_get_string(duk_context
*ctx
, duk_idx_t index
) {
1232 DUK_ASSERT_CTX_VALID(ctx
);
1234 return duk_get_lstring(ctx
, index
, NULL
);
1237 DUK_EXTERNAL
const char *duk_require_string(duk_context
*ctx
, duk_idx_t index
) {
1238 DUK_ASSERT_CTX_VALID(ctx
);
1240 return duk_require_lstring(ctx
, index
, NULL
);
1243 DUK_EXTERNAL
void *duk_get_pointer(duk_context
*ctx
, duk_idx_t index
) {
1246 DUK_ASSERT_CTX_VALID(ctx
);
1248 tv
= duk_get_tval(ctx
, index
);
1249 if (tv
&& DUK_TVAL_IS_POINTER(tv
)) {
1250 void *p
= DUK_TVAL_GET_POINTER(tv
); /* may be NULL */
1257 DUK_EXTERNAL
void *duk_require_pointer(duk_context
*ctx
, duk_idx_t index
) {
1258 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1261 DUK_ASSERT_CTX_VALID(ctx
);
1263 /* Note: here we must be wary of the fact that a pointer may be
1264 * valid and be a NULL.
1266 tv
= duk_get_tval(ctx
, index
);
1267 if (tv
&& DUK_TVAL_IS_POINTER(tv
)) {
1268 void *p
= DUK_TVAL_GET_POINTER(tv
); /* may be NULL */
1272 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_POINTER
);
1273 return NULL
; /* not reachable */
1277 DUK_INTERNAL
void *duk_get_voidptr(duk_context
*ctx
, duk_idx_t index
) {
1280 DUK_ASSERT_CTX_VALID(ctx
);
1282 tv
= duk_get_tval(ctx
, index
);
1283 if (tv
&& DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
1284 duk_heaphdr
*h
= DUK_TVAL_GET_HEAPHDR(tv
);
1285 DUK_ASSERT(h
!= NULL
);
1293 DUK_LOCAL
void *duk__get_buffer_helper(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
, duk_bool_t throw_flag
) {
1294 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1297 DUK_ASSERT_CTX_VALID(ctx
);
1300 if (out_size
!= NULL
) {
1304 tv
= duk_get_tval(ctx
, index
);
1305 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
1306 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
1307 DUK_ASSERT(h
!= NULL
);
1309 *out_size
= DUK_HBUFFER_GET_SIZE(h
);
1311 return (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
); /* may be NULL (but only if size is 0) */
1315 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_BUFFER
);
1320 DUK_EXTERNAL
void *duk_get_buffer(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1321 return duk__get_buffer_helper(ctx
, index
, out_size
, 0 /*throw_flag*/);
1324 DUK_EXTERNAL
void *duk_require_buffer(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1325 return duk__get_buffer_helper(ctx
, index
, out_size
, 1 /*throw_flag*/);
1328 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
) {
1329 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1332 DUK_ASSERT_CTX_VALID(ctx
);
1335 if (out_size
!= NULL
) {
1339 tv
= duk_get_tval(ctx
, index
);
1344 if (DUK_TVAL_IS_BUFFER(tv
)) {
1345 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
1346 DUK_ASSERT(h
!= NULL
);
1348 *out_size
= DUK_HBUFFER_GET_SIZE(h
);
1350 return (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
); /* may be NULL (but only if size is 0) */
1351 } else if (DUK_TVAL_IS_OBJECT(tv
)) {
1352 duk_hobject
*h
= DUK_TVAL_GET_OBJECT(tv
);
1353 DUK_ASSERT(h
!= NULL
);
1354 if (DUK_HOBJECT_IS_BUFFEROBJECT(h
)) {
1355 /* XXX: this is probably a useful shared helper: for a
1356 * duk_hbufferobject, get a validated buffer pointer/length.
1358 duk_hbufferobject
*h_bufobj
= (duk_hbufferobject
*) h
;
1359 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
1361 if (h_bufobj
->buf
!= NULL
&&
1362 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj
)) {
1365 p
= (duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_bufobj
->buf
);
1366 if (out_size
!= NULL
) {
1367 *out_size
= (duk_size_t
) h_bufobj
->length
;
1369 return (void *) (p
+ h_bufobj
->offset
);
1371 /* if slice not fully valid, treat as error */
1377 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_BUFFER
);
1382 DUK_EXTERNAL
void *duk_get_buffer_data(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1383 return duk__get_buffer_data_helper(ctx
, index
, out_size
, 0 /*throw_flag*/);
1386 DUK_EXTERNAL
void *duk_require_buffer_data(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
) {
1387 return duk__get_buffer_data_helper(ctx
, index
, out_size
, 1 /*throw_flag*/);
1390 /* Raw helper for getting a value from the stack, checking its tag, and possible its object class.
1391 * The tag cannot be a number because numbers don't have an internal tag in the packed representation.
1393 DUK_INTERNAL duk_heaphdr
*duk_get_tagged_heaphdr_raw(duk_context
*ctx
, duk_idx_t index
, duk_uint_t flags_and_tag
) {
1394 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1396 duk_small_uint_t tag
= flags_and_tag
& 0xffffU
; /* tags can be up to 16 bits */
1398 DUK_ASSERT_CTX_VALID(ctx
);
1400 tv
= duk_get_tval(ctx
, index
);
1401 if (tv
&& (DUK_TVAL_GET_TAG(tv
) == tag
)) {
1404 /* Note: tag comparison in general doesn't work for numbers,
1405 * but it does work for everything else (heap objects here).
1407 ret
= DUK_TVAL_GET_HEAPHDR(tv
);
1408 DUK_ASSERT(ret
!= NULL
); /* tagged null pointers should never occur */
1410 /* If class check has been requested, tag must also be DUK_TAG_OBJECT.
1411 * This allows us to just check the class check flag without checking
1414 DUK_ASSERT((flags_and_tag
& DUK_GETTAGGED_FLAG_CHECK_CLASS
) == 0 ||
1415 tag
== DUK_TAG_OBJECT
);
1417 if ((flags_and_tag
& DUK_GETTAGGED_FLAG_CHECK_CLASS
) == 0 || /* no class check */
1418 (duk_int_t
) DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject
*) ret
) == /* or class check matches */
1419 (duk_int_t
) ((flags_and_tag
>> DUK_GETTAGGED_CLASS_SHIFT
) & 0xff)) {
1424 if (flags_and_tag
& DUK_GETTAGGED_FLAG_ALLOW_NULL
) {
1425 return (duk_heaphdr
*) NULL
;
1428 /* Formatting the tag number here is not very useful: the tag value
1429 * is Duktape internal (not the same as DUK_TYPE_xxx) and even depends
1430 * on the duk_tval layout. If anything, add a human readable type here.
1432 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_UNEXPECTED_TYPE
);
1433 return NULL
; /* not reachable */
1436 DUK_INTERNAL duk_hstring
*duk_get_hstring(duk_context
*ctx
, duk_idx_t index
) {
1437 return (duk_hstring
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_STRING
| DUK_GETTAGGED_FLAG_ALLOW_NULL
);
1440 DUK_INTERNAL duk_hstring
*duk_require_hstring(duk_context
*ctx
, duk_idx_t index
) {
1441 return (duk_hstring
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_STRING
);
1444 DUK_INTERNAL duk_hobject
*duk_get_hobject(duk_context
*ctx
, duk_idx_t index
) {
1445 return (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
| DUK_GETTAGGED_FLAG_ALLOW_NULL
);
1448 DUK_INTERNAL duk_hobject
*duk_require_hobject(duk_context
*ctx
, duk_idx_t index
) {
1449 return (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1452 DUK_INTERNAL duk_hbuffer
*duk_get_hbuffer(duk_context
*ctx
, duk_idx_t index
) {
1453 return (duk_hbuffer
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_BUFFER
| DUK_GETTAGGED_FLAG_ALLOW_NULL
);
1456 DUK_INTERNAL duk_hbuffer
*duk_require_hbuffer(duk_context
*ctx
, duk_idx_t index
) {
1457 return (duk_hbuffer
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_BUFFER
);
1460 DUK_INTERNAL duk_hthread
*duk_get_hthread(duk_context
*ctx
, duk_idx_t index
) {
1461 duk_hobject
*h
= (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
| DUK_GETTAGGED_FLAG_ALLOW_NULL
);
1462 if (h
!= NULL
&& !DUK_HOBJECT_IS_THREAD(h
)) {
1465 return (duk_hthread
*) h
;
1468 DUK_INTERNAL duk_hthread
*duk_require_hthread(duk_context
*ctx
, duk_idx_t index
) {
1469 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1470 duk_hobject
*h
= (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1471 DUK_ASSERT(h
!= NULL
);
1472 if (!DUK_HOBJECT_IS_THREAD(h
)) {
1473 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_THREAD
);
1475 return (duk_hthread
*) h
;
1478 DUK_INTERNAL duk_hcompiledfunction
*duk_get_hcompiledfunction(duk_context
*ctx
, duk_idx_t index
) {
1479 duk_hobject
*h
= (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
| DUK_GETTAGGED_FLAG_ALLOW_NULL
);
1480 if (h
!= NULL
&& !DUK_HOBJECT_IS_COMPILEDFUNCTION(h
)) {
1483 return (duk_hcompiledfunction
*) h
;
1486 DUK_INTERNAL duk_hcompiledfunction
*duk_require_hcompiledfunction(duk_context
*ctx
, duk_idx_t index
) {
1487 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1488 duk_hobject
*h
= (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1489 DUK_ASSERT(h
!= NULL
);
1490 if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(h
)) {
1491 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_COMPILEDFUNCTION
);
1493 return (duk_hcompiledfunction
*) h
;
1496 DUK_INTERNAL duk_hnativefunction
*duk_get_hnativefunction(duk_context
*ctx
, duk_idx_t index
) {
1497 duk_hobject
*h
= (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
| DUK_GETTAGGED_FLAG_ALLOW_NULL
);
1498 if (h
!= NULL
&& !DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
1501 return (duk_hnativefunction
*) h
;
1504 DUK_INTERNAL duk_hnativefunction
*duk_require_hnativefunction(duk_context
*ctx
, duk_idx_t index
) {
1505 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1506 duk_hobject
*h
= (duk_hobject
*) duk_get_tagged_heaphdr_raw(ctx
, index
, DUK_TAG_OBJECT
);
1507 DUK_ASSERT(h
!= NULL
);
1508 if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
1509 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_NATIVEFUNCTION
);
1511 return (duk_hnativefunction
*) h
;
1514 DUK_EXTERNAL duk_c_function
duk_get_c_function(duk_context
*ctx
, duk_idx_t index
) {
1517 duk_hnativefunction
*f
;
1519 DUK_ASSERT_CTX_VALID(ctx
);
1521 tv
= duk_get_tval(ctx
, index
);
1525 if (!DUK_TVAL_IS_OBJECT(tv
)) {
1528 h
= DUK_TVAL_GET_OBJECT(tv
);
1529 DUK_ASSERT(h
!= NULL
);
1531 if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h
)) {
1534 DUK_ASSERT(DUK_HOBJECT_HAS_NATIVEFUNCTION(h
));
1535 f
= (duk_hnativefunction
*) h
;
1540 DUK_EXTERNAL duk_c_function
duk_require_c_function(duk_context
*ctx
, duk_idx_t index
) {
1541 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1544 DUK_ASSERT_CTX_VALID(ctx
);
1546 ret
= duk_get_c_function(ctx
, index
);
1548 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_C_FUNCTION
);
1553 DUK_EXTERNAL duk_context
*duk_get_context(duk_context
*ctx
, duk_idx_t index
) {
1554 DUK_ASSERT_CTX_VALID(ctx
);
1556 return (duk_context
*) duk_get_hthread(ctx
, index
);
1559 DUK_EXTERNAL duk_context
*duk_require_context(duk_context
*ctx
, duk_idx_t index
) {
1560 DUK_ASSERT_CTX_VALID(ctx
);
1562 return (duk_context
*) duk_require_hthread(ctx
, index
);
1565 DUK_EXTERNAL
void *duk_get_heapptr(duk_context
*ctx
, duk_idx_t index
) {
1569 DUK_ASSERT_CTX_VALID(ctx
);
1571 tv
= duk_get_tval(ctx
, index
);
1572 if (tv
&& DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
1573 ret
= (void *) DUK_TVAL_GET_HEAPHDR(tv
);
1574 DUK_ASSERT(ret
!= NULL
);
1578 return (void *) NULL
;
1581 DUK_EXTERNAL
void *duk_require_heapptr(duk_context
*ctx
, duk_idx_t index
) {
1582 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1586 DUK_ASSERT_CTX_VALID(ctx
);
1588 tv
= duk_require_tval(ctx
, index
);
1589 DUK_ASSERT(tv
!= NULL
);
1590 if (DUK_TVAL_IS_HEAP_ALLOCATED(tv
)) {
1591 ret
= (void *) DUK_TVAL_GET_HEAPHDR(tv
);
1592 DUK_ASSERT(ret
!= NULL
);
1596 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_UNEXPECTED_TYPE
);
1597 return (void *) NULL
; /* not reachable */
1601 /* This would be pointless: we'd return NULL for both lightfuncs and
1604 duk_hobject
*duk_get_hobject_or_lfunc(duk_context
*ctx
, duk_idx_t index
) {
1608 /* Useful for internal call sites where we either expect an object (function)
1609 * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
1610 * to an object). Return value is NULL if value is neither an object nor a
1613 duk_hobject
*duk_get_hobject_or_lfunc_coerce(duk_context
*ctx
, duk_idx_t index
) {
1616 DUK_ASSERT_CTX_VALID(ctx
);
1618 tv
= duk_require_tval(ctx
, index
);
1619 DUK_ASSERT(tv
!= NULL
);
1620 if (DUK_TVAL_IS_OBJECT(tv
)) {
1621 return DUK_TVAL_GET_OBJECT(tv
);
1622 } else if (DUK_TVAL_IS_LIGHTFUNC(tv
)) {
1623 duk_to_object(ctx
, index
);
1624 return duk_require_hobject(ctx
, index
);
1630 /* Useful for internal call sites where we either expect an object (function)
1631 * or a lightfunc. Returns NULL for a lightfunc.
1633 DUK_INTERNAL duk_hobject
*duk_require_hobject_or_lfunc(duk_context
*ctx
, duk_idx_t index
) {
1634 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1637 DUK_ASSERT_CTX_VALID(ctx
);
1639 tv
= duk_require_tval(ctx
, index
);
1640 DUK_ASSERT(tv
!= NULL
);
1641 if (DUK_TVAL_IS_OBJECT(tv
)) {
1642 return DUK_TVAL_GET_OBJECT(tv
);
1643 } else if (DUK_TVAL_IS_LIGHTFUNC(tv
)) {
1647 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_UNEXPECTED_TYPE
);
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
);
1669 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_UNEXPECTED_TYPE
);
1670 return NULL
; /* not reachable */
1673 DUK_EXTERNAL duk_size_t
duk_get_length(duk_context
*ctx
, duk_idx_t index
) {
1676 DUK_ASSERT_CTX_VALID(ctx
);
1678 tv
= duk_get_tval(ctx
, index
);
1683 switch (DUK_TVAL_GET_TAG(tv
)) {
1684 case DUK_TAG_UNDEFINED
:
1686 case DUK_TAG_BOOLEAN
:
1687 case DUK_TAG_POINTER
:
1689 case DUK_TAG_STRING
: {
1690 duk_hstring
*h
= DUK_TVAL_GET_STRING(tv
);
1691 DUK_ASSERT(h
!= NULL
);
1692 return (duk_size_t
) DUK_HSTRING_GET_CHARLEN(h
);
1694 case DUK_TAG_OBJECT
: {
1695 duk_hobject
*h
= DUK_TVAL_GET_OBJECT(tv
);
1696 DUK_ASSERT(h
!= NULL
);
1697 return (duk_size_t
) duk_hobject_get_length((duk_hthread
*) ctx
, h
);
1699 case DUK_TAG_BUFFER
: {
1700 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
1701 DUK_ASSERT(h
!= NULL
);
1702 return (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
);
1704 case DUK_TAG_LIGHTFUNC
: {
1705 duk_small_uint_t lf_flags
;
1706 lf_flags
= DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv
);
1707 return (duk_size_t
) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags
);
1709 #if defined(DUK_USE_FASTINT)
1710 case DUK_TAG_FASTINT
:
1714 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
1721 DUK_INTERNAL
void duk_set_length(duk_context
*ctx
, duk_idx_t index
, duk_size_t length
) {
1722 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1725 DUK_ASSERT_CTX_VALID(ctx
);
1727 h
= duk_get_hobject(ctx
, index
);
1732 duk_hobject_set_length(thr
, h
, (duk_uint32_t
) length
); /* XXX: typing */
1736 * Conversions and coercions
1738 * The conversion/coercions are in-place operations on the value stack.
1739 * Some operations are implemented here directly, while others call a
1740 * helper in duk_js_ops.c after validating arguments.
1743 /* E5 Section 8.12.8 */
1745 DUK_LOCAL duk_bool_t
duk__defaultvalue_coerce_attempt(duk_context
*ctx
, duk_idx_t index
, duk_small_int_t func_stridx
) {
1746 if (duk_get_prop_stridx(ctx
, index
, func_stridx
)) {
1748 if (duk_is_callable(ctx
, -1)) {
1749 duk_dup(ctx
, index
); /* -> [ ... func this ] */
1750 duk_call_method(ctx
, 0); /* -> [ ... retval ] */
1751 if (duk_is_primitive(ctx
, -1)) {
1752 duk_replace(ctx
, index
);
1755 /* [ ... retval ]; popped below */
1758 duk_pop(ctx
); /* [ ... func/retval ] -> [ ... ] */
1762 DUK_EXTERNAL
void duk_to_defaultvalue(duk_context
*ctx
, duk_idx_t index
, duk_int_t hint
) {
1763 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1765 /* inline initializer for coercers[] is not allowed by old compilers like BCC */
1766 duk_small_int_t coercers
[2];
1768 DUK_ASSERT_CTX_VALID(ctx
);
1769 DUK_ASSERT(thr
!= NULL
);
1771 coercers
[0] = DUK_STRIDX_VALUE_OF
;
1772 coercers
[1] = DUK_STRIDX_TO_STRING
;
1774 index
= duk_require_normalize_index(ctx
, index
);
1775 obj
= duk_require_hobject_or_lfunc(ctx
, index
);
1777 if (hint
== DUK_HINT_NONE
) {
1778 if (obj
!= NULL
&& DUK_HOBJECT_GET_CLASS_NUMBER(obj
) == DUK_HOBJECT_CLASS_DATE
) {
1779 hint
= DUK_HINT_STRING
;
1781 hint
= DUK_HINT_NUMBER
;
1785 if (hint
== DUK_HINT_STRING
) {
1786 coercers
[0] = DUK_STRIDX_TO_STRING
;
1787 coercers
[1] = DUK_STRIDX_VALUE_OF
;
1790 if (duk__defaultvalue_coerce_attempt(ctx
, index
, coercers
[0])) {
1794 if (duk__defaultvalue_coerce_attempt(ctx
, index
, coercers
[1])) {
1798 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_DEFAULTVALUE_COERCE_FAILED
);
1801 DUK_EXTERNAL
void duk_to_undefined(duk_context
*ctx
, duk_idx_t index
) {
1802 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1806 DUK_ASSERT_CTX_VALID(ctx
);
1809 tv
= duk_require_tval(ctx
, index
);
1810 DUK_ASSERT(tv
!= NULL
);
1811 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1812 DUK_TVAL_SET_UNDEFINED_ACTUAL(tv
); /* no need to incref */
1813 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1816 DUK_EXTERNAL
void duk_to_null(duk_context
*ctx
, duk_idx_t index
) {
1817 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1821 DUK_ASSERT_CTX_VALID(ctx
);
1824 tv
= duk_require_tval(ctx
, index
);
1825 DUK_ASSERT(tv
!= NULL
);
1826 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1827 DUK_TVAL_SET_NULL(tv
); /* no need to incref */
1828 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1831 /* E5 Section 9.1 */
1832 DUK_EXTERNAL
void duk_to_primitive(duk_context
*ctx
, duk_idx_t index
, duk_int_t hint
) {
1833 DUK_ASSERT_CTX_VALID(ctx
);
1834 DUK_ASSERT(hint
== DUK_HINT_NONE
|| hint
== DUK_HINT_NUMBER
|| hint
== DUK_HINT_STRING
);
1836 index
= duk_require_normalize_index(ctx
, index
);
1838 if (!duk_check_type_mask(ctx
, index
, DUK_TYPE_MASK_OBJECT
|
1839 DUK_TYPE_MASK_LIGHTFUNC
)) {
1840 /* everything except object stay as is */
1843 duk_to_defaultvalue(ctx
, index
, hint
);
1846 /* E5 Section 9.2 */
1847 DUK_EXTERNAL duk_bool_t
duk_to_boolean(duk_context
*ctx
, duk_idx_t index
) {
1848 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1853 DUK_ASSERT_CTX_VALID(ctx
);
1856 index
= duk_require_normalize_index(ctx
, index
);
1858 tv
= duk_require_tval(ctx
, index
);
1859 DUK_ASSERT(tv
!= NULL
);
1861 val
= duk_js_toboolean(tv
);
1862 DUK_ASSERT(val
== 0 || val
== 1);
1864 /* Note: no need to re-lookup tv, conversion is side effect free */
1865 DUK_ASSERT(tv
!= NULL
);
1866 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1867 DUK_TVAL_SET_BOOLEAN(tv
, val
); /* no need to incref */
1868 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1872 DUK_EXTERNAL duk_double_t
duk_to_number(duk_context
*ctx
, duk_idx_t index
) {
1873 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1878 DUK_ASSERT_CTX_VALID(ctx
);
1880 tv
= duk_require_tval(ctx
, index
);
1881 DUK_ASSERT(tv
!= NULL
);
1883 d
= duk_js_tonumber(thr
, tv
);
1885 /* Note: need to re-lookup because ToNumber() may have side effects */
1886 tv
= duk_require_tval(ctx
, index
);
1887 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1888 DUK_TVAL_SET_NUMBER(tv
, d
); /* no need to incref */
1889 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1893 /* XXX: combine all the integer conversions: they share everything
1894 * but the helper function for coercion.
1897 typedef duk_double_t (*duk__toint_coercer
)(duk_hthread
*thr
, duk_tval
*tv
);
1899 DUK_LOCAL duk_double_t
duk__to_int_uint_helper(duk_context
*ctx
, duk_idx_t index
, duk__toint_coercer coerce_func
) {
1900 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1905 DUK_ASSERT_CTX_VALID(ctx
);
1907 tv
= duk_require_tval(ctx
, index
);
1908 DUK_ASSERT(tv
!= NULL
);
1909 d
= coerce_func(thr
, tv
);
1913 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1914 tv
= duk_require_tval(ctx
, index
);
1915 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1916 DUK_TVAL_SET_NUMBER(tv
, d
); /* no need to incref */
1917 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1921 DUK_EXTERNAL duk_int_t
duk_to_int(duk_context
*ctx
, duk_idx_t index
) {
1922 /* Value coercion (in stack): ToInteger(), E5 Section 9.4
1923 * API return value coercion: custom
1925 DUK_ASSERT_CTX_VALID(ctx
);
1926 (void) duk__to_int_uint_helper(ctx
, index
, duk_js_tointeger
);
1927 return (duk_int_t
) duk__api_coerce_d2i(ctx
, index
, 0 /*require*/);
1930 DUK_EXTERNAL duk_uint_t
duk_to_uint(duk_context
*ctx
, duk_idx_t index
) {
1931 /* Value coercion (in stack): ToInteger(), E5 Section 9.4
1932 * API return value coercion: custom
1934 DUK_ASSERT_CTX_VALID(ctx
);
1935 (void) duk__to_int_uint_helper(ctx
, index
, duk_js_tointeger
);
1936 return (duk_uint_t
) duk__api_coerce_d2ui(ctx
, index
, 0 /*require*/);
1939 DUK_EXTERNAL duk_int32_t
duk_to_int32(duk_context
*ctx
, duk_idx_t index
) {
1940 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1945 DUK_ASSERT_CTX_VALID(ctx
);
1947 tv
= duk_require_tval(ctx
, index
);
1948 DUK_ASSERT(tv
!= NULL
);
1949 ret
= duk_js_toint32(thr
, tv
);
1951 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1952 tv
= duk_require_tval(ctx
, index
);
1953 #if defined(DUK_USE_FASTINT)
1954 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1955 DUK_TVAL_SET_FASTINT_I32(tv
, ret
); /* no need to incref */
1956 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1959 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1960 DUK_TVAL_SET_NUMBER(tv
, (duk_double_t
) ret
); /* no need to incref */
1961 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1966 DUK_EXTERNAL duk_uint32_t
duk_to_uint32(duk_context
*ctx
, duk_idx_t index
) {
1967 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1972 DUK_ASSERT_CTX_VALID(ctx
);
1974 tv
= duk_require_tval(ctx
, index
);
1975 DUK_ASSERT(tv
!= NULL
);
1976 ret
= duk_js_touint32(thr
, tv
);
1978 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1979 tv
= duk_require_tval(ctx
, index
);
1980 #if defined(DUK_USE_FASTINT)
1981 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1982 DUK_TVAL_SET_FASTINT_U32(tv
, ret
); /* no need to incref */
1983 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1986 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
1987 DUK_TVAL_SET_NUMBER(tv
, (duk_double_t
) ret
); /* no need to incref */
1988 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
1993 DUK_EXTERNAL duk_uint16_t
duk_to_uint16(duk_context
*ctx
, duk_idx_t index
) {
1994 duk_hthread
*thr
= (duk_hthread
*) ctx
;
1999 DUK_ASSERT_CTX_VALID(ctx
);
2001 tv
= duk_require_tval(ctx
, index
);
2002 DUK_ASSERT(tv
!= NULL
);
2003 ret
= duk_js_touint16(thr
, tv
);
2005 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
2006 tv
= duk_require_tval(ctx
, index
);
2007 #if defined(DUK_USE_FASTINT)
2008 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
2009 DUK_TVAL_SET_FASTINT_U32(tv
, ret
); /* no need to incref */
2010 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
2013 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
2014 DUK_TVAL_SET_NUMBER(tv
, (duk_double_t
) ret
); /* no need to incref */
2015 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
2020 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2021 /* Special coercion for Uint8ClampedArray. */
2022 DUK_INTERNAL duk_uint8_t
duk_to_uint8clamped(duk_context
*ctx
, duk_idx_t index
) {
2027 /* XXX: Simplify this algorithm, should be possible to come up with
2028 * a shorter and faster algorithm by inspecting IEEE representation
2032 d
= duk_to_number(ctx
, index
);
2035 } else if (d
>= 255) {
2037 } else if (DUK_ISNAN(d
)) {
2038 /* Avoid NaN-to-integer coercion as it is compiler specific. */
2042 t
= d
- DUK_FLOOR(d
);
2044 /* Exact halfway, round to even. */
2045 ret
= (duk_uint8_t
) d
;
2046 ret
= (ret
+ 1) & 0xfe; /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4
2047 * Example: d=4.5, t=0.5 -> ret = (4 + 1) & 0xfe = 5 & 0xfe = 4
2050 /* Not halfway, round to nearest. */
2051 ret
= (duk_uint8_t
) (d
+ 0.5);
2055 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2057 DUK_EXTERNAL
const char *duk_to_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
2058 DUK_ASSERT_CTX_VALID(ctx
);
2060 (void) duk_to_string(ctx
, index
);
2061 return duk_require_lstring(ctx
, index
, out_len
);
2064 DUK_LOCAL duk_ret_t
duk__safe_to_string_raw(duk_context
*ctx
) {
2065 DUK_ASSERT_CTX_VALID(ctx
);
2067 duk_to_string(ctx
, -1);
2071 DUK_EXTERNAL
const char *duk_safe_to_lstring(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_len
) {
2072 DUK_ASSERT_CTX_VALID(ctx
);
2074 index
= duk_require_normalize_index(ctx
, index
);
2076 /* We intentionally ignore the duk_safe_call() return value and only
2077 * check the output type. This way we don't also need to check that
2078 * the returned value is indeed a string in the success case.
2081 duk_dup(ctx
, index
);
2082 (void) duk_safe_call(ctx
, duk__safe_to_string_raw
, 1 /*nargs*/, 1 /*nrets*/);
2083 if (!duk_is_string(ctx
, -1)) {
2084 /* Error: try coercing error to string once. */
2085 (void) duk_safe_call(ctx
, duk__safe_to_string_raw
, 1 /*nargs*/, 1 /*nrets*/);
2086 if (!duk_is_string(ctx
, -1)) {
2089 duk_push_hstring_stridx(ctx
, DUK_STRIDX_UC_ERROR
);
2096 DUK_ASSERT(duk_is_string(ctx
, -1));
2098 duk_replace(ctx
, index
);
2099 return duk_require_lstring(ctx
, index
, out_len
);
2102 /* XXX: other variants like uint, u32 etc */
2103 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
) {
2104 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2107 duk_double_t d
, dmin
, dmax
;
2109 duk_bool_t clamped
= 0;
2111 DUK_ASSERT_CTX_VALID(ctx
);
2113 tv
= duk_require_tval(ctx
, index
);
2114 DUK_ASSERT(tv
!= NULL
);
2115 d
= duk_js_tointeger(thr
, tv
); /* E5 Section 9.4, ToInteger() */
2117 dmin
= (duk_double_t
) minval
;
2118 dmax
= (duk_double_t
) maxval
;
2124 } else if (d
> dmax
) {
2129 res
= (duk_int_t
) d
;
2131 /* 'd' and 'res' agree here */
2133 /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */
2134 tv
= duk_require_tval(ctx
, index
);
2135 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
2136 #if defined(DUK_USE_FASTINT)
2137 #if (DUK_INT_MAX <= 0x7fffffffL)
2138 DUK_TVAL_SET_FASTINT_I32(tv
, res
);
2140 /* Clamping needed if duk_int_t is 64 bits. */
2141 if (res
>= DUK_FASTINT_MIN
&& res
<= DUK_FASTINT_MAX
) {
2142 DUK_TVAL_SET_FASTINT(tv
, res
);
2144 DUK_TVAL_SET_NUMBER(tv
, d
);
2148 DUK_TVAL_SET_NUMBER(tv
, d
); /* no need to incref */
2150 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
2153 *out_clamped
= clamped
;
2155 /* coerced value is updated to value stack even when RangeError thrown */
2157 DUK_ERROR(thr
, DUK_ERR_RANGE_ERROR
, DUK_STR_NUMBER_OUTSIDE_RANGE
);
2164 DUK_INTERNAL duk_int_t
duk_to_int_clamped(duk_context
*ctx
, duk_idx_t index
, duk_idx_t minval
, duk_idx_t maxval
) {
2166 return duk_to_int_clamped_raw(ctx
, index
, minval
, maxval
, &dummy
);
2169 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
) {
2170 return duk_to_int_clamped_raw(ctx
, index
, minval
, maxval
, NULL
); /* out_clamped==NULL -> RangeError if outside range */
2173 DUK_EXTERNAL
const char *duk_to_string(duk_context
*ctx
, duk_idx_t index
) {
2174 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2177 DUK_ASSERT_CTX_VALID(ctx
);
2180 index
= duk_require_normalize_index(ctx
, index
);
2182 tv
= duk_require_tval(ctx
, index
);
2183 DUK_ASSERT(tv
!= NULL
);
2185 switch (DUK_TVAL_GET_TAG(tv
)) {
2186 case DUK_TAG_UNDEFINED
: {
2187 duk_push_hstring_stridx(ctx
, DUK_STRIDX_LC_UNDEFINED
);
2190 case DUK_TAG_NULL
: {
2191 duk_push_hstring_stridx(ctx
, DUK_STRIDX_LC_NULL
);
2194 case DUK_TAG_BOOLEAN
: {
2195 if (DUK_TVAL_GET_BOOLEAN(tv
)) {
2196 duk_push_hstring_stridx(ctx
, DUK_STRIDX_TRUE
);
2198 duk_push_hstring_stridx(ctx
, DUK_STRIDX_FALSE
);
2202 case DUK_TAG_STRING
: {
2206 case DUK_TAG_OBJECT
: {
2207 duk_to_primitive(ctx
, index
, DUK_HINT_STRING
);
2208 return duk_to_string(ctx
, index
); /* Note: recursive call */
2210 case DUK_TAG_BUFFER
: {
2211 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2213 /* Note: this allows creation of internal strings. */
2215 DUK_ASSERT(h
!= NULL
);
2216 duk_push_lstring(ctx
,
2217 (const char *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h
),
2218 (duk_size_t
) DUK_HBUFFER_GET_SIZE(h
));
2221 case DUK_TAG_POINTER
: {
2222 void *ptr
= DUK_TVAL_GET_POINTER(tv
);
2224 duk_push_sprintf(ctx
, DUK_STR_FMT_PTR
, (void *) ptr
);
2226 /* Represent a null pointer as 'null' to be consistent with
2227 * the JX format variant. Native '%p' format for a NULL
2228 * pointer may be e.g. '(nil)'.
2230 duk_push_hstring_stridx(ctx
, DUK_STRIDX_LC_NULL
);
2234 case DUK_TAG_LIGHTFUNC
: {
2235 /* Should match Function.prototype.toString() */
2236 duk_push_lightfunc_tostring(ctx
, tv
);
2239 #if defined(DUK_USE_FASTINT)
2240 case DUK_TAG_FASTINT
:
2244 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2245 duk_push_tval(ctx
, tv
);
2246 duk_numconv_stringify(ctx
,
2248 0 /*precision:shortest*/,
2249 0 /*force_exponential*/);
2254 duk_replace(ctx
, index
);
2257 return duk_require_string(ctx
, index
);
2260 DUK_INTERNAL duk_hstring
*duk_to_hstring(duk_context
*ctx
, duk_idx_t index
) {
2262 DUK_ASSERT_CTX_VALID(ctx
);
2263 duk_to_string(ctx
, index
);
2264 ret
= duk_get_hstring(ctx
, index
);
2265 DUK_ASSERT(ret
!= NULL
);
2269 DUK_EXTERNAL
void *duk_to_buffer_raw(duk_context
*ctx
, duk_idx_t index
, duk_size_t
*out_size
, duk_uint_t mode
) {
2270 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2272 const duk_uint8_t
*src_data
;
2273 duk_size_t src_size
;
2274 duk_uint8_t
*dst_data
;
2276 DUK_ASSERT_CTX_VALID(ctx
);
2279 index
= duk_require_normalize_index(ctx
, index
);
2281 h_buf
= duk_get_hbuffer(ctx
, index
);
2282 if (h_buf
!= NULL
) {
2283 /* Buffer is kept as is, with the fixed/dynamic nature of the
2284 * buffer only changed if requested. An external buffer
2285 * is converted into a non-external dynamic buffer in a
2286 * duk_to_dynamic_buffer() call.
2290 src_data
= (const duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_buf
);
2291 src_size
= DUK_HBUFFER_GET_SIZE(h_buf
);
2293 tmp
= (DUK_HBUFFER_HAS_DYNAMIC(h_buf
) ? DUK_BUF_MODE_DYNAMIC
: DUK_BUF_MODE_FIXED
);
2294 if ((tmp
== mode
&& !DUK_HBUFFER_HAS_EXTERNAL(h_buf
)) ||
2295 mode
== DUK_BUF_MODE_DONTCARE
) {
2296 /* Note: src_data may be NULL if input is a zero-size
2299 dst_data
= (duk_uint8_t
*) src_data
;
2303 /* Non-buffer value is first ToString() coerced, then converted
2304 * to a buffer (fixed buffer is used unless a dynamic buffer is
2305 * explicitly requested).
2308 src_data
= (const duk_uint8_t
*) duk_to_lstring(ctx
, index
, &src_size
);
2311 dst_data
= (duk_uint8_t
*) duk_push_buffer(ctx
, src_size
, (mode
== DUK_BUF_MODE_DYNAMIC
) /*dynamic*/);
2312 if (DUK_LIKELY(src_size
> 0)) {
2313 /* When src_size == 0, src_data may be NULL (if source
2314 * buffer is dynamic), and dst_data may be NULL (if
2315 * target buffer is dynamic). Avoid zero-size memcpy()
2316 * with an invalid pointer.
2318 DUK_MEMCPY(dst_data
, src_data
, src_size
);
2320 duk_replace(ctx
, index
);
2324 *out_size
= src_size
;
2329 DUK_EXTERNAL
void *duk_to_pointer(duk_context
*ctx
, duk_idx_t index
) {
2333 DUK_ASSERT_CTX_VALID(ctx
);
2335 index
= duk_require_normalize_index(ctx
, index
);
2337 tv
= duk_require_tval(ctx
, index
);
2338 DUK_ASSERT(tv
!= NULL
);
2340 switch (DUK_TVAL_GET_TAG(tv
)) {
2341 case DUK_TAG_UNDEFINED
:
2343 case DUK_TAG_BOOLEAN
:
2346 case DUK_TAG_POINTER
:
2347 res
= DUK_TVAL_GET_POINTER(tv
);
2349 case DUK_TAG_STRING
:
2350 case DUK_TAG_OBJECT
:
2351 case DUK_TAG_BUFFER
:
2352 /* Heap allocated: return heap pointer which is NOT useful
2353 * for the caller, except for debugging.
2355 res
= (void *) DUK_TVAL_GET_HEAPHDR(tv
);
2357 case DUK_TAG_LIGHTFUNC
:
2358 /* Function pointers do not always cast correctly to void *
2359 * (depends on memory and segmentation model for instance),
2360 * so they coerce to NULL.
2364 #if defined(DUK_USE_FASTINT)
2365 case DUK_TAG_FASTINT
:
2373 duk_push_pointer(ctx
, res
);
2374 duk_replace(ctx
, index
);
2378 DUK_EXTERNAL
void duk_to_object(duk_context
*ctx
, duk_idx_t index
) {
2379 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2381 duk_uint_t flags
= 0; /* shared flags for a subset of types */
2382 duk_small_int_t proto
= 0;
2384 DUK_ASSERT_CTX_VALID(ctx
);
2386 index
= duk_require_normalize_index(ctx
, index
);
2388 tv
= duk_require_tval(ctx
, index
);
2389 DUK_ASSERT(tv
!= NULL
);
2391 switch (DUK_TVAL_GET_TAG(tv
)) {
2392 case DUK_TAG_UNDEFINED
:
2393 case DUK_TAG_NULL
: {
2394 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_OBJECT_COERCIBLE
);
2397 case DUK_TAG_BOOLEAN
: {
2398 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2399 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN
);
2400 proto
= DUK_BIDX_BOOLEAN_PROTOTYPE
;
2403 case DUK_TAG_STRING
: {
2404 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2405 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ
|
2406 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING
);
2407 proto
= DUK_BIDX_STRING_PROTOTYPE
;
2410 case DUK_TAG_OBJECT
: {
2414 case DUK_TAG_BUFFER
: {
2415 /* A plain buffer coerces to a Duktape.Buffer because it's the
2416 * object counterpart of the plain buffer value. But it might
2417 * still make more sense to produce an ArrayBuffer here?
2420 duk_hbufferobject
*h_bufobj
;
2423 h_val
= DUK_TVAL_GET_BUFFER(tv
);
2424 DUK_ASSERT(h_val
!= NULL
);
2426 h_bufobj
= duk_push_bufferobject_raw(ctx
,
2427 DUK_HOBJECT_FLAG_EXTENSIBLE
|
2428 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
2429 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER
),
2430 DUK_BIDX_BUFFER_PROTOTYPE
);
2431 DUK_ASSERT(h_bufobj
!= NULL
);
2432 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE((duk_hobject
*) h_bufobj
));
2433 DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject
*) h_bufobj
));
2435 h_bufobj
->buf
= h_val
;
2436 DUK_HBUFFER_INCREF(thr
, h_val
);
2437 DUK_ASSERT(h_bufobj
->offset
== 0);
2438 h_bufobj
->length
= (duk_uint_t
) DUK_HBUFFER_GET_SIZE(h_val
);
2439 DUK_ASSERT(h_bufobj
->shift
== 0);
2440 DUK_ASSERT(h_bufobj
->elem_type
== DUK_HBUFFEROBJECT_ELEM_UINT8
);
2442 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
2445 case DUK_TAG_POINTER
: {
2446 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2447 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER
);
2448 proto
= DUK_BIDX_POINTER_PROTOTYPE
;
2451 case DUK_TAG_LIGHTFUNC
: {
2452 /* Lightfunc coerces to a Function instance with concrete
2453 * properties. Since 'length' is virtual for Duktape/C
2454 * functions, don't need to define that.
2456 * The result is made extensible to mimic what happens to
2458 * > Object.isExtensible(Object('foo'))
2461 duk_small_uint_t lf_flags
;
2462 duk_small_uint_t nargs
;
2463 duk_small_uint_t lf_len
;
2464 duk_c_function func
;
2465 duk_hnativefunction
*nf
;
2467 DUK_TVAL_GET_LIGHTFUNC(tv
, func
, lf_flags
);
2469 nargs
= DUK_LFUNC_FLAGS_GET_NARGS(lf_flags
);
2470 if (nargs
== DUK_LFUNC_NARGS_VARARGS
) {
2471 nargs
= DUK_VARARGS
;
2473 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2474 DUK_HOBJECT_FLAG_CONSTRUCTABLE
|
2475 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
2476 DUK_HOBJECT_FLAG_NEWENV
|
2477 DUK_HOBJECT_FLAG_STRICT
|
2478 DUK_HOBJECT_FLAG_NOTAIL
|
2479 /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */
2480 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
2481 (void) duk__push_c_function_raw(ctx
, func
, (duk_idx_t
) nargs
, flags
);
2483 lf_len
= DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags
);
2484 if (lf_len
!= nargs
) {
2485 /* Explicit length is only needed if it differs from 'nargs'. */
2486 duk_push_int(ctx
, (duk_int_t
) lf_len
);
2487 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LENGTH
, DUK_PROPDESC_FLAGS_NONE
);
2489 duk_push_lightfunc_name(ctx
, tv
);
2490 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_NAME
, DUK_PROPDESC_FLAGS_NONE
);
2492 nf
= duk_get_hnativefunction(ctx
, -1);
2493 DUK_ASSERT(nf
!= NULL
);
2494 nf
->magic
= (duk_int16_t
) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags
);
2496 /* Enable DUKFUNC exotic behavior once properties are set up. */
2497 DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject
*) nf
);
2500 #if defined(DUK_USE_FASTINT)
2501 case DUK_TAG_FASTINT
:
2504 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
2505 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER
);
2506 proto
= DUK_BIDX_NUMBER_PROTOTYPE
;
2513 (void) duk_push_object_helper(ctx
, flags
, proto
);
2515 /* Note: Boolean prototype's internal value property is not writable,
2516 * but duk_xdef_prop_stridx() disregards the write protection. Boolean
2517 * instances are immutable.
2519 * String and buffer special behaviors are already enabled which is not
2520 * ideal, but a write to the internal value is not affected by them.
2522 duk_dup(ctx
, index
);
2523 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_INT_VALUE
, DUK_PROPDESC_FLAGS_NONE
);
2526 duk_replace(ctx
, index
);
2533 DUK_LOCAL duk_bool_t
duk__tag_check(duk_context
*ctx
, duk_idx_t index
, duk_small_uint_t tag
) {
2536 tv
= duk_get_tval(ctx
, index
);
2540 return (DUK_TVAL_GET_TAG(tv
) == tag
);
2543 DUK_LOCAL duk_bool_t
duk__obj_flag_any_default_false(duk_context
*ctx
, duk_idx_t index
, duk_uint_t flag_mask
) {
2546 DUK_ASSERT_CTX_VALID(ctx
);
2548 obj
= duk_get_hobject(ctx
, index
);
2550 return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr
*) obj
, flag_mask
) ? 1 : 0);
2555 DUK_EXTERNAL duk_int_t
duk_get_type(duk_context
*ctx
, duk_idx_t index
) {
2558 DUK_ASSERT_CTX_VALID(ctx
);
2560 tv
= duk_get_tval(ctx
, index
);
2562 return DUK_TYPE_NONE
;
2564 switch (DUK_TVAL_GET_TAG(tv
)) {
2565 case DUK_TAG_UNDEFINED
:
2566 return DUK_TYPE_UNDEFINED
;
2568 return DUK_TYPE_NULL
;
2569 case DUK_TAG_BOOLEAN
:
2570 return DUK_TYPE_BOOLEAN
;
2571 case DUK_TAG_STRING
:
2572 return DUK_TYPE_STRING
;
2573 case DUK_TAG_OBJECT
:
2574 return DUK_TYPE_OBJECT
;
2575 case DUK_TAG_BUFFER
:
2576 return DUK_TYPE_BUFFER
;
2577 case DUK_TAG_POINTER
:
2578 return DUK_TYPE_POINTER
;
2579 case DUK_TAG_LIGHTFUNC
:
2580 return DUK_TYPE_LIGHTFUNC
;
2581 #if defined(DUK_USE_FASTINT)
2582 case DUK_TAG_FASTINT
:
2585 /* Note: number has no explicit tag (in 8-byte representation) */
2586 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2587 return DUK_TYPE_NUMBER
;
2592 DUK_EXTERNAL duk_bool_t
duk_check_type(duk_context
*ctx
, duk_idx_t index
, duk_int_t type
) {
2593 DUK_ASSERT_CTX_VALID(ctx
);
2595 return (duk_get_type(ctx
, index
) == type
) ? 1 : 0;
2598 DUK_EXTERNAL duk_uint_t
duk_get_type_mask(duk_context
*ctx
, duk_idx_t index
) {
2601 DUK_ASSERT_CTX_VALID(ctx
);
2603 tv
= duk_get_tval(ctx
, index
);
2605 return DUK_TYPE_MASK_NONE
;
2607 switch (DUK_TVAL_GET_TAG(tv
)) {
2608 case DUK_TAG_UNDEFINED
:
2609 return DUK_TYPE_MASK_UNDEFINED
;
2611 return DUK_TYPE_MASK_NULL
;
2612 case DUK_TAG_BOOLEAN
:
2613 return DUK_TYPE_MASK_BOOLEAN
;
2614 case DUK_TAG_STRING
:
2615 return DUK_TYPE_MASK_STRING
;
2616 case DUK_TAG_OBJECT
:
2617 return DUK_TYPE_MASK_OBJECT
;
2618 case DUK_TAG_BUFFER
:
2619 return DUK_TYPE_MASK_BUFFER
;
2620 case DUK_TAG_POINTER
:
2621 return DUK_TYPE_MASK_POINTER
;
2622 case DUK_TAG_LIGHTFUNC
:
2623 return DUK_TYPE_MASK_LIGHTFUNC
;
2624 #if defined(DUK_USE_FASTINT)
2625 case DUK_TAG_FASTINT
:
2628 /* Note: number has no explicit tag (in 8-byte representation) */
2629 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv
));
2630 return DUK_TYPE_MASK_NUMBER
;
2635 DUK_EXTERNAL duk_bool_t
duk_check_type_mask(duk_context
*ctx
, duk_idx_t index
, duk_uint_t mask
) {
2636 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2638 DUK_ASSERT_CTX_VALID(ctx
);
2640 if (duk_get_type_mask(ctx
, index
) & mask
) {
2643 if (mask
& DUK_TYPE_MASK_THROW
) {
2644 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_UNEXPECTED_TYPE
);
2650 DUK_EXTERNAL duk_bool_t
duk_is_undefined(duk_context
*ctx
, duk_idx_t index
) {
2651 DUK_ASSERT_CTX_VALID(ctx
);
2652 return duk__tag_check(ctx
, index
, DUK_TAG_UNDEFINED
);
2655 DUK_EXTERNAL duk_bool_t
duk_is_null(duk_context
*ctx
, duk_idx_t index
) {
2656 DUK_ASSERT_CTX_VALID(ctx
);
2657 return duk__tag_check(ctx
, index
, DUK_TAG_NULL
);
2660 DUK_EXTERNAL duk_bool_t
duk_is_null_or_undefined(duk_context
*ctx
, duk_idx_t index
) {
2662 duk_small_uint_t tag
;
2664 DUK_ASSERT_CTX_VALID(ctx
);
2666 tv
= duk_get_tval(ctx
, index
);
2670 tag
= DUK_TVAL_GET_TAG(tv
);
2671 return (tag
== DUK_TAG_UNDEFINED
) || (tag
== DUK_TAG_NULL
);
2674 DUK_EXTERNAL duk_bool_t
duk_is_boolean(duk_context
*ctx
, duk_idx_t index
) {
2675 DUK_ASSERT_CTX_VALID(ctx
);
2676 return duk__tag_check(ctx
, index
, DUK_TAG_BOOLEAN
);
2679 DUK_EXTERNAL duk_bool_t
duk_is_number(duk_context
*ctx
, duk_idx_t index
) {
2682 DUK_ASSERT_CTX_VALID(ctx
);
2685 * Number is special because it doesn't have a specific
2686 * tag in the 8-byte representation.
2689 /* XXX: shorter version for 12-byte representation? */
2691 tv
= duk_get_tval(ctx
, index
);
2695 return DUK_TVAL_IS_NUMBER(tv
);
2698 DUK_EXTERNAL duk_bool_t
duk_is_nan(duk_context
*ctx
, duk_idx_t index
) {
2699 /* XXX: This will now return false for non-numbers, even though they would
2700 * coerce to NaN (as a general rule). In particular, duk_get_number()
2701 * returns a NaN for non-numbers, so should this function also return
2702 * true for non-numbers?
2707 DUK_ASSERT_CTX_VALID(ctx
);
2709 tv
= duk_get_tval(ctx
, index
);
2710 if (!tv
|| !DUK_TVAL_IS_NUMBER(tv
)) {
2713 return DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv
));
2716 DUK_EXTERNAL duk_bool_t
duk_is_string(duk_context
*ctx
, duk_idx_t index
) {
2717 DUK_ASSERT_CTX_VALID(ctx
);
2718 return duk__tag_check(ctx
, index
, DUK_TAG_STRING
);
2721 DUK_EXTERNAL duk_bool_t
duk_is_object(duk_context
*ctx
, duk_idx_t index
) {
2722 DUK_ASSERT_CTX_VALID(ctx
);
2723 return duk__tag_check(ctx
, index
, DUK_TAG_OBJECT
);
2726 DUK_EXTERNAL duk_bool_t
duk_is_buffer(duk_context
*ctx
, duk_idx_t index
) {
2727 DUK_ASSERT_CTX_VALID(ctx
);
2728 return duk__tag_check(ctx
, index
, DUK_TAG_BUFFER
);
2731 DUK_EXTERNAL duk_bool_t
duk_is_pointer(duk_context
*ctx
, duk_idx_t index
) {
2732 DUK_ASSERT_CTX_VALID(ctx
);
2733 return duk__tag_check(ctx
, index
, DUK_TAG_POINTER
);
2736 DUK_EXTERNAL duk_bool_t
duk_is_lightfunc(duk_context
*ctx
, duk_idx_t index
) {
2737 DUK_ASSERT_CTX_VALID(ctx
);
2738 return duk__tag_check(ctx
, index
, DUK_TAG_LIGHTFUNC
);
2741 DUK_EXTERNAL duk_bool_t
duk_is_array(duk_context
*ctx
, duk_idx_t index
) {
2744 DUK_ASSERT_CTX_VALID(ctx
);
2746 obj
= duk_get_hobject(ctx
, index
);
2748 return (DUK_HOBJECT_GET_CLASS_NUMBER(obj
) == DUK_HOBJECT_CLASS_ARRAY
? 1 : 0);
2753 DUK_EXTERNAL duk_bool_t
duk_is_function(duk_context
*ctx
, duk_idx_t index
) {
2756 DUK_ASSERT_CTX_VALID(ctx
);
2758 tv
= duk_get_tval(ctx
, index
);
2759 if (tv
&& DUK_TVAL_IS_LIGHTFUNC(tv
)) {
2762 return duk__obj_flag_any_default_false(ctx
,
2764 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
|
2765 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
2766 DUK_HOBJECT_FLAG_BOUND
);
2769 DUK_EXTERNAL duk_bool_t
duk_is_c_function(duk_context
*ctx
, duk_idx_t index
) {
2770 DUK_ASSERT_CTX_VALID(ctx
);
2771 return duk__obj_flag_any_default_false(ctx
,
2773 DUK_HOBJECT_FLAG_NATIVEFUNCTION
);
2776 DUK_EXTERNAL duk_bool_t
duk_is_ecmascript_function(duk_context
*ctx
, duk_idx_t index
) {
2777 DUK_ASSERT_CTX_VALID(ctx
);
2778 return duk__obj_flag_any_default_false(ctx
,
2780 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
);
2783 DUK_EXTERNAL duk_bool_t
duk_is_bound_function(duk_context
*ctx
, duk_idx_t index
) {
2784 DUK_ASSERT_CTX_VALID(ctx
);
2785 return duk__obj_flag_any_default_false(ctx
,
2787 DUK_HOBJECT_FLAG_BOUND
);
2790 DUK_EXTERNAL duk_bool_t
duk_is_thread(duk_context
*ctx
, duk_idx_t index
) {
2791 DUK_ASSERT_CTX_VALID(ctx
);
2792 return duk__obj_flag_any_default_false(ctx
,
2794 DUK_HOBJECT_FLAG_THREAD
);
2797 DUK_EXTERNAL duk_bool_t
duk_is_callable(duk_context
*ctx
, duk_idx_t index
) {
2798 /* XXX: currently same as duk_is_function() */
2799 DUK_ASSERT_CTX_VALID(ctx
);
2800 return duk_is_function(ctx
, index
);
2803 DUK_EXTERNAL duk_bool_t
duk_is_fixed_buffer(duk_context
*ctx
, duk_idx_t index
) {
2806 DUK_ASSERT_CTX_VALID(ctx
);
2808 tv
= duk_get_tval(ctx
, index
);
2809 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
2810 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2811 DUK_ASSERT(h
!= NULL
);
2812 return (DUK_HBUFFER_HAS_DYNAMIC(h
) ? 0 : 1);
2817 DUK_EXTERNAL duk_bool_t
duk_is_dynamic_buffer(duk_context
*ctx
, duk_idx_t index
) {
2820 DUK_ASSERT_CTX_VALID(ctx
);
2822 tv
= duk_get_tval(ctx
, index
);
2823 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
2824 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2825 DUK_ASSERT(h
!= NULL
);
2826 return (DUK_HBUFFER_HAS_DYNAMIC(h
) && !DUK_HBUFFER_HAS_EXTERNAL(h
) ? 1 : 0);
2831 DUK_EXTERNAL duk_bool_t
duk_is_external_buffer(duk_context
*ctx
, duk_idx_t index
) {
2834 DUK_ASSERT_CTX_VALID(ctx
);
2836 tv
= duk_get_tval(ctx
, index
);
2837 if (tv
&& DUK_TVAL_IS_BUFFER(tv
)) {
2838 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
2839 DUK_ASSERT(h
!= NULL
);
2840 return (DUK_HBUFFER_HAS_DYNAMIC(h
) && DUK_HBUFFER_HAS_EXTERNAL(h
) ? 1 : 0);
2845 DUK_EXTERNAL duk_errcode_t
duk_get_error_code(duk_context
*ctx
, duk_idx_t index
) {
2846 duk_hthread
*thr
= (duk_hthread
*) ctx
;
2850 DUK_ASSERT_CTX_VALID(ctx
);
2852 h
= duk_get_hobject(ctx
, index
);
2854 sanity
= DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY
;
2857 return DUK_ERR_NONE
;
2859 if (h
== thr
->builtins
[DUK_BIDX_EVAL_ERROR_PROTOTYPE
]) {
2860 return DUK_ERR_EVAL_ERROR
;
2862 if (h
== thr
->builtins
[DUK_BIDX_RANGE_ERROR_PROTOTYPE
]) {
2863 return DUK_ERR_RANGE_ERROR
;
2865 if (h
== thr
->builtins
[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE
]) {
2866 return DUK_ERR_REFERENCE_ERROR
;
2868 if (h
== thr
->builtins
[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE
]) {
2869 return DUK_ERR_SYNTAX_ERROR
;
2871 if (h
== thr
->builtins
[DUK_BIDX_TYPE_ERROR_PROTOTYPE
]) {
2872 return DUK_ERR_TYPE_ERROR
;
2874 if (h
== thr
->builtins
[DUK_BIDX_URI_ERROR_PROTOTYPE
]) {
2875 return DUK_ERR_URI_ERROR
;
2877 if (h
== thr
->builtins
[DUK_BIDX_ERROR_PROTOTYPE
]) {
2878 return DUK_ERR_ERROR
;
2881 h
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
);
2882 } while (--sanity
> 0);
2884 return DUK_ERR_NONE
;
2891 DUK_INTERNAL
void duk_push_tval(duk_context
*ctx
, duk_tval
*tv
) {
2895 DUK_ASSERT_CTX_VALID(ctx
);
2896 DUK_ASSERT(tv
!= NULL
);
2897 thr
= (duk_hthread
*) ctx
;
2899 tv_slot
= thr
->valstack_top
++;
2900 DUK_TVAL_SET_TVAL(tv_slot
, tv
);
2901 DUK_TVAL_INCREF(thr
, tv
); /* no side effects */
2904 #if defined(DUK_USE_DEBUGGER_SUPPORT)
2905 /* Right now only needed by the debugger. */
2906 DUK_INTERNAL
void duk_push_unused(duk_context
*ctx
) {
2910 DUK_ASSERT_CTX_VALID(ctx
);
2911 thr
= (duk_hthread
*) ctx
;
2913 tv_slot
= thr
->valstack_top
++;
2914 DUK_TVAL_SET_UNDEFINED_UNUSED(tv_slot
);
2918 DUK_EXTERNAL
void duk_push_undefined(duk_context
*ctx
) {
2922 DUK_ASSERT_CTX_VALID(ctx
);
2923 thr
= (duk_hthread
*) ctx
;
2925 tv_slot
= thr
->valstack_top
++;
2926 DUK_TVAL_SET_UNDEFINED_ACTUAL(tv_slot
);
2929 DUK_EXTERNAL
void duk_push_null(duk_context
*ctx
) {
2933 DUK_ASSERT_CTX_VALID(ctx
);
2934 thr
= (duk_hthread
*) ctx
;
2936 tv_slot
= thr
->valstack_top
++;
2937 DUK_TVAL_SET_NULL(tv_slot
);
2940 DUK_EXTERNAL
void duk_push_boolean(duk_context
*ctx
, duk_bool_t val
) {
2945 DUK_ASSERT_CTX_VALID(ctx
);
2946 thr
= (duk_hthread
*) ctx
;
2948 b
= (val
? 1 : 0); /* ensure value is 1 or 0 (not other non-zero) */
2949 tv_slot
= thr
->valstack_top
++;
2950 DUK_TVAL_SET_BOOLEAN(tv_slot
, b
);
2953 DUK_EXTERNAL
void duk_push_true(duk_context
*ctx
) {
2957 DUK_ASSERT_CTX_VALID(ctx
);
2958 thr
= (duk_hthread
*) ctx
;
2960 tv_slot
= thr
->valstack_top
++;
2961 DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot
);
2964 DUK_EXTERNAL
void duk_push_false(duk_context
*ctx
) {
2968 DUK_ASSERT_CTX_VALID(ctx
);
2969 thr
= (duk_hthread
*) ctx
;
2971 tv_slot
= thr
->valstack_top
++;
2972 DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot
);
2975 /* normalize NaN which may not match our canonical internal NaN */
2976 DUK_EXTERNAL
void duk_push_number(duk_context
*ctx
, duk_double_t val
) {
2979 duk_double_union du
;
2981 DUK_ASSERT_CTX_VALID(ctx
);
2982 thr
= (duk_hthread
*) ctx
;
2985 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du
);
2986 tv_slot
= thr
->valstack_top
++;
2987 DUK_TVAL_SET_NUMBER(tv_slot
, du
.d
);
2990 DUK_EXTERNAL
void duk_push_int(duk_context
*ctx
, duk_int_t val
) {
2991 #if defined(DUK_USE_FASTINT)
2995 DUK_ASSERT_CTX_VALID(ctx
);
2996 thr
= (duk_hthread
*) ctx
;
2998 tv_slot
= thr
->valstack_top
++;
2999 #if DUK_INT_MAX <= 0x7fffffffL
3000 DUK_TVAL_SET_FASTINT_I32(tv_slot
, (duk_int32_t
) val
);
3002 if (val
>= DUK_FASTINT_MIN
&& val
<= DUK_FASTINT_MAX
) {
3003 DUK_TVAL_SET_FASTINT(tv_slot
, (duk_int64_t
) val
);
3005 duk_double_t
= (duk_double_t
) val
;
3006 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3009 #else /* DUK_USE_FASTINT */
3014 DUK_ASSERT_CTX_VALID(ctx
);
3015 thr
= (duk_hthread
*) ctx
;
3017 d
= (duk_double_t
) val
;
3018 tv_slot
= thr
->valstack_top
++;
3019 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3020 #endif /* DUK_USE_FASTINT */
3023 DUK_EXTERNAL
void duk_push_uint(duk_context
*ctx
, duk_uint_t val
) {
3024 #if defined(DUK_USE_FASTINT)
3028 DUK_ASSERT_CTX_VALID(ctx
);
3029 thr
= (duk_hthread
*) ctx
;
3031 tv_slot
= thr
->valstack_top
++;
3032 #if DUK_UINT_MAX <= 0xffffffffUL
3033 DUK_TVAL_SET_FASTINT_U32(tv_slot
, (duk_uint32_t
) val
);
3035 if (val
<= DUK_FASTINT_MAX
) { /* val is unsigned so >= 0 */
3036 /* XXX: take advantage of val being unsigned, no need to mask */
3037 DUK_TVAL_SET_FASTINT(tv_slot
, (duk_int64_t
) val
);
3039 duk_double_t
= (duk_double_t
) val
;
3040 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3043 #else /* DUK_USE_FASTINT */
3048 DUK_ASSERT_CTX_VALID(ctx
);
3049 thr
= (duk_hthread
*) ctx
;
3051 d
= (duk_double_t
) val
;
3052 tv_slot
= thr
->valstack_top
++;
3053 DUK_TVAL_SET_NUMBER(tv_slot
, d
);
3054 #endif /* DUK_USE_FASTINT */
3057 DUK_EXTERNAL
void duk_push_nan(duk_context
*ctx
) {
3060 duk_double_union du
;
3062 DUK_ASSERT_CTX_VALID(ctx
);
3063 thr
= (duk_hthread
*) ctx
;
3065 DUK_DBLUNION_SET_NAN(&du
);
3066 DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du
));
3067 tv_slot
= thr
->valstack_top
++;
3068 DUK_TVAL_SET_NUMBER(tv_slot
, du
.d
);
3071 DUK_EXTERNAL
const char *duk_push_lstring(duk_context
*ctx
, const char *str
, duk_size_t len
) {
3072 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3076 DUK_ASSERT_CTX_VALID(ctx
);
3078 /* check stack before interning (avoid hanging temp) */
3079 if (thr
->valstack_top
>= thr
->valstack_end
) {
3080 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3083 /* NULL with zero length represents an empty string; NULL with higher
3084 * length is also now trated like an empty string although it is
3085 * a bit dubious. This is unlike duk_push_string() which pushes a
3086 * 'null' if the input string is a NULL.
3092 /* Check for maximum string length */
3093 if (len
> DUK_HSTRING_MAX_BYTELEN
) {
3094 DUK_ERROR(thr
, DUK_ERR_RANGE_ERROR
, DUK_STR_STRING_TOO_LONG
);
3097 h
= duk_heap_string_intern_checked(thr
, (duk_uint8_t
*) str
, (duk_uint32_t
) len
);
3098 DUK_ASSERT(h
!= NULL
);
3100 tv_slot
= thr
->valstack_top
++;
3101 DUK_TVAL_SET_STRING(tv_slot
, h
);
3102 DUK_HSTRING_INCREF(thr
, h
); /* no side effects */
3104 return (const char *) DUK_HSTRING_GET_DATA(h
);
3107 DUK_EXTERNAL
const char *duk_push_string(duk_context
*ctx
, const char *str
) {
3108 DUK_ASSERT_CTX_VALID(ctx
);
3111 return duk_push_lstring(ctx
, str
, DUK_STRLEN(str
));
3118 #ifdef DUK_USE_FILE_IO
3119 /* This is a bit clunky because it is ANSI C portable. Should perhaps
3120 * relocate to another file because this is potentially platform
3123 DUK_EXTERNAL
const char *duk_push_string_file_raw(duk_context
*ctx
, const char *path
, duk_uint_t flags
) {
3124 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3127 long sz
; /* ANSI C typing */
3129 DUK_ASSERT_CTX_VALID(ctx
);
3134 f
= DUK_FOPEN(path
, "rb");
3138 if (DUK_FSEEK(f
, 0, SEEK_END
) < 0) {
3145 if (DUK_FSEEK(f
, 0, SEEK_SET
) < 0) {
3148 buf
= (char *) duk_push_fixed_buffer(ctx
, (duk_size_t
) sz
);
3149 DUK_ASSERT(buf
!= NULL
);
3150 if ((duk_size_t
) DUK_FREAD(buf
, 1, (size_t) sz
, f
) != (duk_size_t
) sz
) {
3153 (void) DUK_FCLOSE(f
); /* ignore fclose() error */
3155 return duk_to_string(ctx
, -1);
3163 DUK_ASSERT(flags
== DUK_STRING_PUSH_SAFE
); /* only flag now */
3164 duk_push_undefined(ctx
);
3166 /* XXX: string not shared because it is conditional */
3167 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, "read file error");
3172 DUK_EXTERNAL
const char *duk_push_string_file_raw(duk_context
*ctx
, const char *path
, duk_uint_t flags
) {
3173 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3174 DUK_ASSERT_CTX_VALID(ctx
);
3178 DUK_ASSERT(flags
== DUK_STRING_PUSH_SAFE
); /* only flag now */
3179 duk_push_undefined(ctx
);
3181 /* XXX: string not shared because it is conditional */
3182 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, "file I/O disabled");
3186 #endif /* DUK_USE_FILE_IO */
3188 DUK_EXTERNAL
void duk_push_pointer(duk_context
*ctx
, void *val
) {
3192 DUK_ASSERT_CTX_VALID(ctx
);
3193 thr
= (duk_hthread
*) ctx
;
3195 tv_slot
= thr
->valstack_top
++;
3196 DUK_TVAL_SET_POINTER(tv_slot
, val
);
3199 #define DUK__PUSH_THIS_FLAG_CHECK_COERC (1 << 0)
3200 #define DUK__PUSH_THIS_FLAG_TO_OBJECT (1 << 1)
3201 #define DUK__PUSH_THIS_FLAG_TO_STRING (1 << 2)
3203 DUK_LOCAL
void duk__push_this_helper(duk_context
*ctx
, duk_small_uint_t flags
) {
3204 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3206 DUK_ASSERT(thr
!= NULL
);
3207 DUK_ASSERT_CTX_VALID(ctx
);
3208 DUK_ASSERT_DISABLE(thr
->callstack_top
>= 0); /* avoid warning (unsigned) */
3209 DUK_ASSERT(thr
->callstack_top
<= thr
->callstack_size
);
3211 if (thr
->callstack_top
== 0) {
3212 if (flags
& DUK__PUSH_THIS_FLAG_CHECK_COERC
) {
3215 duk_push_undefined(ctx
);
3220 /* 'this' binding is just before current activation's bottom */
3221 DUK_ASSERT(thr
->valstack_bottom
> thr
->valstack
);
3222 tv
= thr
->valstack_bottom
- 1;
3223 if (flags
& DUK__PUSH_THIS_FLAG_CHECK_COERC
) {
3224 if (DUK_TVAL_IS_UNDEFINED(tv
) || DUK_TVAL_IS_NULL(tv
)) {
3229 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
3230 duk_push_tval(ctx
, &tv_tmp
);
3233 if (flags
& DUK__PUSH_THIS_FLAG_TO_OBJECT
) {
3234 duk_to_object(ctx
, -1);
3235 } else if (flags
& DUK__PUSH_THIS_FLAG_TO_STRING
) {
3236 duk_to_string(ctx
, -1);
3242 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_NOT_OBJECT_COERCIBLE
);
3245 DUK_EXTERNAL
void duk_push_this(duk_context
*ctx
) {
3246 DUK_ASSERT_CTX_VALID(ctx
);
3248 duk__push_this_helper(ctx
, 0 /*flags*/);
3251 DUK_INTERNAL
void duk_push_this_check_object_coercible(duk_context
*ctx
) {
3252 DUK_ASSERT_CTX_VALID(ctx
);
3254 duk__push_this_helper(ctx
, DUK__PUSH_THIS_FLAG_CHECK_COERC
/*flags*/);
3257 DUK_INTERNAL duk_hobject
*duk_push_this_coercible_to_object(duk_context
*ctx
) {
3260 DUK_ASSERT_CTX_VALID(ctx
);
3262 duk__push_this_helper(ctx
, DUK__PUSH_THIS_FLAG_CHECK_COERC
|
3263 DUK__PUSH_THIS_FLAG_TO_OBJECT
/*flags*/);
3264 h
= duk_get_hobject(ctx
, -1);
3265 DUK_ASSERT(h
!= NULL
);
3269 DUK_INTERNAL duk_hstring
*duk_push_this_coercible_to_string(duk_context
*ctx
) {
3272 DUK_ASSERT_CTX_VALID(ctx
);
3274 duk__push_this_helper(ctx
, DUK__PUSH_THIS_FLAG_CHECK_COERC
|
3275 DUK__PUSH_THIS_FLAG_TO_STRING
/*flags*/);
3276 h
= duk_get_hstring(ctx
, -1);
3277 DUK_ASSERT(h
!= NULL
);
3281 DUK_INTERNAL duk_tval
*duk_get_borrowed_this_tval(duk_context
*ctx
) {
3284 DUK_ASSERT(ctx
!= NULL
);
3285 thr
= (duk_hthread
*) ctx
;
3287 DUK_ASSERT(thr
->callstack_top
> 0); /* caller required to know */
3288 DUK_ASSERT(thr
->valstack_bottom
> thr
->valstack
); /* consequence of above */
3289 DUK_ASSERT(thr
->valstack_bottom
- 1 >= thr
->valstack
); /* 'this' binding exists */
3291 return thr
->valstack_bottom
- 1;
3294 DUK_EXTERNAL
void duk_push_current_function(duk_context
*ctx
) {
3295 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3296 duk_activation
*act
;
3298 DUK_ASSERT_CTX_VALID(ctx
);
3299 DUK_ASSERT(thr
!= NULL
);
3300 DUK_ASSERT_DISABLE(thr
->callstack_top
>= 0);
3301 DUK_ASSERT(thr
->callstack_top
<= thr
->callstack_size
);
3303 act
= duk_hthread_get_current_activation(thr
);
3305 duk_push_tval(ctx
, &act
->tv_func
);
3307 duk_push_undefined(ctx
);
3311 DUK_EXTERNAL
void duk_push_current_thread(duk_context
*ctx
) {
3312 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3314 DUK_ASSERT_CTX_VALID(ctx
);
3315 DUK_ASSERT(thr
!= NULL
);
3317 if (thr
->heap
->curr_thread
) {
3318 duk_push_hobject(ctx
, (duk_hobject
*) thr
->heap
->curr_thread
);
3320 duk_push_undefined(ctx
);
3324 DUK_EXTERNAL
void duk_push_global_object(duk_context
*ctx
) {
3325 DUK_ASSERT_CTX_VALID(ctx
);
3327 duk_push_hobject_bidx(ctx
, DUK_BIDX_GLOBAL
);
3330 /* XXX: size optimize */
3331 DUK_LOCAL
void duk__push_stash(duk_context
*ctx
) {
3332 DUK_ASSERT_CTX_VALID(ctx
);
3333 if (!duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_VALUE
)) {
3334 DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use"));
3336 duk_push_object_internal(ctx
);
3338 duk_xdef_prop_stridx(ctx
, -3, DUK_STRIDX_INT_VALUE
, DUK_PROPDESC_FLAGS_C
); /* [ ... parent stash stash ] -> [ ... parent stash ] */
3340 duk_remove(ctx
, -2);
3343 DUK_EXTERNAL
void duk_push_heap_stash(duk_context
*ctx
) {
3344 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3346 DUK_ASSERT_CTX_VALID(ctx
);
3348 DUK_ASSERT(heap
->heap_object
!= NULL
);
3349 duk_push_hobject(ctx
, heap
->heap_object
);
3350 duk__push_stash(ctx
);
3353 DUK_EXTERNAL
void duk_push_global_stash(duk_context
*ctx
) {
3354 DUK_ASSERT_CTX_VALID(ctx
);
3355 duk_push_global_object(ctx
);
3356 duk__push_stash(ctx
);
3359 DUK_EXTERNAL
void duk_push_thread_stash(duk_context
*ctx
, duk_context
*target_ctx
) {
3360 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3361 DUK_ASSERT_CTX_VALID(ctx
);
3363 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
3364 return; /* not reached */
3366 duk_push_hobject(ctx
, (duk_hobject
*) target_ctx
);
3367 duk__push_stash(ctx
);
3370 /* XXX: duk_ssize_t would be useful here */
3371 DUK_LOCAL duk_int_t
duk__try_push_vsprintf(duk_context
*ctx
, void *buf
, duk_size_t sz
, const char *fmt
, va_list ap
) {
3374 DUK_ASSERT_CTX_VALID(ctx
);
3377 /* NUL terminator handling doesn't matter here */
3378 len
= DUK_VSNPRINTF((char *) buf
, sz
, fmt
, ap
);
3379 if (len
< (duk_int_t
) sz
) {
3380 /* Return value of 'sz' or more indicates output was (potentially)
3383 return (duk_int_t
) len
;
3388 DUK_EXTERNAL
const char *duk_push_vsprintf(duk_context
*ctx
, const char *fmt
, va_list ap
) {
3389 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3390 duk_uint8_t stack_buf
[DUK_PUSH_SPRINTF_INITIAL_SIZE
];
3391 duk_size_t sz
= DUK_PUSH_SPRINTF_INITIAL_SIZE
;
3392 duk_bool_t pushed_buf
= 0;
3394 duk_int_t len
; /* XXX: duk_ssize_t */
3397 DUK_ASSERT_CTX_VALID(ctx
);
3399 /* special handling of fmt==NULL */
3402 duk_push_hstring_stridx(ctx
, DUK_STRIDX_EMPTY_STRING
);
3403 h_str
= DUK_HTHREAD_STRING_EMPTY_STRING(thr
); /* rely on interning, must be this string */
3404 return (const char *) DUK_HSTRING_GET_DATA(h_str
);
3407 /* initial estimate based on format string */
3408 sz
= DUK_STRLEN(fmt
) + 16; /* format plus something to avoid just missing */
3409 if (sz
< DUK_PUSH_SPRINTF_INITIAL_SIZE
) {
3410 sz
= DUK_PUSH_SPRINTF_INITIAL_SIZE
;
3414 /* Try to make do with a stack buffer to avoid allocating a temporary buffer.
3415 * This works 99% of the time which is quite nice.
3418 va_list ap_copy
; /* copied so that 'ap' can be reused */
3420 if (sz
<= sizeof(stack_buf
)) {
3422 } else if (!pushed_buf
) {
3424 buf
= duk_push_dynamic_buffer(ctx
, sz
);
3426 buf
= duk_resize_buffer(ctx
, -1, sz
);
3428 DUK_ASSERT(buf
!= NULL
);
3430 DUK_VA_COPY(ap_copy
, ap
);
3431 len
= duk__try_push_vsprintf(ctx
, buf
, sz
, fmt
, ap_copy
);
3437 /* failed, resize and try again */
3439 if (sz
>= DUK_PUSH_SPRINTF_SANITY_LIMIT
) {
3440 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_SPRINTF_TOO_LONG
);
3444 /* Cannot use duk_to_string() on the buffer because it is usually
3445 * larger than 'len'. Also, 'buf' is usually a stack buffer.
3447 res
= duk_push_lstring(ctx
, (const char *) buf
, (duk_size_t
) len
); /* [ buf? res ] */
3449 duk_remove(ctx
, -2);
3454 DUK_EXTERNAL
const char *duk_push_sprintf(duk_context
*ctx
, const char *fmt
, ...) {
3458 DUK_ASSERT_CTX_VALID(ctx
);
3460 /* allow fmt==NULL */
3462 ret
= duk_push_vsprintf(ctx
, fmt
, ap
);
3468 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
) {
3469 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3474 DUK_ASSERT_CTX_VALID(ctx
);
3475 DUK_ASSERT(prototype_bidx
== -1 ||
3476 (prototype_bidx
>= 0 && prototype_bidx
< DUK_NUM_BUILTINS
));
3478 /* check stack first */
3479 if (thr
->valstack_top
>= thr
->valstack_end
) {
3480 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3483 h
= duk_hobject_alloc(thr
->heap
, hobject_flags_and_class
);
3485 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
3488 DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h
->hdr
.h_flags
));
3490 tv_slot
= thr
->valstack_top
;
3491 DUK_TVAL_SET_OBJECT(tv_slot
, h
);
3492 DUK_HOBJECT_INCREF(thr
, h
); /* no side effects */
3493 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3494 thr
->valstack_top
++;
3496 /* object is now reachable */
3498 if (prototype_bidx
>= 0) {
3499 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, h
, thr
->builtins
[prototype_bidx
]);
3501 DUK_ASSERT(prototype_bidx
== -1);
3502 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
) == NULL
);
3508 DUK_INTERNAL duk_idx_t
duk_push_object_helper_proto(duk_context
*ctx
, duk_uint_t hobject_flags_and_class
, duk_hobject
*proto
) {
3509 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3513 DUK_ASSERT_CTX_VALID(ctx
);
3515 ret
= duk_push_object_helper(ctx
, hobject_flags_and_class
, -1);
3516 h
= duk_get_hobject(ctx
, -1);
3517 DUK_ASSERT(h
!= NULL
);
3518 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, h
) == NULL
);
3519 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, h
, proto
);
3523 DUK_EXTERNAL duk_idx_t
duk_push_object(duk_context
*ctx
) {
3524 DUK_ASSERT_CTX_VALID(ctx
);
3526 return duk_push_object_helper(ctx
,
3527 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3528 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT
),
3529 DUK_BIDX_OBJECT_PROTOTYPE
);
3532 DUK_EXTERNAL duk_idx_t
duk_push_array(duk_context
*ctx
) {
3533 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3537 DUK_ASSERT_CTX_VALID(ctx
);
3539 ret
= duk_push_object_helper(ctx
,
3540 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3541 DUK_HOBJECT_FLAG_ARRAY_PART
|
3542 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY
),
3543 DUK_BIDX_ARRAY_PROTOTYPE
);
3545 obj
= duk_require_hobject(ctx
, ret
);
3548 * An array must have a 'length' property (E5 Section 15.4.5.2).
3549 * The special array behavior flag must only be enabled once the
3550 * length property has been added.
3552 * The internal property must be a number (and preferably a
3553 * fastint if fastint support is enabled).
3556 duk_push_int(ctx
, 0);
3557 #if defined(DUK_USE_FASTINT)
3558 DUK_ASSERT(DUK_TVAL_IS_FASTINT(duk_require_tval(ctx
, -1)));
3561 duk_hobject_define_property_internal(thr
,
3563 DUK_HTHREAD_STRING_LENGTH(thr
),
3564 DUK_PROPDESC_FLAGS_W
);
3565 DUK_HOBJECT_SET_EXOTIC_ARRAY(obj
);
3570 DUK_EXTERNAL duk_idx_t
duk_push_thread_raw(duk_context
*ctx
, duk_uint_t flags
) {
3571 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3576 DUK_ASSERT_CTX_VALID(ctx
);
3578 /* check stack first */
3579 if (thr
->valstack_top
>= thr
->valstack_end
) {
3580 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3583 obj
= duk_hthread_alloc(thr
->heap
,
3584 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3585 DUK_HOBJECT_FLAG_THREAD
|
3586 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD
));
3588 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
3590 obj
->state
= DUK_HTHREAD_STATE_INACTIVE
;
3591 #if defined(DUK_USE_HEAPPTR16)
3592 obj
->strs16
= thr
->strs16
;
3594 obj
->strs
= thr
->strs
;
3596 DUK_DDD(DUK_DDDPRINT("created thread object with flags: 0x%08lx", (unsigned long) obj
->obj
.hdr
.h_flags
));
3598 /* make the new thread reachable */
3599 tv_slot
= thr
->valstack_top
;
3600 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3601 DUK_HTHREAD_INCREF(thr
, obj
);
3602 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3603 thr
->valstack_top
++;
3605 /* important to do this *after* pushing, to make the thread reachable for gc */
3606 if (!duk_hthread_init_stacks(thr
->heap
, obj
)) {
3607 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
3610 /* initialize built-ins - either by copying or creating new ones */
3611 if (flags
& DUK_THREAD_NEW_GLOBAL_ENV
) {
3612 duk_hthread_create_builtin_objects(obj
);
3614 duk_hthread_copy_builtin_objects(thr
, obj
);
3617 /* default prototype (Note: 'obj' must be reachable) */
3618 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, obj
->builtins
[DUK_BIDX_THREAD_PROTOTYPE
]);
3620 /* Initial stack size satisfies the stack spare constraints so there
3621 * is no need to require stack here.
3623 DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE
>=
3624 DUK_VALSTACK_API_ENTRY_MINIMUM
+ DUK_VALSTACK_INTERNAL_EXTRA
);
3629 DUK_INTERNAL duk_idx_t
duk_push_compiledfunction(duk_context
*ctx
) {
3630 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3631 duk_hcompiledfunction
*obj
;
3635 DUK_ASSERT_CTX_VALID(ctx
);
3637 /* check stack first */
3638 if (thr
->valstack_top
>= thr
->valstack_end
) {
3639 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3642 /* Template functions are not strictly constructable (they don't
3643 * have a "prototype" property for instance), so leave the
3644 * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here.
3647 obj
= duk_hcompiledfunction_alloc(thr
->heap
,
3648 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3649 DUK_HOBJECT_FLAG_COMPILEDFUNCTION
|
3650 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
));
3652 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
3655 DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj
->obj
.hdr
.h_flags
));
3657 tv_slot
= thr
->valstack_top
;
3658 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3659 DUK_HOBJECT_INCREF(thr
, obj
);
3660 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3661 thr
->valstack_top
++;
3663 /* default prototype (Note: 'obj' must be reachable) */
3664 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, thr
->builtins
[DUK_BIDX_FUNCTION_PROTOTYPE
]);
3669 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
) {
3670 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3671 duk_hnativefunction
*obj
;
3674 duk_uint16_t func_nargs
;
3676 DUK_ASSERT_CTX_VALID(ctx
);
3678 /* check stack first */
3679 if (thr
->valstack_top
>= thr
->valstack_end
) {
3680 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3685 if (nargs
>= 0 && nargs
< DUK_HNATIVEFUNCTION_NARGS_MAX
) {
3686 func_nargs
= (duk_uint16_t
) nargs
;
3687 } else if (nargs
== DUK_VARARGS
) {
3688 func_nargs
= DUK_HNATIVEFUNCTION_NARGS_VARARGS
;
3693 obj
= duk_hnativefunction_alloc(thr
->heap
, flags
);
3695 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
3699 obj
->nargs
= func_nargs
;
3701 DUK_DDD(DUK_DDDPRINT("created native function object with flags: 0x%08lx, nargs=%ld",
3702 (unsigned long) obj
->obj
.hdr
.h_flags
, (long) obj
->nargs
));
3704 tv_slot
= thr
->valstack_top
;
3705 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3706 DUK_HOBJECT_INCREF(thr
, obj
);
3707 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
3708 thr
->valstack_top
++;
3710 /* default prototype (Note: 'obj' must be reachable) */
3711 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, thr
->builtins
[DUK_BIDX_FUNCTION_PROTOTYPE
]);
3716 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
3717 return 0; /* not reached */
3720 DUK_EXTERNAL duk_idx_t
duk_push_c_function(duk_context
*ctx
, duk_c_function func
, duk_int_t nargs
) {
3723 DUK_ASSERT_CTX_VALID(ctx
);
3725 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
3726 DUK_HOBJECT_FLAG_CONSTRUCTABLE
|
3727 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
3728 DUK_HOBJECT_FLAG_NEWENV
|
3729 DUK_HOBJECT_FLAG_STRICT
|
3730 DUK_HOBJECT_FLAG_NOTAIL
|
3731 DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC
|
3732 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
3734 return duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
3737 DUK_INTERNAL
void duk_push_c_function_noexotic(duk_context
*ctx
, duk_c_function func
, duk_int_t nargs
) {
3740 DUK_ASSERT_CTX_VALID(ctx
);
3742 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
3743 DUK_HOBJECT_FLAG_CONSTRUCTABLE
|
3744 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
3745 DUK_HOBJECT_FLAG_NEWENV
|
3746 DUK_HOBJECT_FLAG_STRICT
|
3747 DUK_HOBJECT_FLAG_NOTAIL
|
3748 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
3750 (void) duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
3753 DUK_INTERNAL
void duk_push_c_function_noconstruct_noexotic(duk_context
*ctx
, duk_c_function func
, duk_int_t nargs
) {
3756 DUK_ASSERT_CTX_VALID(ctx
);
3758 flags
= DUK_HOBJECT_FLAG_EXTENSIBLE
|
3759 DUK_HOBJECT_FLAG_NATIVEFUNCTION
|
3760 DUK_HOBJECT_FLAG_NEWENV
|
3761 DUK_HOBJECT_FLAG_STRICT
|
3762 DUK_HOBJECT_FLAG_NOTAIL
|
3763 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION
);
3765 (void) duk__push_c_function_raw(ctx
, func
, nargs
, flags
);
3768 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
) {
3769 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3771 duk_small_uint_t lf_flags
;
3773 DUK_ASSERT_CTX_VALID(ctx
);
3775 /* check stack first */
3776 if (thr
->valstack_top
>= thr
->valstack_end
) {
3777 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3780 if (nargs
>= DUK_LFUNC_NARGS_MIN
&& nargs
<= DUK_LFUNC_NARGS_MAX
) {
3782 } else if (nargs
== DUK_VARARGS
) {
3783 nargs
= DUK_LFUNC_NARGS_VARARGS
;
3787 if (!(length
>= DUK_LFUNC_LENGTH_MIN
&& length
<= DUK_LFUNC_LENGTH_MAX
)) {
3790 if (!(magic
>= DUK_LFUNC_MAGIC_MIN
&& magic
<= DUK_LFUNC_MAGIC_MAX
)) {
3794 lf_flags
= DUK_LFUNC_FLAGS_PACK(magic
, length
, nargs
);
3795 DUK_TVAL_SET_LIGHTFUNC(&tv_tmp
, func
, lf_flags
);
3796 duk_push_tval(ctx
, &tv_tmp
); /* XXX: direct valstack write */
3797 DUK_ASSERT(thr
->valstack_top
!= thr
->valstack_bottom
);
3798 return ((duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
)) - 1;
3801 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
3802 return 0; /* not reached */
3805 DUK_INTERNAL duk_hbufferobject
*duk_push_bufferobject_raw(duk_context
*ctx
, duk_uint_t hobject_flags_and_class
, duk_small_int_t prototype_bidx
) {
3806 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3807 duk_hbufferobject
*obj
;
3810 DUK_ASSERT(ctx
!= NULL
);
3811 DUK_ASSERT(prototype_bidx
>= 0);
3813 /* check stack first */
3814 if (thr
->valstack_top
>= thr
->valstack_end
) {
3815 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
3818 obj
= duk_hbufferobject_alloc(thr
->heap
, hobject_flags_and_class
);
3820 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
3823 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr
, (duk_hobject
*) obj
, thr
->builtins
[prototype_bidx
]);
3824 DUK_ASSERT_HBUFFEROBJECT_VALID(obj
);
3826 tv_slot
= thr
->valstack_top
;
3827 DUK_TVAL_SET_OBJECT(tv_slot
, (duk_hobject
*) obj
);
3828 DUK_HOBJECT_INCREF(thr
, obj
);
3829 thr
->valstack_top
++;
3834 /* XXX: There's quite a bit of overlap with buffer creation handling in
3835 * duk_bi_buffer.c. Look for overlap and refactor.
3837 #define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,isview) \
3838 (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (isview))
3840 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
3841 static const duk_uint32_t duk__bufobj_flags_lookup
[] = {
3842 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER
, DUK_BIDX_BUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0), /* DUK_BUFOBJ_DUKTAPE_BUFFER */
3843 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER
, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0), /* DUK_BUFOBJ_NODEJS_BUFFER */
3844 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER
, DUK_BIDX_ARRAYBUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */
3845 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW
, DUK_BIDX_DATAVIEW_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 1), /* DUK_BUFOBJ_DATAVIEW */
3846 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY
, DUK_BIDX_INT8ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_INT8
, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */
3847 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY
, DUK_BIDX_UINT8ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */
3848 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY
, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED
, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */
3849 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY
, DUK_BIDX_INT16ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_INT16
, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */
3850 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY
, DUK_BIDX_UINT16ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT16
, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */
3851 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY
, DUK_BIDX_INT32ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_INT32
, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */
3852 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY
, DUK_BIDX_UINT32ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT32
, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */
3853 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY
, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_FLOAT32
, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */
3854 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY
, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_FLOAT64
, 3, 1) /* DUK_BUFOBJ_FLOAT64ARRAY */
3856 #else /* DUK_USE_BUFFEROBJECT_SUPPORT */
3857 /* Only allow Duktape.Buffer when support disabled. */
3858 static const duk_uint32_t duk__bufobj_flags_lookup
[] = {
3859 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER
, DUK_BIDX_BUFFER_PROTOTYPE
, DUK_HBUFFEROBJECT_ELEM_UINT8
, 0, 0) /* DUK_BUFOBJ_DUKTAPE_BUFFER */
3861 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
3862 #undef DUK__PACK_ARGS
3864 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
) {
3866 duk_hbufferobject
*h_bufobj
;
3869 duk_uint_t classnum
;
3870 duk_uint_t protobidx
;
3871 duk_uint_t lookupidx
;
3872 duk_uint_t uint_offset
, uint_length
, uint_added
;
3874 DUK_ASSERT_CTX_VALID(ctx
);
3875 thr
= (duk_hthread
*) ctx
;
3878 /* The underlying types for offset/length in duk_hbufferobject is
3879 * duk_uint_t; make sure argument values fit and that offset + length
3882 uint_offset
= (duk_uint_t
) byte_offset
;
3883 uint_length
= (duk_uint_t
) byte_length
;
3884 if (sizeof(duk_size_t
) != sizeof(duk_uint_t
)) {
3885 if ((duk_size_t
) uint_offset
!= byte_offset
|| (duk_size_t
) uint_length
!= byte_length
) {
3889 uint_added
= uint_offset
+ uint_length
;
3890 if (uint_added
< uint_offset
) {
3893 DUK_ASSERT(uint_added
>= uint_offset
&& uint_added
>= uint_length
);
3895 DUK_ASSERT_DISABLE(flags
>= 0); /* flags is unsigned */
3896 lookupidx
= flags
& 0x0f; /* 4 low bits */
3897 if (lookupidx
>= sizeof(duk__bufobj_flags_lookup
) / sizeof(duk_uint32_t
)) {
3900 tmp
= duk__bufobj_flags_lookup
[lookupidx
];
3901 classnum
= tmp
>> 24;
3902 protobidx
= (tmp
>> 16) & 0xff;
3904 h_val
= duk_require_hbuffer(ctx
, idx_buffer
);
3905 DUK_ASSERT(h_val
!= NULL
);
3907 h_bufobj
= duk_push_bufferobject_raw(ctx
,
3908 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3909 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
3910 DUK_HOBJECT_CLASS_AS_FLAGS(classnum
),
3912 DUK_ASSERT(h_bufobj
!= NULL
);
3914 h_bufobj
->buf
= h_val
;
3915 DUK_HBUFFER_INCREF(thr
, h_val
);
3916 h_bufobj
->offset
= uint_offset
;
3917 h_bufobj
->length
= uint_length
;
3918 h_bufobj
->shift
= (tmp
>> 4) & 0x0f;
3919 h_bufobj
->elem_type
= (tmp
>> 8) & 0xff;
3920 h_bufobj
->is_view
= tmp
& 0x0f;
3921 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
3923 #if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
3924 /* TypedArray views need an automatic ArrayBuffer which must be
3925 * provided as .buffer property of the view. Just create a new
3926 * ArrayBuffer sharing the same underlying buffer.
3928 if (flags
& DUK_BUFOBJ_CREATE_ARRBUF
) {
3929 h_bufobj
= duk_push_bufferobject_raw(ctx
,
3930 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3931 DUK_HOBJECT_FLAG_BUFFEROBJECT
|
3932 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER
),
3933 DUK_BIDX_ARRAYBUFFER_PROTOTYPE
);
3935 DUK_ASSERT(h_bufobj
!= NULL
);
3937 h_bufobj
->buf
= h_val
;
3938 DUK_HBUFFER_INCREF(thr
, h_val
);
3939 h_bufobj
->offset
= uint_offset
;
3940 h_bufobj
->length
= uint_length
;
3941 DUK_ASSERT(h_bufobj
->shift
== 0);
3942 h_bufobj
->elem_type
= DUK_HBUFFEROBJECT_ELEM_UINT8
;
3943 DUK_ASSERT(h_bufobj
->is_view
== 0);
3944 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj
);
3946 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_LC_BUFFER
, DUK_PROPDESC_FLAGS_NONE
);
3947 duk_compact(ctx
, -1);
3949 #endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
3954 DUK_ERROR(thr
, DUK_ERR_RANGE_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
3955 return; /* not reached */
3958 DUK_ERROR(thr
, DUK_ERR_TYPE_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
3959 return; /* not reached */
3962 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
) {
3963 duk_hthread
*thr
= (duk_hthread
*) ctx
;
3966 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
3967 duk_bool_t noblame_fileline
;
3970 DUK_ASSERT_CTX_VALID(ctx
);
3971 DUK_ASSERT(thr
!= NULL
);
3972 DUK_UNREF(filename
);
3975 /* Error code also packs a tracedata related flag. */
3976 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
3977 noblame_fileline
= err_code
& DUK_ERRCODE_FLAG_NOBLAME_FILELINE
;
3979 err_code
= err_code
& (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE
);
3981 /* error gets its 'name' from the prototype */
3982 proto
= duk_error_prototype_from_code(thr
, err_code
);
3983 ret
= duk_push_object_helper_proto(ctx
,
3984 DUK_HOBJECT_FLAG_EXTENSIBLE
|
3985 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR
),
3988 /* ... and its 'message' from an instance property */
3990 duk_push_vsprintf(ctx
, fmt
, ap
);
3991 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_MESSAGE
, DUK_PROPDESC_FLAGS_WC
);
3993 /* If no explicit message given, put error code into message field
3994 * (as a number). This is not fully in keeping with the Ecmascript
3995 * error model because messages are supposed to be strings (Error
3996 * constructors use ToString() on their argument). However, it's
3997 * probably more useful than having a separate 'code' property.
3999 duk_push_int(ctx
, err_code
);
4000 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_MESSAGE
, DUK_PROPDESC_FLAGS_WC
);
4004 /* Disabled for now, not sure this is a useful property */
4005 duk_push_int(ctx
, err_code
);
4006 duk_xdef_prop_stridx(ctx
, -2, DUK_STRIDX_CODE
, DUK_PROPDESC_FLAGS_WC
);
4009 /* Creation time error augmentation */
4010 #ifdef DUK_USE_AUGMENT_ERROR_CREATE
4011 /* filename may be NULL in which case file/line is not recorded */
4012 duk_err_augment_error_create(thr
, thr
, filename
, line
, noblame_fileline
); /* may throw an error */
4018 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
, ...) {
4022 DUK_ASSERT_CTX_VALID(ctx
);
4025 ret
= duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4030 #if !defined(DUK_USE_VARIADIC_MACROS)
4031 DUK_EXTERNAL duk_idx_t
duk_push_error_object_stash(duk_context
*ctx
, duk_errcode_t err_code
, const char *fmt
, ...) {
4032 const char *filename
= duk_api_global_filename
;
4033 duk_int_t line
= duk_api_global_line
;
4037 DUK_ASSERT_CTX_VALID(ctx
);
4039 duk_api_global_filename
= NULL
;
4040 duk_api_global_line
= 0;
4042 ret
= duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4046 #endif /* DUK_USE_VARIADIC_MACROS */
4048 DUK_EXTERNAL
void *duk_push_buffer_raw(duk_context
*ctx
, duk_size_t size
, duk_small_uint_t flags
) {
4049 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4054 DUK_ASSERT_CTX_VALID(ctx
);
4056 /* check stack first */
4057 if (thr
->valstack_top
>= thr
->valstack_end
) {
4058 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_PUSH_BEYOND_ALLOC_STACK
);
4061 /* Check for maximum buffer length. */
4062 if (size
> DUK_HBUFFER_MAX_BYTELEN
) {
4063 DUK_ERROR(thr
, DUK_ERR_RANGE_ERROR
, DUK_STR_BUFFER_TOO_LONG
);
4066 h
= duk_hbuffer_alloc(thr
->heap
, size
, flags
, &buf_data
);
4068 DUK_ERROR(thr
, DUK_ERR_ALLOC_ERROR
, DUK_STR_ALLOC_FAILED
);
4071 tv_slot
= thr
->valstack_top
;
4072 DUK_TVAL_SET_BUFFER(tv_slot
, h
);
4073 DUK_HBUFFER_INCREF(thr
, h
);
4074 thr
->valstack_top
++;
4076 return (void *) buf_data
;
4079 DUK_EXTERNAL duk_idx_t
duk_push_heapptr(duk_context
*ctx
, void *ptr
) {
4080 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4083 DUK_ASSERT_CTX_VALID(ctx
);
4085 ret
= (duk_idx_t
) (thr
->valstack_top
- thr
->valstack_bottom
);
4088 goto push_undefined
;
4091 switch (DUK_HEAPHDR_GET_TYPE((duk_heaphdr
*) ptr
)) {
4092 case DUK_HTYPE_STRING
:
4093 duk_push_hstring(ctx
, (duk_hstring
*) ptr
);
4095 case DUK_HTYPE_OBJECT
:
4096 duk_push_hobject(ctx
, (duk_hobject
*) ptr
);
4098 case DUK_HTYPE_BUFFER
:
4099 duk_push_hbuffer(ctx
, (duk_hbuffer
*) ptr
);
4102 goto push_undefined
;
4107 duk_push_undefined(ctx
);
4111 DUK_INTERNAL duk_idx_t
duk_push_object_internal(duk_context
*ctx
) {
4112 return duk_push_object_helper(ctx
,
4113 DUK_HOBJECT_FLAG_EXTENSIBLE
|
4114 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT
),
4115 -1); /* no prototype */
4118 DUK_INTERNAL
void duk_push_hstring(duk_context
*ctx
, duk_hstring
*h
) {
4120 DUK_ASSERT_CTX_VALID(ctx
);
4121 DUK_ASSERT(h
!= NULL
);
4122 DUK_TVAL_SET_STRING(&tv
, h
);
4123 duk_push_tval(ctx
, &tv
);
4126 DUK_INTERNAL
void duk_push_hstring_stridx(duk_context
*ctx
, duk_small_int_t stridx
) {
4127 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4128 DUK_ASSERT(stridx
>= 0 && stridx
< DUK_HEAP_NUM_STRINGS
);
4129 duk_push_hstring(ctx
, DUK_HTHREAD_GET_STRING(thr
, stridx
));
4132 DUK_INTERNAL
void duk_push_hobject(duk_context
*ctx
, duk_hobject
*h
) {
4134 DUK_ASSERT_CTX_VALID(ctx
);
4135 DUK_ASSERT(h
!= NULL
);
4136 DUK_TVAL_SET_OBJECT(&tv
, h
);
4137 duk_push_tval(ctx
, &tv
);
4140 DUK_INTERNAL
void duk_push_hbuffer(duk_context
*ctx
, duk_hbuffer
*h
) {
4142 DUK_ASSERT_CTX_VALID(ctx
);
4143 DUK_ASSERT(h
!= NULL
);
4144 DUK_TVAL_SET_BUFFER(&tv
, h
);
4145 duk_push_tval(ctx
, &tv
);
4148 DUK_INTERNAL
void duk_push_hobject_bidx(duk_context
*ctx
, duk_small_int_t builtin_idx
) {
4149 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4150 DUK_ASSERT_CTX_VALID(ctx
);
4151 DUK_ASSERT(thr
!= NULL
);
4152 DUK_ASSERT(builtin_idx
>= 0 && builtin_idx
< DUK_NUM_BUILTINS
);
4153 DUK_ASSERT(thr
->builtins
[builtin_idx
] != NULL
);
4154 duk_push_hobject(ctx
, thr
->builtins
[builtin_idx
]);
4161 DUK_EXTERNAL
void duk_pop_n(duk_context
*ctx
, duk_idx_t count
) {
4162 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4164 DUK_ASSERT_CTX_VALID(ctx
);
4167 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_COUNT
);
4171 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4172 if ((duk_size_t
) (thr
->valstack_top
- thr
->valstack_bottom
) < (duk_size_t
) count
) {
4173 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_POP_TOO_MANY
);
4177 * Must be very careful here, every DECREF may cause reallocation
4181 /* XXX: inlined DECREF macro would be nice here: no NULL check,
4182 * refzero queueing but no refzero algorithm run (= no pointer
4183 * instability), inline code.
4186 #ifdef DUK_USE_REFERENCE_COUNTING
4191 tv
= --thr
->valstack_top
; /* tv points to element just below prev top */
4192 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
4193 DUK_TVAL_SET_TVAL(&tv_tmp
, tv
);
4194 DUK_TVAL_SET_UNDEFINED_UNUSED(tv
);
4195 DUK_TVAL_DECREF(thr
, &tv_tmp
); /* side effects */
4202 tv
= --thr
->valstack_top
;
4203 DUK_ASSERT(tv
>= thr
->valstack_bottom
);
4204 DUK_TVAL_SET_UNDEFINED_UNUSED(tv
);
4209 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4212 DUK_EXTERNAL
void duk_pop(duk_context
*ctx
) {
4213 DUK_ASSERT_CTX_VALID(ctx
);
4217 DUK_EXTERNAL
void duk_pop_2(duk_context
*ctx
) {
4218 DUK_ASSERT_CTX_VALID(ctx
);
4222 DUK_EXTERNAL
void duk_pop_3(duk_context
*ctx
) {
4223 DUK_ASSERT_CTX_VALID(ctx
);
4231 DUK_EXTERNAL
void duk_throw(duk_context
*ctx
) {
4232 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4234 DUK_ASSERT(thr
->valstack_bottom
>= thr
->valstack
);
4235 DUK_ASSERT(thr
->valstack_top
>= thr
->valstack_bottom
);
4236 DUK_ASSERT(thr
->valstack_end
>= thr
->valstack_top
);
4238 if (thr
->valstack_top
== thr
->valstack_bottom
) {
4239 DUK_ERROR(thr
, DUK_ERR_API_ERROR
, DUK_STR_INVALID_CALL_ARGS
);
4242 /* Errors are augmented when they are created, not when they are
4243 * thrown or re-thrown. The current error handler, however, runs
4244 * just before an error is thrown.
4247 /* Sync so that augmentation sees up-to-date activations, NULL
4248 * thr->ptr_curr_pc so that it's not used if side effects occur
4249 * in augmentation or longjmp handling.
4251 duk_hthread_sync_and_null_currpc(thr
);
4253 #if defined(DUK_USE_AUGMENT_ERROR_THROW)
4254 DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval
*) duk_get_tval(ctx
, -1)));
4255 duk_err_augment_error_throw(thr
);
4257 DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval
*) duk_get_tval(ctx
, -1)));
4259 duk_err_setup_heap_ljstate(thr
, DUK_LJ_TYPE_THROW
);
4261 /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't
4262 * need to check that here. If the value is NULL, a panic occurs because
4266 duk_err_longjmp(thr
);
4270 DUK_EXTERNAL
void duk_fatal(duk_context
*ctx
, duk_errcode_t err_code
, const char *err_msg
) {
4271 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4273 DUK_ASSERT_CTX_VALID(ctx
);
4274 DUK_ASSERT(thr
!= NULL
);
4275 DUK_ASSERT(thr
->heap
!= NULL
);
4276 DUK_ASSERT(thr
->heap
->fatal_func
!= NULL
);
4278 DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s",
4279 (long) err_code
, (const char *) err_msg
));
4281 /* fatal_func should be noreturn, but noreturn declarations on function
4282 * pointers has a very spotty support apparently so it's not currently
4285 thr
->heap
->fatal_func(ctx
, err_code
, err_msg
);
4287 DUK_PANIC(DUK_ERR_API_ERROR
, "fatal handler returned");
4290 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
) {
4291 DUK_ASSERT_CTX_VALID(ctx
);
4293 duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4297 DUK_EXTERNAL
void duk_error_raw(duk_context
*ctx
, duk_errcode_t err_code
, const char *filename
, duk_int_t line
, const char *fmt
, ...) {
4300 DUK_ASSERT_CTX_VALID(ctx
);
4303 duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4308 #if !defined(DUK_USE_VARIADIC_MACROS)
4309 DUK_EXTERNAL
void duk_error_stash(duk_context
*ctx
, duk_errcode_t err_code
, const char *fmt
, ...) {
4310 const char *filename
;
4314 DUK_ASSERT_CTX_VALID(ctx
);
4316 filename
= duk_api_global_filename
;
4317 line
= duk_api_global_line
;
4318 duk_api_global_filename
= NULL
;
4319 duk_api_global_line
= 0;
4322 duk_push_error_object_va_raw(ctx
, err_code
, filename
, line
, fmt
, ap
);
4326 #endif /* DUK_USE_VARIADIC_MACROS */
4332 DUK_EXTERNAL duk_bool_t
duk_equals(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
4333 duk_hthread
*thr
= (duk_hthread
*) ctx
;
4334 duk_tval
*tv1
, *tv2
;
4336 DUK_ASSERT_CTX_VALID(ctx
);
4338 tv1
= duk_get_tval(ctx
, index1
);
4339 tv2
= duk_get_tval(ctx
, index2
);
4340 if ((tv1
== NULL
) || (tv2
== NULL
)) {
4344 /* Coercion may be needed, the helper handles that by pushing the
4345 * tagged values to the stack.
4347 return duk_js_equals(thr
, tv1
, tv2
);
4350 DUK_EXTERNAL duk_bool_t
duk_strict_equals(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
4351 duk_tval
*tv1
, *tv2
;
4353 DUK_ASSERT_CTX_VALID(ctx
);
4355 tv1
= duk_get_tval(ctx
, index1
);
4356 tv2
= duk_get_tval(ctx
, index2
);
4357 if ((tv1
== NULL
) || (tv2
== NULL
)) {
4361 /* No coercions or other side effects, so safe */
4362 return duk_js_strict_equals(tv1
, tv2
);
4369 DUK_EXTERNAL duk_bool_t
duk_instanceof(duk_context
*ctx
, duk_idx_t index1
, duk_idx_t index2
) {
4370 duk_tval
*tv1
, *tv2
;
4372 DUK_ASSERT_CTX_VALID(ctx
);
4374 /* Index validation is strict, which differs from duk_equals().
4375 * The strict behavior mimics how instanceof itself works, e.g.
4376 * it is a TypeError if rval is not a -callable- object. It would
4377 * be somewhat inconsistent if rval would be allowed to be
4378 * non-existent without a TypeError.
4380 tv1
= duk_require_tval(ctx
, index1
);
4381 DUK_ASSERT(tv1
!= NULL
);
4382 tv2
= duk_require_tval(ctx
, index2
);
4383 DUK_ASSERT(tv2
!= NULL
);
4385 return duk_js_instanceof((duk_hthread
*) ctx
, tv1
, tv2
);
4392 DUK_INTERNAL
void duk_push_lightfunc_name(duk_context
*ctx
, duk_tval
*tv
) {
4393 duk_c_function func
;
4395 DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv
));
4397 /* Lightfunc name, includes Duktape/C native function pointer, which
4398 * can often be used to locate the function from a symbol table.
4399 * The name also includes the 16-bit duk_tval flags field because it
4400 * includes the magic value. Because a single native function often
4401 * provides different functionality depending on the magic value, it
4402 * seems reasonably to include it in the name.
4404 * On the other hand, a complicated name increases string table
4405 * pressure in low memory environments (but only when function name
4409 func
= DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv
);
4410 duk_push_sprintf(ctx
, "light_");
4411 duk_push_string_funcptr(ctx
, (duk_uint8_t
*) &func
, sizeof(func
));
4412 duk_push_sprintf(ctx
, "_%04x", (unsigned int) DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv
));
4416 DUK_INTERNAL
void duk_push_lightfunc_tostring(duk_context
*ctx
, duk_tval
*tv
) {
4417 DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv
));
4419 duk_push_string(ctx
, "function ");
4420 duk_push_lightfunc_name(ctx
, tv
);
4421 duk_push_string(ctx
, "() {/* light */}");
4428 * Printing function pointers is non-portable, so we do that by hex printing
4429 * bytes from memory.
4432 DUK_INTERNAL
void duk_push_string_funcptr(duk_context
*ctx
, duk_uint8_t
*ptr
, duk_size_t sz
) {
4433 duk_uint8_t buf
[32 * 2];
4438 DUK_ASSERT(sz
<= 32); /* sanity limit for function pointer size */
4441 #if defined(DUK_USE_INTEGER_LE)
4446 for (i
= 0; i
< sz
; i
++) {
4447 #if defined(DUK_USE_INTEGER_LE)
4452 *p
++ = duk_lc_digits
[t
>> 4];
4453 *p
++ = duk_lc_digits
[t
& 0x0f];
4456 duk_push_lstring(ctx
, (const char *) buf
, sz
* 2);
4459 #undef DUK__CHECK_SPACE