summaryrefslogtreecommitdiffstats
path: root/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_api_stack.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_api_stack.c4788
1 files changed, 4788 insertions, 0 deletions
diff --git a/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_api_stack.c b/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_api_stack.c
new file mode 100644
index 000000000..295887116
--- /dev/null
+++ b/src/civetweb/src/third_party/duktape-1.8.0/src-separate/duk_api_stack.c
@@ -0,0 +1,4788 @@
+/*
+ * API calls related to general value stack manipulation: resizing the value
+ * stack, pushing and popping values, type checking and reading values,
+ * coercing values, etc.
+ *
+ * Also contains internal functions (such as duk_get_tval()), defined
+ * in duk_api_internal.h, with semantics similar to the public API.
+ */
+
+/* XXX: repetition of stack pre-checks -> helper or macro or inline */
+/* XXX: shared api error strings, and perhaps even throw code for rare cases? */
+
+#include "duk_internal.h"
+
+/*
+ * Forward declarations
+ */
+
+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);
+
+/*
+ * Global state for working around missing variadic macros
+ */
+
+#ifndef DUK_USE_VARIADIC_MACROS
+DUK_EXTERNAL const char *duk_api_global_filename = NULL;
+DUK_EXTERNAL duk_int_t duk_api_global_line = 0;
+#endif
+
+/*
+ * Misc helpers
+ */
+
+/* Check that there's room to push one value. */
+#if defined(DUK_USE_VALSTACK_UNSAFE)
+/* Faster but value stack overruns are memory unsafe. */
+#define DUK__CHECK_SPACE() do { \
+ DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
+ } while (0)
+#else
+#define DUK__CHECK_SPACE() do { \
+ if (DUK_UNLIKELY(thr->valstack_top >= thr->valstack_end)) { \
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK); \
+ } \
+ } while (0)
+#endif
+
+DUK_LOCAL_DECL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t index, duk_uint_t tag);
+
+DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t index, duk_bool_t require) {
+ duk_hthread *thr;
+ duk_tval *tv;
+ duk_small_int_t c;
+ duk_double_t d;
+
+ thr = (duk_hthread *) ctx;
+
+ tv = duk_get_tval(ctx, index);
+ if (tv == NULL) {
+ goto error_notnumber;
+ }
+
+ /*
+ * Special cases like NaN and +/- Infinity are handled explicitly
+ * because a plain C coercion from double to int handles these cases
+ * in undesirable ways. For instance, NaN may coerce to INT_MIN
+ * (not zero), and INT_MAX + 1 may coerce to INT_MIN (not INT_MAX).
+ *
+ * This double-to-int coercion differs from ToInteger() because it
+ * has a finite range (ToInteger() allows e.g. +/- Infinity). It
+ * also differs from ToInt32() because the INT_MIN/INT_MAX clamping
+ * depends on the size of the int type on the platform. In particular,
+ * on platforms with a 64-bit int type, the full range is allowed.
+ */
+
+#if defined(DUK_USE_FASTINT)
+ if (DUK_TVAL_IS_FASTINT(tv)) {
+ duk_int64_t t = DUK_TVAL_GET_FASTINT(tv);
+#if (DUK_INT_MAX <= 0x7fffffffL)
+ /* Clamping only necessary for 32-bit ints. */
+ if (t < DUK_INT_MIN) {
+ t = DUK_INT_MIN;
+ } else if (t > DUK_INT_MAX) {
+ t = DUK_INT_MAX;
+ }
+#endif
+ return (duk_int_t) t;
+ }
+#endif
+
+ if (DUK_TVAL_IS_NUMBER(tv)) {
+ d = DUK_TVAL_GET_NUMBER(tv);
+ c = (duk_small_int_t) DUK_FPCLASSIFY(d);
+ if (c == DUK_FP_NAN) {
+ return 0;
+ } else if (d < (duk_double_t) DUK_INT_MIN) {
+ /* covers -Infinity */
+ return DUK_INT_MIN;
+ } else if (d > (duk_double_t) DUK_INT_MAX) {
+ /* covers +Infinity */
+ return DUK_INT_MAX;
+ } else {
+ /* coerce towards zero */
+ return (duk_int_t) d;
+ }
+ }
+
+ error_notnumber:
+
+ if (require) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "number", DUK_STR_NOT_NUMBER);
+ /* not reachable */
+ }
+ return 0;
+}
+
+DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t index, duk_bool_t require) {
+ duk_hthread *thr;
+ duk_tval *tv;
+ duk_small_int_t c;
+ duk_double_t d;
+
+ /* Same as above but for unsigned int range. */
+
+ thr = (duk_hthread *) ctx;
+
+ tv = duk_get_tval(ctx, index);
+ if (tv == NULL) {
+ goto error_notnumber;
+ }
+
+#if defined(DUK_USE_FASTINT)
+ if (DUK_TVAL_IS_FASTINT(tv)) {
+ duk_int64_t t = DUK_TVAL_GET_FASTINT(tv);
+ if (t < 0) {
+ t = 0;
+ }
+#if (DUK_UINT_MAX <= 0xffffffffUL)
+ /* Clamping only necessary for 32-bit ints. */
+ else if (t > DUK_UINT_MAX) {
+ t = DUK_UINT_MAX;
+ }
+#endif
+ return (duk_uint_t) t;
+ }
+#endif
+
+ if (DUK_TVAL_IS_NUMBER(tv)) {
+ d = DUK_TVAL_GET_NUMBER(tv);
+ c = (duk_small_int_t) DUK_FPCLASSIFY(d);
+ if (c == DUK_FP_NAN) {
+ return 0;
+ } else if (d < 0.0) {
+ /* covers -Infinity */
+ return (duk_uint_t) 0;
+ } else if (d > (duk_double_t) DUK_UINT_MAX) {
+ /* covers +Infinity */
+ return (duk_uint_t) DUK_UINT_MAX;
+ } else {
+ /* coerce towards zero */
+ return (duk_uint_t) d;
+ }
+ }
+
+ error_notnumber:
+
+ if (require) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "number", DUK_STR_NOT_NUMBER);
+ /* not reachable */
+ }
+ return 0;
+}
+
+/*
+ * Stack index validation/normalization and getting a stack duk_tval ptr.
+ *
+ * These are called by many API entrypoints so the implementations must be
+ * fast and "inlined".
+ *
+ * There's some repetition because of this; keep the functions in sync.
+ */
+
+DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_uidx_t vs_size;
+ duk_uidx_t uindex;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ /* Care must be taken to avoid pointer wrapping in the index
+ * validation. For instance, on a 32-bit platform with 8-byte
+ * duk_tval the index 0x20000000UL would wrap the memory space
+ * once.
+ */
+
+ /* Assume value stack sizes (in elements) fits into duk_idx_t. */
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
+ DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */
+
+ if (index < 0) {
+ uindex = vs_size + (duk_uidx_t) index;
+ } else {
+ /* since index non-negative */
+ DUK_ASSERT(index != DUK_INVALID_INDEX);
+ uindex = (duk_uidx_t) index;
+ }
+
+ /* DUK_INVALID_INDEX won't be accepted as a valid index. */
+ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);
+
+ if (DUK_LIKELY(uindex < vs_size)) {
+ return (duk_idx_t) uindex;
+ }
+ return DUK_INVALID_INDEX;
+}
+
+DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_uidx_t vs_size;
+ duk_uidx_t uindex;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
+ DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */
+
+ if (index < 0) {
+ uindex = vs_size + (duk_uidx_t) index;
+ } else {
+ DUK_ASSERT(index != DUK_INVALID_INDEX);
+ uindex = (duk_uidx_t) index;
+ }
+
+ /* DUK_INVALID_INDEX won't be accepted as a valid index. */
+ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);
+
+ if (DUK_LIKELY(uindex < vs_size)) {
+ return (duk_idx_t) uindex;
+ }
+ DUK_ERROR_API_INDEX(thr, index);
+ return 0; /* unreachable */
+}
+
+DUK_INTERNAL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_uidx_t vs_size;
+ duk_uidx_t uindex;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
+ DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */
+
+ if (index < 0) {
+ uindex = vs_size + (duk_uidx_t) index;
+ } else {
+ DUK_ASSERT(index != DUK_INVALID_INDEX);
+ uindex = (duk_uidx_t) index;
+ }
+
+ /* DUK_INVALID_INDEX won't be accepted as a valid index. */
+ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);
+
+ if (DUK_LIKELY(uindex < vs_size)) {
+ return thr->valstack_bottom + uindex;
+ }
+ return NULL;
+}
+
+DUK_INTERNAL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_uidx_t vs_size;
+ duk_uidx_t uindex;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
+ DUK_ASSERT_DISABLE(vs_size >= 0); /* unsigned */
+
+ /* Use unsigned arithmetic to optimize comparison. */
+ if (index < 0) {
+ uindex = vs_size + (duk_uidx_t) index;
+ } else {
+ DUK_ASSERT(index != DUK_INVALID_INDEX);
+ uindex = (duk_uidx_t) index;
+ }
+
+ /* DUK_INVALID_INDEX won't be accepted as a valid index. */
+ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);
+
+ if (DUK_LIKELY(uindex < vs_size)) {
+ return thr->valstack_bottom + uindex;
+ }
+ DUK_ERROR_API_INDEX(thr, index);
+ return NULL;
+}
+
+/* Non-critical. */
+DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ return (duk_normalize_index(ctx, index) >= 0);
+}
+
+/* Non-critical. */
+DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ if (duk_normalize_index(ctx, index) < 0) {
+ DUK_ERROR_API_INDEX(thr, index);
+ return; /* unreachable */
+ }
+}
+
+/*
+ * Value stack top handling
+ */
+
+DUK_EXTERNAL duk_idx_t duk_get_top(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+}
+
+/* Set stack top within currently allocated range, but don't reallocate.
+ * This is performance critical especially for call handling, so whenever
+ * changing, profile and look at generated code.
+ */
+DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_uidx_t vs_size;
+ duk_uidx_t vs_limit;
+ duk_uidx_t uindex;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(DUK_INVALID_INDEX < 0);
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ DUK_ASSERT(thr->valstack_end >= thr->valstack_bottom);
+ vs_size = (duk_uidx_t) (thr->valstack_top - thr->valstack_bottom);
+ vs_limit = (duk_uidx_t) (thr->valstack_end - thr->valstack_bottom);
+
+ if (index < 0) {
+ /* Negative indices are always within allocated stack but
+ * must not go below zero index.
+ */
+ uindex = vs_size + (duk_uidx_t) index;
+ } else {
+ /* Positive index can be higher than valstack top but must
+ * not go above allocated stack (equality is OK).
+ */
+ uindex = (duk_uidx_t) index;
+ }
+
+ /* DUK_INVALID_INDEX won't be accepted as a valid index. */
+ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_size);
+ DUK_ASSERT(vs_size + (duk_uidx_t) DUK_INVALID_INDEX >= vs_limit);
+
+#if defined(DUK_USE_VALSTACK_UNSAFE)
+ DUK_ASSERT(uindex <= vs_limit);
+ DUK_UNREF(vs_limit);
+#else
+ if (DUK_UNLIKELY(uindex > vs_limit)) {
+ DUK_ERROR_API_INDEX(thr, index);
+ return; /* unreachable */
+ }
+#endif
+ DUK_ASSERT(uindex <= vs_limit);
+
+ /* Handle change in value stack top. Respect value stack
+ * initialization policy: 'undefined' above top. Note that
+ * DECREF may cause a side effect that reallocates valstack,
+ * so must relookup after DECREF.
+ */
+
+ if (uindex >= vs_size) {
+ /* Stack size increases or stays the same. */
+#if defined(DUK_USE_ASSERTIONS)
+ duk_uidx_t count;
+
+ count = uindex - vs_size;
+ while (count != 0) {
+ count--;
+ tv = thr->valstack_top + count;
+ DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(tv));
+ }
+#endif
+ thr->valstack_top = thr->valstack_bottom + uindex;
+ } else {
+ /* Stack size decreases. */
+#if defined(DUK_USE_REFERENCE_COUNTING)
+ duk_uidx_t count;
+
+ count = vs_size - uindex;
+ DUK_ASSERT(count > 0);
+ while (count > 0) {
+ count--;
+ tv = --thr->valstack_top; /* tv -> value just before prev top value; must relookup */
+ DUK_ASSERT(tv >= thr->valstack_bottom);
+ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */
+ }
+#else /* DUK_USE_REFERENCE_COUNTING */
+ duk_uidx_t count;
+ duk_tval *tv_end;
+
+ count = vs_size - uindex;
+ tv = thr->valstack_top;
+ tv_end = tv - count;
+ DUK_ASSERT(tv > tv_end);
+ do {
+ tv--;
+ DUK_TVAL_SET_UNDEFINED(tv);
+ } while (tv != tv_end);
+ thr->valstack_top = tv_end;
+#endif /* DUK_USE_REFERENCE_COUNTING */
+ }
+}
+
+DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ ret = ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
+ if (DUK_UNLIKELY(ret < 0)) {
+ /* Return invalid index; if caller uses this without checking
+ * in another API call, the index won't map to a valid stack
+ * entry.
+ */
+ return DUK_INVALID_INDEX;
+ }
+ return ret;
+}
+
+DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ ret = ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
+ if (DUK_UNLIKELY(ret < 0)) {
+ DUK_ERROR_API_INDEX(thr, -1);
+ return 0; /* unreachable */
+ }
+ return ret;
+}
+
+/*
+ * Value stack resizing.
+ *
+ * This resizing happens above the current "top": the value stack can be
+ * grown or shrunk, but the "top" is not affected. The value stack cannot
+ * be resized to a size below the current "top".
+ *
+ * The low level reallocation primitive must carefully recompute all value
+ * stack pointers, and must also work if ALL pointers are NULL. The resize
+ * is quite tricky because the valstack realloc may cause a mark-and-sweep,
+ * which may run finalizers. Running finalizers may resize the valstack
+ * recursively (the same value stack we're working on). So, after realloc
+ * returns, we know that the valstack "top" should still be the same (there
+ * should not be live values above the "top"), but its underlying size and
+ * pointer may have changed.
+ */
+
+/* XXX: perhaps refactor this to allow caller to specify some parameters, or
+ * at least a 'compact' flag which skips any spare or round-up .. useful for
+ * emergency gc.
+ */
+
+DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_ptrdiff_t old_bottom_offset;
+ duk_ptrdiff_t old_top_offset;
+ duk_ptrdiff_t old_end_offset_post;
+#ifdef DUK_USE_DEBUG
+ duk_ptrdiff_t old_end_offset_pre;
+ duk_tval *old_valstack_pre;
+ duk_tval *old_valstack_post;
+#endif
+ duk_tval *new_valstack;
+ duk_size_t new_alloc_size;
+ duk_tval *p;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+ DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) <= new_size); /* can't resize below 'top' */
+ DUK_ASSERT(new_size <= thr->valstack_max); /* valstack limit caller has check, prevents wrapping */
+ DUK_ASSERT(new_size <= DUK_SIZE_MAX / sizeof(duk_tval)); /* specific assert for wrapping */
+
+ /* get pointer offsets for tweaking below */
+ old_bottom_offset = (((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack));
+ old_top_offset = (((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack));
+#ifdef DUK_USE_DEBUG
+ old_end_offset_pre = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack)); /* not very useful, used for debugging */
+ old_valstack_pre = thr->valstack;
+#endif
+
+ /* Allocate a new valstack.
+ *
+ * Note: cannot use a plain DUK_REALLOC() because a mark-and-sweep may
+ * invalidate the original thr->valstack base pointer inside the realloc
+ * process. See doc/memory-management.rst.
+ */
+
+ new_alloc_size = sizeof(duk_tval) * new_size;
+ new_valstack = (duk_tval *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_valstack_ptr, (void *) thr, new_alloc_size);
+ if (!new_valstack) {
+ /* Because new_size != 0, if condition doesn't need to be
+ * (new_valstack != NULL || new_size == 0).
+ */
+ DUK_ASSERT(new_size != 0);
+ DUK_D(DUK_DPRINT("failed to resize valstack to %lu entries (%lu bytes)",
+ (unsigned long) new_size, (unsigned long) new_alloc_size));
+ return 0;
+ }
+
+ /* Note: the realloc may have triggered a mark-and-sweep which may
+ * have resized our valstack internally. However, the mark-and-sweep
+ * MUST NOT leave the stack bottom/top in a different state. Particular
+ * assumptions and facts:
+ *
+ * - The thr->valstack pointer may be different after realloc,
+ * and the offset between thr->valstack_end <-> thr->valstack
+ * may have changed.
+ * - The offset between thr->valstack_bottom <-> thr->valstack
+ * and thr->valstack_top <-> thr->valstack MUST NOT have changed,
+ * because mark-and-sweep must adhere to a strict stack policy.
+ * In other words, logical bottom and top MUST NOT have changed.
+ * - All values above the top are unreachable but are initialized
+ * to UNDEFINED, up to the post-realloc valstack_end.
+ * - 'old_end_offset' must be computed after realloc to be correct.
+ */
+
+ DUK_ASSERT((((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack)) == old_bottom_offset);
+ DUK_ASSERT((((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack)) == old_top_offset);
+
+ /* success, fixup pointers */
+ old_end_offset_post = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack)); /* must be computed after realloc */
+#ifdef DUK_USE_DEBUG
+ old_valstack_post = thr->valstack;
+#endif
+ thr->valstack = new_valstack;
+ thr->valstack_end = new_valstack + new_size;
+#if !defined(DUK_USE_PREFER_SIZE)
+ thr->valstack_size = new_size;
+#endif
+ thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_bottom_offset);
+ thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_top_offset);
+
+ DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+
+ /* useful for debugging */
+#ifdef DUK_USE_DEBUG
+ if (old_end_offset_pre != old_end_offset_post) {
+ DUK_D(DUK_DPRINT("valstack was resized during valstack_resize(), probably by mark-and-sweep; "
+ "end offset changed: %lu -> %lu",
+ (unsigned long) old_end_offset_pre,
+ (unsigned long) old_end_offset_post));
+ }
+ if (old_valstack_pre != old_valstack_post) {
+ DUK_D(DUK_DPRINT("valstack pointer changed during valstack_resize(), probably by mark-and-sweep: %p -> %p",
+ (void *) old_valstack_pre,
+ (void *) old_valstack_post));
+ }
+#endif
+
+ DUK_DD(DUK_DDPRINT("resized valstack to %lu elements (%lu bytes), bottom=%ld, top=%ld, "
+ "new pointers: start=%p end=%p bottom=%p top=%p",
+ (unsigned long) new_size, (unsigned long) new_alloc_size,
+ (long) (thr->valstack_bottom - thr->valstack),
+ (long) (thr->valstack_top - thr->valstack),
+ (void *) thr->valstack, (void *) thr->valstack_end,
+ (void *) thr->valstack_bottom, (void *) thr->valstack_top));
+
+ /* Init newly allocated slots (only). */
+ p = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + old_end_offset_post);
+ while (p < thr->valstack_end) {
+ /* Never executed if new size is smaller. */
+ DUK_TVAL_SET_UNDEFINED(p);
+ p++;
+ }
+
+ /* Assert for value stack initialization policy. */
+#if defined(DUK_USE_ASSERTIONS)
+ p = thr->valstack_top;
+ while (p < thr->valstack_end) {
+ DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(p));
+ p++;
+ }
+#endif
+
+ return 1;
+}
+
+DUK_INTERNAL
+duk_bool_t duk_valstack_resize_raw(duk_context *ctx,
+ duk_size_t min_new_size,
+ duk_small_uint_t flags) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_size_t old_size;
+ duk_size_t new_size;
+ duk_bool_t is_shrink = 0;
+ duk_small_uint_t shrink_flag = (flags & DUK_VSRESIZE_FLAG_SHRINK);
+ duk_small_uint_t compact_flag = (flags & DUK_VSRESIZE_FLAG_COMPACT);
+ duk_small_uint_t throw_flag = (flags & DUK_VSRESIZE_FLAG_THROW);
+
+ DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, "
+ "curr_bottom=%ld, shrink=%d, compact=%d, throw=%d",
+ (unsigned long) min_new_size,
+ (long) (thr->valstack_end - thr->valstack),
+ (long) (thr->valstack_top - thr->valstack),
+ (long) (thr->valstack_bottom - thr->valstack),
+ (int) shrink_flag, (int) compact_flag, (int) throw_flag));
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+
+#if defined(DUK_USE_PREFER_SIZE)
+ old_size = (duk_size_t) (thr->valstack_end - thr->valstack);
+#else
+ DUK_ASSERT((duk_size_t) (thr->valstack_end - thr->valstack) == thr->valstack_size);
+ old_size = thr->valstack_size;
+#endif
+
+ if (min_new_size <= old_size) {
+ is_shrink = 1;
+ if (!shrink_flag ||
+ old_size - min_new_size < DUK_VALSTACK_SHRINK_THRESHOLD) {
+ DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack"));
+ return 1;
+ }
+ }
+
+ new_size = min_new_size;
+ if (!compact_flag) {
+ if (is_shrink) {
+ /* shrink case; leave some spare */
+ new_size += DUK_VALSTACK_SHRINK_SPARE;
+ }
+
+ /* round up roughly to next 'grow step' */
+ new_size = (new_size / DUK_VALSTACK_GROW_STEP + 1) * DUK_VALSTACK_GROW_STEP;
+ }
+
+ DUK_DD(DUK_DDPRINT("want to %s valstack: %lu -> %lu elements (min_new_size %lu)",
+ (const char *) (new_size > old_size ? "grow" : "shrink"),
+ (unsigned long) old_size, (unsigned long) new_size,
+ (unsigned long) min_new_size));
+
+ if (new_size > thr->valstack_max) {
+ /* Note: may be triggered even if minimal new_size would not reach the limit,
+ * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account).
+ */
+ if (throw_flag) {
+ DUK_ERROR_RANGE(thr, DUK_STR_VALSTACK_LIMIT);
+ } else {
+ return 0;
+ }
+ }
+
+ /*
+ * When resizing the valstack, a mark-and-sweep may be triggered for
+ * the allocation of the new valstack. If the mark-and-sweep needs
+ * to use our thread for something, it may cause *the same valstack*
+ * to be resized recursively. This happens e.g. when mark-and-sweep
+ * finalizers are called. This is taken into account carefully in
+ * duk__resize_valstack().
+ *
+ * 'new_size' is known to be <= valstack_max, which ensures that
+ * size_t and pointer arithmetic won't wrap in duk__resize_valstack().
+ */
+
+ if (!duk__resize_valstack(ctx, new_size)) {
+ if (is_shrink) {
+ DUK_DD(DUK_DDPRINT("valstack resize failed, but is a shrink, ignore"));
+ return 1;
+ }
+
+ DUK_DD(DUK_DDPRINT("valstack resize failed"));
+
+ if (throw_flag) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ } else {
+ return 0;
+ }
+ }
+
+ DUK_DDD(DUK_DDDPRINT("valstack resize successful"));
+ return 1;
+}
+
+DUK_EXTERNAL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_size_t min_new_size;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+
+ if (DUK_UNLIKELY(extra < 0)) {
+ /* Clamping to zero makes the API more robust to calling code
+ * calculation errors.
+ */
+ extra = 0;
+ }
+
+ min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA;
+ return duk_valstack_resize_raw(ctx,
+ min_new_size, /* min_new_size */
+ 0 /* no shrink */ | /* flags */
+ 0 /* no compact */ |
+ 0 /* no throw */);
+}
+
+DUK_EXTERNAL void duk_require_stack(duk_context *ctx, duk_idx_t extra) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_size_t min_new_size;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+
+ if (DUK_UNLIKELY(extra < 0)) {
+ /* Clamping to zero makes the API more robust to calling code
+ * calculation errors.
+ */
+ extra = 0;
+ }
+
+ min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA;
+ (void) duk_valstack_resize_raw(ctx,
+ min_new_size, /* min_new_size */
+ 0 /* no shrink */ | /* flags */
+ 0 /* no compact */ |
+ DUK_VSRESIZE_FLAG_THROW);
+}
+
+DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_context *ctx, duk_idx_t top) {
+ duk_hthread *thr;
+ duk_size_t min_new_size;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+
+ if (DUK_UNLIKELY(top < 0)) {
+ /* Clamping to zero makes the API more robust to calling code
+ * calculation errors.
+ */
+ top = 0;
+ }
+
+ min_new_size = (thr->valstack_bottom - thr->valstack) + top + DUK_VALSTACK_INTERNAL_EXTRA;
+ return duk_valstack_resize_raw(ctx,
+ min_new_size, /* min_new_size */
+ 0 /* no shrink */ | /* flags */
+ 0 /* no compact */ |
+ 0 /* no throw */);
+}
+
+DUK_EXTERNAL void duk_require_stack_top(duk_context *ctx, duk_idx_t top) {
+ duk_hthread *thr;
+ duk_size_t min_new_size;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+
+ if (DUK_UNLIKELY(top < 0)) {
+ /* Clamping to zero makes the API more robust to calling code
+ * calculation errors.
+ */
+ top = 0;
+ }
+
+ min_new_size = (thr->valstack_bottom - thr->valstack) + top + DUK_VALSTACK_INTERNAL_EXTRA;
+ (void) duk_valstack_resize_raw(ctx,
+ min_new_size, /* min_new_size */
+ 0 /* no shrink */ | /* flags */
+ 0 /* no compact */ |
+ DUK_VSRESIZE_FLAG_THROW);
+}
+
+/*
+ * Basic stack manipulation: swap, dup, insert, replace, etc
+ */
+
+DUK_EXTERNAL void duk_swap(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
+ duk_tval *tv1;
+ duk_tval *tv2;
+ duk_tval tv_tmp;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv1 = duk_require_tval(ctx, index1);
+ DUK_ASSERT(tv1 != NULL);
+ tv2 = duk_require_tval(ctx, index2);
+ DUK_ASSERT(tv2 != NULL);
+
+ /* If tv1==tv2 this is a NOP, no check is needed */
+ DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
+ DUK_TVAL_SET_TVAL(tv1, tv2);
+ DUK_TVAL_SET_TVAL(tv2, &tv_tmp);
+}
+
+DUK_EXTERNAL void duk_swap_top(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk_swap(ctx, index, -1);
+}
+
+DUK_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_index) {
+ duk_hthread *thr;
+ duk_tval *tv_from;
+ duk_tval *tv_to;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+
+ tv_from = duk_require_tval(ctx, from_index);
+ tv_to = thr->valstack_top++;
+ DUK_ASSERT(tv_from != NULL);
+ DUK_ASSERT(tv_to != NULL);
+ DUK_TVAL_SET_TVAL(tv_to, tv_from);
+ DUK_TVAL_INCREF(thr, tv_to); /* no side effects */
+}
+
+DUK_EXTERNAL void duk_dup_top(duk_context *ctx) {
+ duk_hthread *thr;
+ duk_tval *tv_from;
+ duk_tval *tv_to;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+
+ if (thr->valstack_top - thr->valstack_bottom <= 0) {
+ DUK_ERROR_API_INDEX(thr, -1);
+ return; /* unreachable */
+ }
+ tv_from = thr->valstack_top - 1;
+ tv_to = thr->valstack_top++;
+ DUK_ASSERT(tv_from != NULL);
+ DUK_ASSERT(tv_to != NULL);
+ DUK_TVAL_SET_TVAL(tv_to, tv_from);
+ DUK_TVAL_INCREF(thr, tv_to); /* no side effects */
+}
+
+DUK_EXTERNAL void duk_insert(duk_context *ctx, duk_idx_t to_index) {
+ duk_tval *p;
+ duk_tval *q;
+ duk_tval tv_tmp;
+ duk_size_t nbytes;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ p = duk_require_tval(ctx, to_index);
+ DUK_ASSERT(p != NULL);
+ q = duk_require_tval(ctx, -1);
+ DUK_ASSERT(q != NULL);
+
+ DUK_ASSERT(q >= p);
+
+ /* nbytes
+ * <--------->
+ * [ ... | p | x | x | q ]
+ * => [ ... | q | p | x | x ]
+ */
+
+ nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */
+
+ DUK_DDD(DUK_DDDPRINT("duk_insert: to_index=%ld, p=%p, q=%p, nbytes=%lu",
+ (long) to_index, (void *) p, (void *) q, (unsigned long) nbytes));
+
+ /* No net refcount changes. */
+
+ if (nbytes > 0) {
+ DUK_TVAL_SET_TVAL(&tv_tmp, q);
+ DUK_ASSERT(nbytes > 0);
+ DUK_MEMMOVE((void *) (p + 1), (const void *) p, (size_t) nbytes);
+ DUK_TVAL_SET_TVAL(p, &tv_tmp);
+ } else {
+ /* nop: insert top to top */
+ DUK_ASSERT(nbytes == 0);
+ DUK_ASSERT(p == q);
+ }
+}
+
+DUK_EXTERNAL void duk_replace(duk_context *ctx, duk_idx_t to_index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv1;
+ duk_tval *tv2;
+ duk_tval tv_tmp;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv1 = duk_require_tval(ctx, -1);
+ DUK_ASSERT(tv1 != NULL);
+ tv2 = duk_require_tval(ctx, to_index);
+ DUK_ASSERT(tv2 != NULL);
+
+ /* For tv1 == tv2, both pointing to stack top, the end result
+ * is same as duk_pop(ctx).
+ */
+ DUK_TVAL_SET_TVAL(&tv_tmp, tv2);
+ DUK_TVAL_SET_TVAL(tv2, tv1);
+ DUK_TVAL_SET_UNDEFINED(tv1);
+ thr->valstack_top--;
+ DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
+}
+
+DUK_EXTERNAL void duk_copy(duk_context *ctx, duk_idx_t from_index, duk_idx_t to_index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv1;
+ duk_tval *tv2;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr); /* w/o refcounting */
+
+ tv1 = duk_require_tval(ctx, from_index);
+ DUK_ASSERT(tv1 != NULL);
+ tv2 = duk_require_tval(ctx, to_index);
+ DUK_ASSERT(tv2 != NULL);
+
+ /* For tv1 == tv2, this is a no-op (no explicit check needed). */
+ DUK_TVAL_SET_TVAL_UPDREF(thr, tv2, tv1); /* side effects */
+}
+
+DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *p;
+ duk_tval *q;
+#ifdef DUK_USE_REFERENCE_COUNTING
+ duk_tval tv_tmp;
+#endif
+ duk_size_t nbytes;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ p = duk_require_tval(ctx, index);
+ DUK_ASSERT(p != NULL);
+ q = duk_require_tval(ctx, -1);
+ DUK_ASSERT(q != NULL);
+
+ DUK_ASSERT(q >= p);
+
+ /* nbytes zero size case
+ * <--------->
+ * [ ... | p | x | x | q ] [ ... | p==q ]
+ * => [ ... | x | x | q ] [ ... ]
+ */
+
+#ifdef DUK_USE_REFERENCE_COUNTING
+ /* use a temp: decref only when valstack reachable values are correct */
+ DUK_TVAL_SET_TVAL(&tv_tmp, p);
+#endif
+
+ nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */
+ DUK_MEMMOVE((void *) p, (const void *) (p + 1), (size_t) nbytes); /* zero size not an issue: pointers are valid */
+
+ DUK_TVAL_SET_UNDEFINED(q);
+ thr->valstack_top--;
+
+#ifdef DUK_USE_REFERENCE_COUNTING
+ DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
+#endif
+}
+
+/*
+ * Stack slice primitives
+ */
+
+DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, duk_idx_t count, duk_bool_t is_copy) {
+ duk_hthread *to_thr = (duk_hthread *) to_ctx;
+ duk_hthread *from_thr = (duk_hthread *) from_ctx;
+ void *src;
+ duk_size_t nbytes;
+ duk_tval *p;
+ duk_tval *q;
+
+ /* XXX: several pointer comparison issues here */
+
+ DUK_ASSERT_CTX_VALID(to_ctx);
+ DUK_ASSERT_CTX_VALID(from_ctx);
+ DUK_ASSERT(to_ctx != NULL);
+ DUK_ASSERT(from_ctx != NULL);
+
+ if (to_ctx == from_ctx) {
+ DUK_ERROR_API(to_thr, DUK_STR_INVALID_CONTEXT);
+ return;
+ }
+ if ((count < 0) ||
+ (count > (duk_idx_t) to_thr->valstack_max)) {
+ /* Maximum value check ensures 'nbytes' won't wrap below. */
+ DUK_ERROR_API(to_thr, DUK_STR_INVALID_COUNT);
+ return;
+ }
+
+ nbytes = sizeof(duk_tval) * count;
+ if (nbytes == 0) {
+ return;
+ }
+ DUK_ASSERT(to_thr->valstack_top <= to_thr->valstack_end);
+ if ((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes) {
+ DUK_ERROR_API(to_thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+ src = (void *) ((duk_uint8_t *) from_thr->valstack_top - nbytes);
+ if (src < (void *) from_thr->valstack_bottom) {
+ DUK_ERROR_API(to_thr, DUK_STR_INVALID_COUNT);
+ }
+
+ /* copy values (no overlap even if to_ctx == from_ctx; that's not
+ * allowed now anyway)
+ */
+ DUK_ASSERT(nbytes > 0);
+ DUK_MEMCPY((void *) to_thr->valstack_top, (const void *) src, (size_t) nbytes);
+
+ p = to_thr->valstack_top;
+ to_thr->valstack_top = (duk_tval *) (void *) (((duk_uint8_t *) p) + nbytes);
+
+ if (is_copy) {
+ /* Incref copies, keep originals. */
+ q = to_thr->valstack_top;
+ while (p < q) {
+ DUK_TVAL_INCREF(to_thr, p); /* no side effects */
+ p++;
+ }
+ } else {
+ /* No net refcount change. */
+ p = from_thr->valstack_top;
+ q = (duk_tval *) (void *) (((duk_uint8_t *) p) - nbytes);
+ from_thr->valstack_top = q;
+
+ while (p > q) {
+ p--;
+ DUK_TVAL_SET_UNDEFINED(p);
+ /* XXX: fast primitive to set a bunch of values to UNDEFINED */
+ }
+ }
+}
+
+/*
+ * Get/require
+ */
+
+DUK_EXTERNAL void duk_require_undefined(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_UNDEFINED(tv)) {
+ return;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "undefined", DUK_STR_NOT_UNDEFINED);
+ return; /* not reachable */
+}
+
+DUK_EXTERNAL void duk_require_null(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_NULL(tv)) {
+ return;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "null", DUK_STR_NOT_NULL);
+ return; /* not reachable */
+}
+
+DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t index) {
+ duk_bool_t ret = 0; /* default: false */
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_BOOLEAN(tv)) {
+ ret = DUK_TVAL_GET_BOOLEAN(tv);
+ }
+
+ DUK_ASSERT(ret == 0 || ret == 1);
+ return ret;
+}
+
+DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_BOOLEAN(tv)) {
+ duk_bool_t ret = DUK_TVAL_GET_BOOLEAN(tv);
+ DUK_ASSERT(ret == 0 || ret == 1);
+ return ret;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "boolean", DUK_STR_NOT_BOOLEAN);
+ return 0; /* not reachable */
+}
+
+DUK_EXTERNAL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t index) {
+ duk_double_union ret;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ ret.d = DUK_DOUBLE_NAN; /* default: NaN */
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_NUMBER(tv)) {
+ ret.d = DUK_TVAL_GET_NUMBER(tv);
+ }
+
+ /*
+ * Number should already be in NaN-normalized form, but let's
+ * normalize anyway.
+ */
+
+ DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret);
+ return ret.d;
+}
+
+DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_NUMBER(tv)) {
+ duk_double_union ret;
+ ret.d = DUK_TVAL_GET_NUMBER(tv);
+
+ /*
+ * Number should already be in NaN-normalized form,
+ * but let's normalize anyway.
+ */
+
+ DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret);
+ return ret.d;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "number", DUK_STR_NOT_NUMBER);
+ return DUK_DOUBLE_NAN; /* not reachable */
+}
+
+DUK_EXTERNAL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t index) {
+ /* Custom coercion for API */
+ DUK_ASSERT_CTX_VALID(ctx);
+ return (duk_int_t) duk__api_coerce_d2i(ctx, index, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t index) {
+ /* Custom coercion for API */
+ DUK_ASSERT_CTX_VALID(ctx);
+ return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t index) {
+ /* Custom coercion for API */
+ DUK_ASSERT_CTX_VALID(ctx);
+ return (duk_int_t) duk__api_coerce_d2i(ctx, index, 1 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t index) {
+ /* Custom coercion for API */
+ DUK_ASSERT_CTX_VALID(ctx);
+ return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 1 /*require*/);
+}
+
+DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
+ const char *ret;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* default: NULL, length 0 */
+ ret = NULL;
+ if (out_len) {
+ *out_len = 0;
+ }
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_STRING(tv)) {
+ /* Here we rely on duk_hstring instances always being zero
+ * terminated even if the actual string is not.
+ */
+ duk_hstring *h = DUK_TVAL_GET_STRING(tv);
+ DUK_ASSERT(h != NULL);
+ ret = (const char *) DUK_HSTRING_GET_DATA(h);
+ if (out_len) {
+ *out_len = DUK_HSTRING_GET_BYTELEN(h);
+ }
+ }
+
+ return ret;
+}
+
+DUK_EXTERNAL const char *duk_require_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ const char *ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* Note: this check relies on the fact that even a zero-size string
+ * has a non-NULL pointer.
+ */
+ ret = duk_get_lstring(ctx, index, out_len);
+ if (ret) {
+ return ret;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "string", DUK_STR_NOT_STRING);
+ return NULL; /* not reachable */
+}
+
+DUK_EXTERNAL const char *duk_get_string(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return duk_get_lstring(ctx, index, NULL);
+}
+
+DUK_EXTERNAL const char *duk_require_string(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return duk_require_lstring(ctx, index, NULL);
+}
+
+DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_POINTER(tv)) {
+ void *p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */
+ return (void *) p;
+ }
+
+ return NULL;
+}
+
+DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* Note: here we must be wary of the fact that a pointer may be
+ * valid and be a NULL.
+ */
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_POINTER(tv)) {
+ void *p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */
+ return (void *) p;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "pointer", DUK_STR_NOT_POINTER);
+ return NULL; /* not reachable */
+}
+
+#if 0 /*unused*/
+DUK_INTERNAL void *duk_get_voidptr(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
+ duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
+ DUK_ASSERT(h != NULL);
+ return (void *) h;
+ }
+
+ return NULL;
+}
+#endif
+
+DUK_LOCAL void *duk__get_buffer_helper(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_bool_t throw_flag) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ if (out_size != NULL) {
+ *out_size = 0;
+ }
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_BUFFER(tv)) {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ if (out_size) {
+ *out_size = DUK_HBUFFER_GET_SIZE(h);
+ }
+ return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */
+ }
+
+ if (throw_flag) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "buffer", DUK_STR_NOT_BUFFER);
+ }
+ return NULL;
+}
+
+DUK_EXTERNAL void *duk_get_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
+ return duk__get_buffer_helper(ctx, index, out_size, 0 /*throw_flag*/);
+}
+
+DUK_EXTERNAL void *duk_require_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
+ return duk__get_buffer_helper(ctx, index, out_size, 1 /*throw_flag*/);
+}
+
+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) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ if (out_size != NULL) {
+ *out_size = 0;
+ }
+
+ tv = duk_get_tval(ctx, index);
+ if (tv == NULL) {
+ goto fail;
+ }
+
+ if (DUK_TVAL_IS_BUFFER(tv)) {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ if (out_size) {
+ *out_size = DUK_HBUFFER_GET_SIZE(h);
+ }
+ return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */
+ } else if (DUK_TVAL_IS_OBJECT(tv)) {
+ duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(h != NULL);
+ if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
+ /* XXX: this is probably a useful shared helper: for a
+ * duk_hbufferobject, get a validated buffer pointer/length.
+ */
+ duk_hbufferobject *h_bufobj = (duk_hbufferobject *) h;
+ DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
+
+ if (h_bufobj->buf != NULL &&
+ DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)) {
+ duk_uint8_t *p;
+
+ p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf);
+ if (out_size != NULL) {
+ *out_size = (duk_size_t) h_bufobj->length;
+ }
+ return (void *) (p + h_bufobj->offset);
+ }
+ /* if slice not fully valid, treat as error */
+ }
+ }
+
+ fail:
+ if (throw_flag) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "buffer", DUK_STR_NOT_BUFFER);
+ }
+ return NULL;
+}
+
+DUK_EXTERNAL void *duk_get_buffer_data(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
+ return duk__get_buffer_data_helper(ctx, index, out_size, 0 /*throw_flag*/);
+}
+
+DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
+ return duk__get_buffer_data_helper(ctx, index, out_size, 1 /*throw_flag*/);
+}
+
+/* Raw helper for getting a value from the stack, checking its tag.
+ * The tag cannot be a number because numbers don't have an internal
+ * tag in the packed representation.
+ */
+
+DUK_LOCAL duk_heaphdr *duk__get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t index, duk_uint_t tag) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && (DUK_TVAL_GET_TAG(tv) == tag)) {
+ duk_heaphdr *ret;
+ ret = DUK_TVAL_GET_HEAPHDR(tv);
+ DUK_ASSERT(ret != NULL); /* tagged null pointers should never occur */
+ return ret;
+ }
+
+ return (duk_heaphdr *) NULL;
+}
+
+DUK_INTERNAL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t index) {
+ return (duk_hstring *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_STRING);
+}
+
+DUK_INTERNAL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t index) {
+ duk_heaphdr *h;
+ h = duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_STRING);
+ if (h == NULL) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, index, "string", DUK_STR_NOT_STRING);
+ }
+ return (duk_hstring *) h;
+}
+
+DUK_INTERNAL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t index) {
+ return (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+}
+
+DUK_INTERNAL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t index) {
+ duk_heaphdr *h;
+ h = duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (h == NULL) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, index, "object", DUK_STR_NOT_OBJECT);
+ }
+ return (duk_hobject *) h;
+}
+
+DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t index) {
+ return (duk_hbuffer *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_BUFFER);
+}
+
+DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t index) {
+ duk_heaphdr *h;
+ h = duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_BUFFER);
+ if (h == NULL) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(ctx, index, "buffer", DUK_STR_NOT_BUFFER);
+ }
+ return (duk_hbuffer *) h;
+}
+
+DUK_INTERNAL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t index) {
+ duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (h != NULL && !DUK_HOBJECT_IS_THREAD(h)) {
+ h = NULL;
+ }
+ return (duk_hthread *) h;
+}
+
+DUK_INTERNAL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (!(h != NULL && DUK_HOBJECT_IS_THREAD(h))) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "thread", DUK_STR_NOT_THREAD);
+ }
+ return (duk_hthread *) h;
+}
+
+DUK_INTERNAL duk_hcompiledfunction *duk_get_hcompiledfunction(duk_context *ctx, duk_idx_t index) {
+ duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (h != NULL && !DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
+ h = NULL;
+ }
+ return (duk_hcompiledfunction *) h;
+}
+
+DUK_INTERNAL duk_hcompiledfunction *duk_require_hcompiledfunction(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (!(h != NULL && DUK_HOBJECT_IS_COMPILEDFUNCTION(h))) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "compiledfunction", DUK_STR_NOT_COMPILEDFUNCTION);
+ }
+ return (duk_hcompiledfunction *) h;
+}
+
+DUK_INTERNAL duk_hnativefunction *duk_get_hnativefunction(duk_context *ctx, duk_idx_t index) {
+ duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (h != NULL && !DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
+ h = NULL;
+ }
+ return (duk_hnativefunction *) h;
+}
+
+DUK_INTERNAL duk_hnativefunction *duk_require_hnativefunction(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (!(h != NULL && DUK_HOBJECT_IS_NATIVEFUNCTION(h))) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "nativefunction", DUK_STR_NOT_NATIVEFUNCTION);
+ }
+ return (duk_hnativefunction *) h;
+}
+
+DUK_EXTERNAL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+ duk_hobject *h;
+ duk_hnativefunction *f;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return NULL;
+ }
+ if (!DUK_TVAL_IS_OBJECT(tv)) {
+ return NULL;
+ }
+ h = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(h != NULL);
+
+ if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
+ return NULL;
+ }
+ DUK_ASSERT(DUK_HOBJECT_HAS_NATIVEFUNCTION(h));
+ f = (duk_hnativefunction *) h;
+
+ return f->func;
+}
+
+DUK_EXTERNAL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_c_function ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ ret = duk_get_c_function(ctx, index);
+ if (!ret) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "nativefunction", DUK_STR_NOT_NATIVEFUNCTION);
+ }
+ return ret;
+}
+
+DUK_EXTERNAL void duk_require_function(duk_context *ctx, duk_idx_t index) {
+ if (!duk_is_function(ctx, index)) {
+ DUK_ERROR_REQUIRE_TYPE_INDEX((duk_hthread *) ctx, index, "function", DUK_STR_NOT_FUNCTION);
+ }
+}
+
+DUK_EXTERNAL duk_context *duk_get_context(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return (duk_context *) duk_get_hthread(ctx, index);
+}
+
+DUK_EXTERNAL duk_context *duk_require_context(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return (duk_context *) duk_require_hthread(ctx, index);
+}
+
+DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+ void *ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
+ ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
+ DUK_ASSERT(ret != NULL);
+ return ret;
+ }
+
+ return (void *) NULL;
+}
+
+DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ void *ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
+ ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
+ DUK_ASSERT(ret != NULL);
+ return ret;
+ }
+
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "heapobject", DUK_STR_UNEXPECTED_TYPE);
+ return (void *) NULL; /* not reachable */
+}
+
+#if 0
+/* This would be pointless: we'd return NULL for both lightfuncs and
+ * unexpected types.
+ */
+DUK_INTERNAL duk_hobject *duk_get_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) {
+}
+#endif
+
+/* Useful for internal call sites where we either expect an object (function)
+ * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
+ * to an object). Return value is NULL if value is neither an object nor a
+ * lightfunc.
+ */
+DUK_INTERNAL duk_hobject *duk_get_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ if (DUK_TVAL_IS_OBJECT(tv)) {
+ return DUK_TVAL_GET_OBJECT(tv);
+ } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+ duk_to_object(ctx, index);
+ return duk_require_hobject(ctx, index);
+ }
+
+ return NULL;
+}
+
+/* Useful for internal call sites where we either expect an object (function)
+ * or a lightfunc. Returns NULL for a lightfunc.
+ */
+DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ if (DUK_TVAL_IS_OBJECT(tv)) {
+ return DUK_TVAL_GET_OBJECT(tv);
+ } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+ return NULL;
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "object", DUK_STR_NOT_OBJECT);
+ return NULL; /* not reachable */
+}
+
+/* Useful for internal call sites where we either expect an object (function)
+ * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
+ * to an object). Return value is never NULL.
+ */
+DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ if (DUK_TVAL_IS_OBJECT(tv)) {
+ return DUK_TVAL_GET_OBJECT(tv);
+ } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
+ duk_to_object(ctx, index);
+ return duk_require_hobject(ctx, index);
+ }
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, "object", DUK_STR_NOT_OBJECT);
+ return NULL; /* not reachable */
+}
+
+DUK_INTERNAL duk_hobject *duk_get_hobject_with_class(duk_context *ctx, duk_idx_t index, duk_small_uint_t classnum) {
+ duk_hobject *h;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT_DISABLE(classnum >= 0); /* unsigned */
+ DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX);
+
+ h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) != classnum) {
+ h = NULL;
+ }
+ return h;
+}
+
+DUK_INTERNAL duk_hobject *duk_require_hobject_with_class(duk_context *ctx, duk_idx_t index, duk_small_uint_t classnum) {
+ duk_hthread *thr;
+ duk_hobject *h;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT_DISABLE(classnum >= 0); /* unsigned */
+ DUK_ASSERT(classnum <= DUK_HOBJECT_CLASS_MAX);
+ thr = (duk_hthread *) ctx;
+
+ h = (duk_hobject *) duk__get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
+ if (!(h != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(h) == classnum)) {
+ duk_hstring *h_class;
+ h_class = DUK_HTHREAD_GET_STRING(thr, DUK_HOBJECT_CLASS_NUMBER_TO_STRIDX(classnum));
+ DUK_UNREF(h_class);
+
+ DUK_ERROR_REQUIRE_TYPE_INDEX(thr, index, (const char *) DUK_HSTRING_GET_DATA(h_class), DUK_STR_UNEXPECTED_TYPE);
+ }
+ return h;
+}
+
+DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return 0;
+ }
+
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNDEFINED:
+ case DUK_TAG_NULL:
+ case DUK_TAG_BOOLEAN:
+ case DUK_TAG_POINTER:
+ return 0;
+ case DUK_TAG_STRING: {
+ duk_hstring *h = DUK_TVAL_GET_STRING(tv);
+ DUK_ASSERT(h != NULL);
+ return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h);
+ }
+ case DUK_TAG_OBJECT: {
+ duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(h != NULL);
+ return (duk_size_t) duk_hobject_get_length((duk_hthread *) ctx, h);
+ }
+ case DUK_TAG_BUFFER: {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ return (duk_size_t) DUK_HBUFFER_GET_SIZE(h);
+ }
+ case DUK_TAG_LIGHTFUNC: {
+ duk_small_uint_t lf_flags;
+ lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
+ return (duk_size_t) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
+ }
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+#endif
+ default:
+ /* number */
+ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ return 0;
+ }
+
+ DUK_UNREACHABLE();
+}
+
+DUK_INTERNAL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_size_t length) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *h;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ h = duk_get_hobject(ctx, index);
+ if (!h) {
+ return;
+ }
+
+ duk_hobject_set_length(thr, h, (duk_uint32_t) length); /* XXX: typing */
+}
+
+/*
+ * Conversions and coercions
+ *
+ * The conversion/coercions are in-place operations on the value stack.
+ * Some operations are implemented here directly, while others call a
+ * helper in duk_js_ops.c after validating arguments.
+ */
+
+/* E5 Section 8.12.8 */
+
+DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_context *ctx, duk_idx_t index, duk_small_int_t func_stridx) {
+ if (duk_get_prop_stridx(ctx, index, func_stridx)) {
+ /* [ ... func ] */
+ if (duk_is_callable(ctx, -1)) {
+ duk_dup(ctx, index); /* -> [ ... func this ] */
+ duk_call_method(ctx, 0); /* -> [ ... retval ] */
+ if (duk_is_primitive(ctx, -1)) {
+ duk_replace(ctx, index);
+ return 1;
+ }
+ /* [ ... retval ]; popped below */
+ }
+ }
+ duk_pop(ctx); /* [ ... func/retval ] -> [ ... ] */
+ return 0;
+}
+
+DUK_EXTERNAL void duk_to_defaultvalue(duk_context *ctx, duk_idx_t index, duk_int_t hint) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *obj;
+ /* inline initializer for coercers[] is not allowed by old compilers like BCC */
+ duk_small_int_t coercers[2];
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+
+ coercers[0] = DUK_STRIDX_VALUE_OF;
+ coercers[1] = DUK_STRIDX_TO_STRING;
+
+ index = duk_require_normalize_index(ctx, index);
+ obj = duk_require_hobject_or_lfunc(ctx, index);
+
+ if (hint == DUK_HINT_NONE) {
+ if (obj != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_DATE) {
+ hint = DUK_HINT_STRING;
+ } else {
+ hint = DUK_HINT_NUMBER;
+ }
+ }
+
+ if (hint == DUK_HINT_STRING) {
+ coercers[0] = DUK_STRIDX_TO_STRING;
+ coercers[1] = DUK_STRIDX_VALUE_OF;
+ }
+
+ if (duk__defaultvalue_coerce_attempt(ctx, index, coercers[0])) {
+ return;
+ }
+
+ if (duk__defaultvalue_coerce_attempt(ctx, index, coercers[1])) {
+ return;
+ }
+
+ DUK_ERROR_TYPE(thr, DUK_STR_DEFAULTVALUE_COERCE_FAILED);
+}
+
+DUK_EXTERNAL void duk_to_undefined(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */
+}
+
+DUK_EXTERNAL void duk_to_null(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ DUK_TVAL_SET_NULL_UPDREF(thr, tv); /* side effects */
+}
+
+/* E5 Section 9.1 */
+DUK_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t index, duk_int_t hint) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(hint == DUK_HINT_NONE || hint == DUK_HINT_NUMBER || hint == DUK_HINT_STRING);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ if (!duk_check_type_mask(ctx, index, DUK_TYPE_MASK_OBJECT |
+ DUK_TYPE_MASK_LIGHTFUNC)) {
+ /* everything except object stay as is */
+ return;
+ }
+ duk_to_defaultvalue(ctx, index, hint);
+}
+
+/* E5 Section 9.2 */
+DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_bool_t val;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+
+ val = duk_js_toboolean(tv);
+ DUK_ASSERT(val == 0 || val == 1);
+
+ /* Note: no need to re-lookup tv, conversion is side effect free */
+ DUK_ASSERT(tv != NULL);
+ DUK_TVAL_SET_BOOLEAN_UPDREF(thr, tv, val); /* side effects */
+ return val;
+}
+
+DUK_EXTERNAL duk_double_t duk_to_number(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_double_t d;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ /* XXX: fastint? */
+ d = duk_js_tonumber(thr, tv);
+
+ /* Note: need to re-lookup because ToNumber() may have side effects */
+ tv = duk_require_tval(ctx, index);
+ DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d); /* side effects */
+ return d;
+}
+
+/* XXX: combine all the integer conversions: they share everything
+ * but the helper function for coercion.
+ */
+
+typedef duk_double_t (*duk__toint_coercer)(duk_hthread *thr, duk_tval *tv);
+
+DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t index, duk__toint_coercer coerce_func) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_double_t d;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ d = coerce_func(thr, tv);
+
+ /* XXX: fastint? */
+
+ /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
+ tv = duk_require_tval(ctx, index);
+ DUK_TVAL_SET_NUMBER_UPDREF(thr, tv, d); /* side effects */
+ return d;
+}
+
+DUK_EXTERNAL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t index) {
+ /* Value coercion (in stack): ToInteger(), E5 Section 9.4
+ * API return value coercion: custom
+ */
+ DUK_ASSERT_CTX_VALID(ctx);
+ (void) duk__to_int_uint_helper(ctx, index, duk_js_tointeger);
+ return (duk_int_t) duk__api_coerce_d2i(ctx, index, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t index) {
+ /* Value coercion (in stack): ToInteger(), E5 Section 9.4
+ * API return value coercion: custom
+ */
+ DUK_ASSERT_CTX_VALID(ctx);
+ (void) duk__to_int_uint_helper(ctx, index, duk_js_tointeger);
+ return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 0 /*require*/);
+}
+
+DUK_EXTERNAL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_int32_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ ret = duk_js_toint32(thr, tv);
+
+ /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
+ tv = duk_require_tval(ctx, index);
+ DUK_TVAL_SET_FASTINT_I32_UPDREF(thr, tv, ret); /* side effects */
+ return ret;
+}
+
+DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_uint32_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ ret = duk_js_touint32(thr, tv);
+
+ /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
+ tv = duk_require_tval(ctx, index);
+ DUK_TVAL_SET_FASTINT_U32_UPDREF(thr, tv, ret); /* side effects */
+ return ret;
+}
+
+DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_uint16_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ ret = duk_js_touint16(thr, tv);
+
+ /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
+ tv = duk_require_tval(ctx, index);
+ DUK_TVAL_SET_FASTINT_U32_UPDREF(thr, tv, ret); /* side effects */
+ return ret;
+}
+
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
+/* Special coercion for Uint8ClampedArray. */
+DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t index) {
+ duk_double_t d;
+ duk_double_t t;
+ duk_uint8_t ret;
+
+ /* XXX: Simplify this algorithm, should be possible to come up with
+ * a shorter and faster algorithm by inspecting IEEE representation
+ * directly.
+ */
+
+ d = duk_to_number(ctx, index);
+ if (d <= 0.0) {
+ return 0;
+ } else if (d >= 255) {
+ return 255;
+ } else if (DUK_ISNAN(d)) {
+ /* Avoid NaN-to-integer coercion as it is compiler specific. */
+ return 0;
+ }
+
+ t = d - DUK_FLOOR(d);
+ if (t == 0.5) {
+ /* Exact halfway, round to even. */
+ ret = (duk_uint8_t) d;
+ ret = (ret + 1) & 0xfe; /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4
+ * Example: d=4.5, t=0.5 -> ret = (4 + 1) & 0xfe = 5 & 0xfe = 4
+ */
+ } else {
+ /* Not halfway, round to nearest. */
+ ret = (duk_uint8_t) (d + 0.5);
+ }
+ return ret;
+}
+#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
+
+DUK_EXTERNAL const char *duk_to_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ (void) duk_to_string(ctx, index);
+ return duk_require_lstring(ctx, index, out_len);
+}
+
+DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk_to_string(ctx, -1);
+ return 1;
+}
+
+DUK_EXTERNAL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ /* We intentionally ignore the duk_safe_call() return value and only
+ * check the output type. This way we don't also need to check that
+ * the returned value is indeed a string in the success case.
+ */
+
+ duk_dup(ctx, index);
+ (void) duk_safe_call(ctx, duk__safe_to_string_raw, 1 /*nargs*/, 1 /*nrets*/);
+ if (!duk_is_string(ctx, -1)) {
+ /* Error: try coercing error to string once. */
+ (void) duk_safe_call(ctx, duk__safe_to_string_raw, 1 /*nargs*/, 1 /*nrets*/);
+ if (!duk_is_string(ctx, -1)) {
+ /* Double error */
+ duk_pop(ctx);
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_ERROR);
+ } else {
+ ;
+ }
+ } else {
+ ;
+ }
+ DUK_ASSERT(duk_is_string(ctx, -1));
+ DUK_ASSERT(duk_get_string(ctx, -1) != NULL);
+
+ duk_replace(ctx, index);
+ return duk_get_lstring(ctx, index, out_len);
+}
+
+#if defined(DUK_USE_DEBUGGER_SUPPORT) /* only needed by debugger for now */
+DUK_INTERNAL duk_hstring *duk_safe_to_hstring(duk_context *ctx, duk_idx_t index) {
+ (void) duk_safe_to_string(ctx, index);
+ DUK_ASSERT(duk_is_string(ctx, index));
+ DUK_ASSERT(duk_get_hstring(ctx, index) != NULL);
+ return duk_get_hstring(ctx, index);
+}
+#endif
+
+/* Coerce top into Object.prototype.toString() output. */
+DUK_INTERNAL void duk_to_object_class_string_top(duk_context *ctx) {
+ duk_hthread *thr;
+ duk_uint_t typemask;
+ duk_hstring *h_strclass;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK_UNREF(thr);
+
+ typemask = duk_get_type_mask(ctx, -1);
+ if (typemask & DUK_TYPE_MASK_UNDEFINED) {
+ h_strclass = DUK_HTHREAD_STRING_UC_UNDEFINED(thr);
+ } else if (typemask & DUK_TYPE_MASK_NULL) {
+ h_strclass = DUK_HTHREAD_STRING_UC_NULL(thr);
+ } else {
+ duk_hobject *h_obj;
+
+ duk_to_object(ctx, -1);
+ h_obj = duk_get_hobject(ctx, -1);
+ DUK_ASSERT(h_obj != NULL);
+
+ h_strclass = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h_obj);
+ }
+ DUK_ASSERT(h_strclass != NULL);
+
+ duk_pop(ctx);
+ duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass));
+}
+
+#if !defined(DUK_USE_PARANOID_ERRORS)
+DUK_INTERNAL void duk_push_hobject_class_string(duk_context *ctx, duk_hobject *h) {
+ duk_hthread *thr;
+ duk_hstring *h_strclass;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(h != NULL);
+ thr = (duk_hthread *) ctx;
+ DUK_UNREF(thr);
+
+ h_strclass = DUK_HOBJECT_GET_CLASS_STRING(thr->heap, h);
+ DUK_ASSERT(h_strclass != NULL);
+ duk_push_sprintf(ctx, "[object %s]", (const char *) DUK_HSTRING_GET_DATA(h_strclass));
+}
+#endif /* !DUK_USE_PARANOID_ERRORS */
+
+/* XXX: other variants like uint, u32 etc */
+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) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_tval tv_tmp;
+ duk_double_t d, dmin, dmax;
+ duk_int_t res;
+ duk_bool_t clamped = 0;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+ d = duk_js_tointeger(thr, tv); /* E5 Section 9.4, ToInteger() */
+
+ dmin = (duk_double_t) minval;
+ dmax = (duk_double_t) maxval;
+
+ if (d < dmin) {
+ clamped = 1;
+ res = minval;
+ d = dmin;
+ } else if (d > dmax) {
+ clamped = 1;
+ res = maxval;
+ d = dmax;
+ } else {
+ res = (duk_int_t) d;
+ }
+ DUK_UNREF(d); /* SCANBUILD: with suitable dmin/dmax limits 'd' is unused */
+ /* 'd' and 'res' agree here */
+
+ /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */
+ tv = duk_get_tval(ctx, index);
+ DUK_ASSERT(tv != NULL); /* not popped by side effect */
+ DUK_TVAL_SET_TVAL(&tv_tmp, tv);
+#if defined(DUK_USE_FASTINT)
+#if (DUK_INT_MAX <= 0x7fffffffL)
+ DUK_TVAL_SET_FASTINT_I32(tv, res);
+#else
+ /* Clamping needed if duk_int_t is 64 bits. */
+ if (res >= DUK_FASTINT_MIN && res <= DUK_FASTINT_MAX) {
+ DUK_TVAL_SET_FASTINT(tv, res);
+ } else {
+ DUK_TVAL_SET_NUMBER(tv, d);
+ }
+#endif
+#else
+ DUK_TVAL_SET_NUMBER(tv, d); /* no need to incref */
+#endif
+ DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
+
+ if (out_clamped) {
+ *out_clamped = clamped;
+ } else {
+ /* coerced value is updated to value stack even when RangeError thrown */
+ if (clamped) {
+ DUK_ERROR_RANGE(thr, DUK_STR_NUMBER_OUTSIDE_RANGE);
+ }
+ }
+
+ return res;
+}
+
+DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t index, duk_idx_t minval, duk_idx_t maxval) {
+ duk_bool_t dummy;
+ return duk_to_int_clamped_raw(ctx, index, minval, maxval, &dummy);
+}
+
+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) {
+ return duk_to_int_clamped_raw(ctx, index, minval, maxval, NULL); /* out_clamped==NULL -> RangeError if outside range */
+}
+
+DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNDEFINED: {
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_UNDEFINED);
+ break;
+ }
+ case DUK_TAG_NULL: {
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL);
+ break;
+ }
+ case DUK_TAG_BOOLEAN: {
+ if (DUK_TVAL_GET_BOOLEAN(tv)) {
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_TRUE);
+ } else {
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_FALSE);
+ }
+ break;
+ }
+ case DUK_TAG_STRING: {
+ /* nop */
+ goto skip_replace;
+ }
+ case DUK_TAG_OBJECT: {
+ duk_to_primitive(ctx, index, DUK_HINT_STRING);
+ return duk_to_string(ctx, index); /* Note: recursive call */
+ }
+ case DUK_TAG_BUFFER: {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+
+ /* Note: this allows creation of internal strings. */
+
+ DUK_ASSERT(h != NULL);
+ duk_push_lstring(ctx,
+ (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h),
+ (duk_size_t) DUK_HBUFFER_GET_SIZE(h));
+ break;
+ }
+ case DUK_TAG_POINTER: {
+ void *ptr = DUK_TVAL_GET_POINTER(tv);
+ if (ptr != NULL) {
+ duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) ptr);
+ } else {
+ /* Represent a null pointer as 'null' to be consistent with
+ * the JX format variant. Native '%p' format for a NULL
+ * pointer may be e.g. '(nil)'.
+ */
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL);
+ }
+ break;
+ }
+ case DUK_TAG_LIGHTFUNC: {
+ /* Should match Function.prototype.toString() */
+ duk_push_lightfunc_tostring(ctx, tv);
+ break;
+ }
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+#endif
+ default: {
+ /* number */
+ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ duk_push_tval(ctx, tv);
+ duk_numconv_stringify(ctx,
+ 10 /*radix*/,
+ 0 /*precision:shortest*/,
+ 0 /*force_exponential*/);
+ break;
+ }
+ }
+
+ duk_replace(ctx, index);
+
+ skip_replace:
+ return duk_require_string(ctx, index);
+}
+
+DUK_INTERNAL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t index) {
+ duk_hstring *ret;
+ DUK_ASSERT_CTX_VALID(ctx);
+ duk_to_string(ctx, index);
+ ret = duk_get_hstring(ctx, index);
+ DUK_ASSERT(ret != NULL);
+ return ret;
+}
+
+DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_uint_t mode) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hbuffer *h_buf;
+ const duk_uint8_t *src_data;
+ duk_size_t src_size;
+ duk_uint8_t *dst_data;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(thr);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ h_buf = duk_get_hbuffer(ctx, index);
+ if (h_buf != NULL) {
+ /* Buffer is kept as is, with the fixed/dynamic nature of the
+ * buffer only changed if requested. An external buffer
+ * is converted into a non-external dynamic buffer in a
+ * duk_to_dynamic_buffer() call.
+ */
+ duk_uint_t tmp;
+ duk_uint8_t *tmp_ptr;
+
+ tmp_ptr = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf);
+ src_data = (const duk_uint8_t *) tmp_ptr;
+ src_size = DUK_HBUFFER_GET_SIZE(h_buf);
+
+ tmp = (DUK_HBUFFER_HAS_DYNAMIC(h_buf) ? DUK_BUF_MODE_DYNAMIC : DUK_BUF_MODE_FIXED);
+ if ((tmp == mode && !DUK_HBUFFER_HAS_EXTERNAL(h_buf)) ||
+ mode == DUK_BUF_MODE_DONTCARE) {
+ /* Note: src_data may be NULL if input is a zero-size
+ * dynamic buffer.
+ */
+ dst_data = tmp_ptr;
+ goto skip_copy;
+ }
+ } else {
+ /* Non-buffer value is first ToString() coerced, then converted
+ * to a buffer (fixed buffer is used unless a dynamic buffer is
+ * explicitly requested).
+ */
+
+ src_data = (const duk_uint8_t *) duk_to_lstring(ctx, index, &src_size);
+ }
+
+ dst_data = (duk_uint8_t *) duk_push_buffer(ctx, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/);
+ if (DUK_LIKELY(src_size > 0)) {
+ /* When src_size == 0, src_data may be NULL (if source
+ * buffer is dynamic), and dst_data may be NULL (if
+ * target buffer is dynamic). Avoid zero-size memcpy()
+ * with an invalid pointer.
+ */
+ DUK_MEMCPY((void *) dst_data, (const void *) src_data, (size_t) src_size);
+ }
+ duk_replace(ctx, index);
+ skip_copy:
+
+ if (out_size) {
+ *out_size = src_size;
+ }
+ return dst_data;
+}
+
+DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+ void *res;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNDEFINED:
+ case DUK_TAG_NULL:
+ case DUK_TAG_BOOLEAN:
+ res = NULL;
+ break;
+ case DUK_TAG_POINTER:
+ res = DUK_TVAL_GET_POINTER(tv);
+ break;
+ case DUK_TAG_STRING:
+ case DUK_TAG_OBJECT:
+ case DUK_TAG_BUFFER:
+ /* Heap allocated: return heap pointer which is NOT useful
+ * for the caller, except for debugging.
+ */
+ res = (void *) DUK_TVAL_GET_HEAPHDR(tv);
+ break;
+ case DUK_TAG_LIGHTFUNC:
+ /* Function pointers do not always cast correctly to void *
+ * (depends on memory and segmentation model for instance),
+ * so they coerce to NULL.
+ */
+ res = NULL;
+ break;
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+#endif
+ default:
+ /* number */
+ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ res = NULL;
+ break;
+ }
+
+ duk_push_pointer(ctx, res);
+ duk_replace(ctx, index);
+ return res;
+}
+
+DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ duk_uint_t flags = 0; /* shared flags for a subset of types */
+ duk_small_int_t proto = 0;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ index = duk_require_normalize_index(ctx, index);
+
+ tv = duk_require_tval(ctx, index);
+ DUK_ASSERT(tv != NULL);
+
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNDEFINED:
+ case DUK_TAG_NULL: {
+ DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE);
+ break;
+ }
+ case DUK_TAG_BOOLEAN: {
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN);
+ proto = DUK_BIDX_BOOLEAN_PROTOTYPE;
+ goto create_object;
+ }
+ case DUK_TAG_STRING: {
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
+ proto = DUK_BIDX_STRING_PROTOTYPE;
+ goto create_object;
+ }
+ case DUK_TAG_OBJECT: {
+ /* nop */
+ break;
+ }
+ case DUK_TAG_BUFFER: {
+ /* A plain buffer coerces to a Duktape.Buffer because it's the
+ * object counterpart of the plain buffer value. But it might
+ * still make more sense to produce an ArrayBuffer here?
+ */
+
+ duk_hbufferobject *h_bufobj;
+ duk_hbuffer *h_val;
+
+ h_val = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h_val != NULL);
+
+ h_bufobj = duk_push_bufferobject_raw(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_BUFFEROBJECT |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
+ DUK_BIDX_BUFFER_PROTOTYPE);
+ DUK_ASSERT(h_bufobj != NULL);
+ DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE((duk_hobject *) h_bufobj));
+ DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_bufobj));
+
+ h_bufobj->buf = h_val;
+ DUK_HBUFFER_INCREF(thr, h_val);
+ DUK_ASSERT(h_bufobj->offset == 0);
+ h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val);
+ DUK_ASSERT(h_bufobj->shift == 0);
+ DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
+
+ DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
+ goto replace_value;
+ }
+ case DUK_TAG_POINTER: {
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER);
+ proto = DUK_BIDX_POINTER_PROTOTYPE;
+ goto create_object;
+ }
+ case DUK_TAG_LIGHTFUNC: {
+ /* Lightfunc coerces to a Function instance with concrete
+ * properties. Since 'length' is virtual for Duktape/C
+ * functions, don't need to define that.
+ *
+ * The result is made extensible to mimic what happens to
+ * strings:
+ * > Object.isExtensible(Object('foo'))
+ * true
+ */
+ duk_small_uint_t lf_flags;
+ duk_idx_t nargs;
+ duk_small_uint_t lf_len;
+ duk_c_function func;
+ duk_hnativefunction *nf;
+
+ DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
+
+ nargs = (duk_idx_t) DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
+ if (nargs == DUK_LFUNC_NARGS_VARARGS) {
+ nargs = (duk_idx_t) DUK_VARARGS;
+ }
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+ DUK_HOBJECT_FLAG_NATIVEFUNCTION |
+ DUK_HOBJECT_FLAG_NEWENV |
+ DUK_HOBJECT_FLAG_STRICT |
+ DUK_HOBJECT_FLAG_NOTAIL |
+ /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
+ (void) duk__push_c_function_raw(ctx, func, nargs, flags);
+
+ lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
+ if ((duk_idx_t) lf_len != nargs) {
+ /* Explicit length is only needed if it differs from 'nargs'. */
+ duk_push_int(ctx, (duk_int_t) lf_len);
+ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
+ }
+ duk_push_lightfunc_name(ctx, tv);
+ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
+
+ nf = duk_get_hnativefunction(ctx, -1);
+ DUK_ASSERT(nf != NULL);
+ nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
+
+ /* Enable DUKFUNC exotic behavior once properties are set up. */
+ DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject *) nf);
+ goto replace_value;
+ }
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+#endif
+ default: {
+ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER);
+ proto = DUK_BIDX_NUMBER_PROTOTYPE;
+ goto create_object;
+ }
+ }
+ return;
+
+ create_object:
+ (void) duk_push_object_helper(ctx, flags, proto);
+
+ /* Note: Boolean prototype's internal value property is not writable,
+ * but duk_xdef_prop_stridx() disregards the write protection. Boolean
+ * instances are immutable.
+ *
+ * String and buffer special behaviors are already enabled which is not
+ * ideal, but a write to the internal value is not affected by them.
+ */
+ duk_dup(ctx, index);
+ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
+
+ replace_value:
+ duk_replace(ctx, index);
+}
+
+/*
+ * Type checking
+ */
+
+DUK_LOCAL duk_bool_t duk__tag_check(duk_context *ctx, duk_idx_t index, duk_small_uint_t tag) {
+ duk_tval *tv;
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return 0;
+ }
+ return (DUK_TVAL_GET_TAG(tv) == tag);
+}
+
+DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_context *ctx, duk_idx_t index, duk_uint_t flag_mask) {
+ duk_hobject *obj;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ obj = duk_get_hobject(ctx, index);
+ if (obj) {
+ return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr *) obj, flag_mask) ? 1 : 0);
+ }
+ return 0;
+}
+
+DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return DUK_TYPE_NONE;
+ }
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNDEFINED:
+ return DUK_TYPE_UNDEFINED;
+ case DUK_TAG_NULL:
+ return DUK_TYPE_NULL;
+ case DUK_TAG_BOOLEAN:
+ return DUK_TYPE_BOOLEAN;
+ case DUK_TAG_STRING:
+ return DUK_TYPE_STRING;
+ case DUK_TAG_OBJECT:
+ return DUK_TYPE_OBJECT;
+ case DUK_TAG_BUFFER:
+ return DUK_TYPE_BUFFER;
+ case DUK_TAG_POINTER:
+ return DUK_TYPE_POINTER;
+ case DUK_TAG_LIGHTFUNC:
+ return DUK_TYPE_LIGHTFUNC;
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+#endif
+ default:
+ /* Note: number has no explicit tag (in 8-byte representation) */
+ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ return DUK_TYPE_NUMBER;
+ }
+ DUK_UNREACHABLE();
+}
+
+#if defined(DUK_USE_VERBOSE_ERRORS) && defined(DUK_USE_PARANOID_ERRORS)
+DUK_LOCAL const char *duk__type_names[] = {
+ "none",
+ "undefined",
+ "null",
+ "boolean",
+ "number",
+ "string",
+ "object",
+ "buffer",
+ "pointer",
+ "lightfunc"
+};
+
+DUK_INTERNAL const char *duk_get_type_name(duk_context *ctx, duk_idx_t index) {
+ duk_int_t type_tag;
+
+ type_tag = duk_get_type(ctx, index);
+ DUK_ASSERT(type_tag >= DUK_TYPE_MIN && type_tag <= DUK_TYPE_MAX);
+ DUK_ASSERT(DUK_TYPE_MIN == 0 && sizeof(duk__type_names) / sizeof(const char *) == DUK_TYPE_MAX + 1);
+
+ return duk__type_names[type_tag];
+}
+#endif
+
+DUK_EXTERNAL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t index, duk_int_t type) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return (duk_get_type(ctx, index) == type) ? 1 : 0;
+}
+
+DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return DUK_TYPE_MASK_NONE;
+ }
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_UNDEFINED:
+ return DUK_TYPE_MASK_UNDEFINED;
+ case DUK_TAG_NULL:
+ return DUK_TYPE_MASK_NULL;
+ case DUK_TAG_BOOLEAN:
+ return DUK_TYPE_MASK_BOOLEAN;
+ case DUK_TAG_STRING:
+ return DUK_TYPE_MASK_STRING;
+ case DUK_TAG_OBJECT:
+ return DUK_TYPE_MASK_OBJECT;
+ case DUK_TAG_BUFFER:
+ return DUK_TYPE_MASK_BUFFER;
+ case DUK_TAG_POINTER:
+ return DUK_TYPE_MASK_POINTER;
+ case DUK_TAG_LIGHTFUNC:
+ return DUK_TYPE_MASK_LIGHTFUNC;
+#if defined(DUK_USE_FASTINT)
+ case DUK_TAG_FASTINT:
+#endif
+ default:
+ /* Note: number has no explicit tag (in 8-byte representation) */
+ DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
+ DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
+ return DUK_TYPE_MASK_NUMBER;
+ }
+ DUK_UNREACHABLE();
+}
+
+DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t index, duk_uint_t mask) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ if (duk_get_type_mask(ctx, index) & mask) {
+ return 1;
+ }
+ if (mask & DUK_TYPE_MASK_THROW) {
+ DUK_ERROR_TYPE(thr, DUK_STR_UNEXPECTED_TYPE);
+ DUK_UNREACHABLE();
+ }
+ return 0;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_UNDEFINED);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_null(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_NULL);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_null_or_undefined(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+ duk_small_uint_t tag;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return 0;
+ }
+ tag = DUK_TVAL_GET_TAG(tv);
+ return (tag == DUK_TAG_UNDEFINED) || (tag == DUK_TAG_NULL);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_BOOLEAN);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /*
+ * Number is special because it doesn't have a specific
+ * tag in the 8-byte representation.
+ */
+
+ /* XXX: shorter version for 12-byte representation? */
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv) {
+ return 0;
+ }
+ return DUK_TVAL_IS_NUMBER(tv);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t index) {
+ /* XXX: This will now return false for non-numbers, even though they would
+ * coerce to NaN (as a general rule). In particular, duk_get_number()
+ * returns a NaN for non-numbers, so should this function also return
+ * true for non-numbers?
+ */
+
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (!tv || !DUK_TVAL_IS_NUMBER(tv)) {
+ return 0;
+ }
+ return DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv));
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_string(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_STRING);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_object(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_OBJECT);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_BUFFER);
+}
+
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
+DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, idx);
+ if (tv == NULL) {
+ return 0;
+ }
+ if (DUK_TVAL_IS_BUFFER(tv)) {
+ return 1;
+ } else if (DUK_TVAL_IS_OBJECT(tv)) {
+ duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(h != NULL);
+ if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+#else /* DUK_USE_BUFFEROBJECT_SUPPORT */
+DUK_EXTERNAL duk_bool_t duk_is_buffer_data(duk_context *ctx, duk_idx_t idx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return duk_is_buffer(ctx, idx);
+}
+#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
+
+DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_POINTER);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__tag_check(ctx, index, DUK_TAG_LIGHTFUNC);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t index) {
+ duk_hobject *obj;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ obj = duk_get_hobject(ctx, index);
+ if (obj) {
+ return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0);
+ }
+ return 0;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_LIGHTFUNC(tv)) {
+ return 1;
+ }
+ return duk__obj_flag_any_default_false(ctx,
+ index,
+ DUK_HOBJECT_FLAG_COMPILEDFUNCTION |
+ DUK_HOBJECT_FLAG_NATIVEFUNCTION |
+ DUK_HOBJECT_FLAG_BOUND);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__obj_flag_any_default_false(ctx,
+ index,
+ DUK_HOBJECT_FLAG_NATIVEFUNCTION);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__obj_flag_any_default_false(ctx,
+ index,
+ DUK_HOBJECT_FLAG_COMPILEDFUNCTION);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__obj_flag_any_default_false(ctx,
+ index,
+ DUK_HOBJECT_FLAG_BOUND);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk__obj_flag_any_default_false(ctx,
+ index,
+ DUK_HOBJECT_FLAG_THREAD);
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_BUFFER(tv)) {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ return (DUK_HBUFFER_HAS_DYNAMIC(h) ? 0 : 1);
+ }
+ return 0;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_BUFFER(tv)) {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ return (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0);
+ }
+ return 0;
+}
+
+DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t index) {
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv = duk_get_tval(ctx, index);
+ if (tv && DUK_TVAL_IS_BUFFER(tv)) {
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ return (DUK_HBUFFER_HAS_DYNAMIC(h) && DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0);
+ }
+ return 0;
+}
+
+DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t index) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *h;
+ duk_uint_t sanity;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ h = duk_get_hobject(ctx, index);
+
+ sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
+ do {
+ if (!h) {
+ return DUK_ERR_NONE;
+ }
+ if (h == thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]) {
+ return DUK_ERR_EVAL_ERROR;
+ }
+ if (h == thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE]) {
+ return DUK_ERR_RANGE_ERROR;
+ }
+ if (h == thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE]) {
+ return DUK_ERR_REFERENCE_ERROR;
+ }
+ if (h == thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE]) {
+ return DUK_ERR_SYNTAX_ERROR;
+ }
+ if (h == thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE]) {
+ return DUK_ERR_TYPE_ERROR;
+ }
+ if (h == thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE]) {
+ return DUK_ERR_URI_ERROR;
+ }
+ if (h == thr->builtins[DUK_BIDX_ERROR_PROTOTYPE]) {
+ return DUK_ERR_ERROR;
+ }
+
+ h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
+ } while (--sanity > 0);
+
+ return DUK_ERR_NONE;
+}
+
+/*
+ * Pushers
+ */
+
+DUK_INTERNAL void duk_push_tval(duk_context *ctx, duk_tval *tv) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(tv != NULL);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_TVAL(tv_slot, tv);
+ DUK_TVAL_INCREF(thr, tv); /* no side effects */
+}
+
+DUK_EXTERNAL void duk_push_undefined(duk_context *ctx) {
+ duk_hthread *thr;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+
+ /* Because value stack init policy is 'undefined above top',
+ * we don't need to write, just assert.
+ */
+ thr->valstack_top++;
+ DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top - 1));
+}
+
+DUK_EXTERNAL void duk_push_null(duk_context *ctx) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_NULL(tv_slot);
+}
+
+DUK_EXTERNAL void duk_push_boolean(duk_context *ctx, duk_bool_t val) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+ duk_small_int_t b;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ b = (val ? 1 : 0); /* ensure value is 1 or 0 (not other non-zero) */
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_BOOLEAN(tv_slot, b);
+}
+
+DUK_EXTERNAL void duk_push_true(duk_context *ctx) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot);
+}
+
+DUK_EXTERNAL void duk_push_false(duk_context *ctx) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot);
+}
+
+/* normalize NaN which may not match our canonical internal NaN */
+DUK_EXTERNAL void duk_push_number(duk_context *ctx, duk_double_t val) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+ duk_double_union du;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ du.d = val;
+ DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_NUMBER(tv_slot, du.d);
+}
+
+DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) {
+#if defined(DUK_USE_FASTINT)
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+#if DUK_INT_MAX <= 0x7fffffffL
+ DUK_TVAL_SET_FASTINT_I32(tv_slot, (duk_int32_t) val);
+#else
+ if (val >= DUK_FASTINT_MIN && val <= DUK_FASTINT_MAX) {
+ DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val);
+ } else {
+ duk_double_t = (duk_double_t) val;
+ DUK_TVAL_SET_NUMBER(tv_slot, d);
+ }
+#endif
+#else /* DUK_USE_FASTINT */
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+ duk_double_t d;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ d = (duk_double_t) val;
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_NUMBER(tv_slot, d);
+#endif /* DUK_USE_FASTINT */
+}
+
+DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) {
+#if defined(DUK_USE_FASTINT)
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+#if DUK_UINT_MAX <= 0xffffffffUL
+ DUK_TVAL_SET_FASTINT_U32(tv_slot, (duk_uint32_t) val);
+#else
+ if (val <= DUK_FASTINT_MAX) { /* val is unsigned so >= 0 */
+ /* XXX: take advantage of val being unsigned, no need to mask */
+ DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val);
+ } else {
+ duk_double_t = (duk_double_t) val;
+ DUK_TVAL_SET_NUMBER(tv_slot, d);
+ }
+#endif
+#else /* DUK_USE_FASTINT */
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+ duk_double_t d;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ d = (duk_double_t) val;
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_NUMBER(tv_slot, d);
+#endif /* DUK_USE_FASTINT */
+}
+
+DUK_EXTERNAL void duk_push_nan(duk_context *ctx) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+ duk_double_union du;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ DUK_DBLUNION_SET_NAN(&du);
+ DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_NUMBER(tv_slot, du.d);
+}
+
+DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk_size_t len) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hstring *h;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* check stack before interning (avoid hanging temp) */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ /* NULL with zero length represents an empty string; NULL with higher
+ * length is also now trated like an empty string although it is
+ * a bit dubious. This is unlike duk_push_string() which pushes a
+ * 'null' if the input string is a NULL.
+ */
+ if (!str) {
+ len = 0;
+ }
+
+ /* Check for maximum string length */
+ if (len > DUK_HSTRING_MAX_BYTELEN) {
+ DUK_ERROR_RANGE(thr, DUK_STR_STRING_TOO_LONG);
+ }
+
+ h = duk_heap_string_intern_checked(thr, (const duk_uint8_t *) str, (duk_uint32_t) len);
+ DUK_ASSERT(h != NULL);
+
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_STRING(tv_slot, h);
+ DUK_HSTRING_INCREF(thr, h); /* no side effects */
+
+ return (const char *) DUK_HSTRING_GET_DATA(h);
+}
+
+DUK_EXTERNAL const char *duk_push_string(duk_context *ctx, const char *str) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ if (str) {
+ return duk_push_lstring(ctx, str, DUK_STRLEN(str));
+ } else {
+ duk_push_null(ctx);
+ return NULL;
+ }
+}
+
+#ifdef DUK_USE_FILE_IO
+/* This is a bit clunky because it is ANSI C portable. Should perhaps
+ * relocate to another file because this is potentially platform
+ * dependent.
+ */
+DUK_EXTERNAL const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_file *f = NULL;
+ char *buf;
+ long sz; /* ANSI C typing */
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ if (!path) {
+ goto fail;
+ }
+ f = DUK_FOPEN(path, "rb");
+ if (!f) {
+ goto fail;
+ }
+ if (DUK_FSEEK(f, 0, SEEK_END) < 0) {
+ goto fail;
+ }
+ sz = DUK_FTELL(f);
+ if (sz < 0) {
+ goto fail;
+ }
+ if (DUK_FSEEK(f, 0, SEEK_SET) < 0) {
+ goto fail;
+ }
+ buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz);
+ DUK_ASSERT(buf != NULL);
+ if ((duk_size_t) DUK_FREAD(buf, 1, (size_t) sz, f) != (duk_size_t) sz) {
+ goto fail;
+ }
+ (void) DUK_FCLOSE(f); /* ignore fclose() error */
+ f = NULL;
+ return duk_to_string(ctx, -1);
+
+ fail:
+ if (f) {
+ DUK_FCLOSE(f);
+ }
+
+ if (flags != 0) {
+ DUK_ASSERT(flags == DUK_STRING_PUSH_SAFE); /* only flag now */
+ duk_push_undefined(ctx);
+ } else {
+ /* XXX: string not shared because it is conditional */
+ DUK_ERROR_TYPE(thr, "read file error");
+ }
+ return NULL;
+}
+#else
+DUK_EXTERNAL const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(path);
+
+ if (flags != 0) {
+ DUK_ASSERT(flags == DUK_STRING_PUSH_SAFE); /* only flag now */
+ duk_push_undefined(ctx);
+ } else {
+ /* XXX: string not shared because it is conditional */
+ DUK_ERROR_UNSUPPORTED(thr, "file I/O disabled");
+ }
+ return NULL;
+}
+#endif /* DUK_USE_FILE_IO */
+
+DUK_EXTERNAL void duk_push_pointer(duk_context *ctx, void *val) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK__CHECK_SPACE();
+ tv_slot = thr->valstack_top++;
+ DUK_TVAL_SET_POINTER(tv_slot, val);
+}
+
+DUK_LOCAL void duk__push_this_helper(duk_context *ctx, duk_small_uint_t check_object_coercible) {
+ duk_hthread *thr;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */
+ thr = (duk_hthread *) ctx;
+ DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
+ DUK__CHECK_SPACE();
+
+ DUK_ASSERT(DUK_TVAL_IS_UNDEFINED(thr->valstack_top)); /* because of valstack init policy */
+ tv_slot = thr->valstack_top++;
+
+ if (DUK_UNLIKELY(thr->callstack_top == 0)) {
+ if (check_object_coercible) {
+ goto type_error;
+ }
+ /* 'undefined' already on stack top */
+ } else {
+ duk_tval *tv;
+
+ /* 'this' binding is just before current activation's bottom */
+ DUK_ASSERT(thr->valstack_bottom > thr->valstack);
+ tv = thr->valstack_bottom - 1;
+ if (check_object_coercible &&
+ (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv))) {
+ /* XXX: better macro for DUK_TVAL_IS_UNDEFINED_OR_NULL(tv) */
+ goto type_error;
+ }
+
+ DUK_TVAL_SET_TVAL(tv_slot, tv);
+ DUK_TVAL_INCREF(thr, tv);
+ }
+ return;
+
+ type_error:
+ DUK_ERROR_TYPE(thr, DUK_STR_NOT_OBJECT_COERCIBLE);
+}
+
+DUK_EXTERNAL void duk_push_this(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk__push_this_helper(ctx, 0 /*check_object_coercible*/);
+}
+
+DUK_INTERNAL void duk_push_this_check_object_coercible(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk__push_this_helper(ctx, 1 /*check_object_coercible*/);
+}
+
+DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx) {
+ duk_hobject *h;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk__push_this_helper(ctx, 1 /*check_object_coercible*/);
+ duk_to_object(ctx, -1);
+ h = duk_get_hobject(ctx, -1);
+ DUK_ASSERT(h != NULL);
+ return h;
+}
+
+DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx) {
+ duk_hstring *h;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk__push_this_helper(ctx, 1 /*check_object_coercible*/);
+ duk_to_string(ctx, -1);
+ h = duk_get_hstring(ctx, -1);
+ DUK_ASSERT(h != NULL);
+ return h;
+}
+
+DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx) {
+ duk_hthread *thr;
+
+ DUK_ASSERT(ctx != NULL);
+ thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT(thr->callstack_top > 0); /* caller required to know */
+ DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* consequence of above */
+ DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack); /* 'this' binding exists */
+
+ return thr->valstack_bottom - 1;
+}
+
+DUK_EXTERNAL void duk_push_current_function(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_activation *act;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
+ DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
+
+ act = duk_hthread_get_current_activation(thr);
+ if (act) {
+ duk_push_tval(ctx, &act->tv_func);
+ } else {
+ duk_push_undefined(ctx);
+ }
+}
+
+DUK_EXTERNAL void duk_push_current_thread(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+
+ if (thr->heap->curr_thread) {
+ duk_push_hobject(ctx, (duk_hobject *) thr->heap->curr_thread);
+ } else {
+ duk_push_undefined(ctx);
+ }
+}
+
+DUK_EXTERNAL void duk_push_global_object(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL);
+}
+
+/* XXX: size optimize */
+DUK_LOCAL void duk__push_stash(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE)) {
+ DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use"));
+ duk_pop(ctx);
+ duk_push_object_internal(ctx);
+ duk_dup_top(ctx);
+ duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C); /* [ ... parent stash stash ] -> [ ... parent stash ] */
+ }
+ duk_remove(ctx, -2);
+}
+
+DUK_EXTERNAL void duk_push_heap_stash(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_heap *heap;
+ DUK_ASSERT_CTX_VALID(ctx);
+ heap = thr->heap;
+ DUK_ASSERT(heap->heap_object != NULL);
+ duk_push_hobject(ctx, heap->heap_object);
+ duk__push_stash(ctx);
+}
+
+DUK_EXTERNAL void duk_push_global_stash(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ duk_push_global_object(ctx);
+ duk__push_stash(ctx);
+}
+
+DUK_EXTERNAL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ DUK_ASSERT_CTX_VALID(ctx);
+ if (!target_ctx) {
+ DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS);
+ return; /* not reached */
+ }
+ duk_push_hobject(ctx, (duk_hobject *) target_ctx);
+ duk__push_stash(ctx);
+}
+
+/* XXX: duk_ssize_t would be useful here */
+DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_context *ctx, void *buf, duk_size_t sz, const char *fmt, va_list ap) {
+ duk_int_t len;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_UNREF(ctx);
+
+ /* NUL terminator handling doesn't matter here */
+ len = DUK_VSNPRINTF((char *) buf, sz, fmt, ap);
+ if (len < (duk_int_t) sz) {
+ /* Return value of 'sz' or more indicates output was (potentially)
+ * truncated.
+ */
+ return (duk_int_t) len;
+ }
+ return -1;
+}
+
+DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va_list ap) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_uint8_t stack_buf[DUK_PUSH_SPRINTF_INITIAL_SIZE];
+ duk_size_t sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
+ duk_bool_t pushed_buf = 0;
+ void *buf;
+ duk_int_t len; /* XXX: duk_ssize_t */
+ const char *res;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* special handling of fmt==NULL */
+ if (!fmt) {
+ duk_hstring *h_str;
+ duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);
+ h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr); /* rely on interning, must be this string */
+ return (const char *) DUK_HSTRING_GET_DATA(h_str);
+ }
+
+ /* initial estimate based on format string */
+ sz = DUK_STRLEN(fmt) + 16; /* format plus something to avoid just missing */
+ if (sz < DUK_PUSH_SPRINTF_INITIAL_SIZE) {
+ sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
+ }
+ DUK_ASSERT(sz > 0);
+
+ /* Try to make do with a stack buffer to avoid allocating a temporary buffer.
+ * This works 99% of the time which is quite nice.
+ */
+ for (;;) {
+ va_list ap_copy; /* copied so that 'ap' can be reused */
+
+ if (sz <= sizeof(stack_buf)) {
+ buf = stack_buf;
+ } else if (!pushed_buf) {
+ pushed_buf = 1;
+ buf = duk_push_dynamic_buffer(ctx, sz);
+ } else {
+ buf = duk_resize_buffer(ctx, -1, sz);
+ }
+ DUK_ASSERT(buf != NULL);
+
+ DUK_VA_COPY(ap_copy, ap);
+ len = duk__try_push_vsprintf(ctx, buf, sz, fmt, ap_copy);
+ va_end(ap_copy);
+ if (len >= 0) {
+ break;
+ }
+
+ /* failed, resize and try again */
+ sz = sz * 2;
+ if (sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT) {
+ DUK_ERROR_API(thr, DUK_STR_SPRINTF_TOO_LONG);
+ }
+ }
+
+ /* Cannot use duk_to_string() on the buffer because it is usually
+ * larger than 'len'. Also, 'buf' is usually a stack buffer.
+ */
+ res = duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len); /* [ buf? res ] */
+ if (pushed_buf) {
+ duk_remove(ctx, -2);
+ }
+ return res;
+}
+
+DUK_EXTERNAL const char *duk_push_sprintf(duk_context *ctx, const char *fmt, ...) {
+ va_list ap;
+ const char *ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* allow fmt==NULL */
+ va_start(ap, fmt);
+ ret = duk_push_vsprintf(ctx, fmt, ap);
+ va_end(ap);
+
+ return ret;
+}
+
+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) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv_slot;
+ duk_hobject *h;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(prototype_bidx == -1 ||
+ (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS));
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ h = duk_hobject_alloc(thr->heap, hobject_flags_and_class);
+ if (!h) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+
+ DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h->hdr.h_flags));
+
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_OBJECT(tv_slot, h);
+ DUK_HOBJECT_INCREF(thr, h); /* no side effects */
+ ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+ thr->valstack_top++;
+
+ /* object is now reachable */
+
+ if (prototype_bidx >= 0) {
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[prototype_bidx]);
+ } else {
+ DUK_ASSERT(prototype_bidx == -1);
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
+ }
+
+ return ret;
+}
+
+DUK_INTERNAL duk_idx_t duk_push_object_helper_proto(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_hobject *proto) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_idx_t ret;
+ duk_hobject *h;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ ret = duk_push_object_helper(ctx, hobject_flags_and_class, -1);
+ h = duk_get_hobject(ctx, -1);
+ DUK_ASSERT(h != NULL);
+ DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, proto);
+ return ret;
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_object(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ return duk_push_object_helper(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
+ DUK_BIDX_OBJECT_PROTOTYPE);
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hobject *obj;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ ret = duk_push_object_helper(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_ARRAY_PART |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY),
+ DUK_BIDX_ARRAY_PROTOTYPE);
+
+ obj = duk_require_hobject(ctx, ret);
+
+ /*
+ * An array must have a 'length' property (E5 Section 15.4.5.2).
+ * The special array behavior flag must only be enabled once the
+ * length property has been added.
+ *
+ * The internal property must be a number (and preferably a
+ * fastint if fastint support is enabled).
+ */
+
+ duk_push_int(ctx, 0);
+#if defined(DUK_USE_FASTINT)
+ DUK_ASSERT(DUK_TVAL_IS_FASTINT(duk_require_tval(ctx, -1)));
+#endif
+
+ duk_hobject_define_property_internal(thr,
+ obj,
+ DUK_HTHREAD_STRING_LENGTH(thr),
+ DUK_PROPDESC_FLAGS_W);
+ DUK_HOBJECT_SET_EXOTIC_ARRAY(obj);
+
+ return ret;
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hthread *obj;
+ duk_idx_t ret;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ obj = duk_hthread_alloc(thr->heap,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_THREAD |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD));
+ if (!obj) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+ obj->state = DUK_HTHREAD_STATE_INACTIVE;
+#if defined(DUK_USE_ROM_STRINGS)
+ /* Nothing to initialize, strs[] is in ROM. */
+#else
+#if defined(DUK_USE_HEAPPTR16)
+ obj->strs16 = thr->strs16;
+#else
+ obj->strs = thr->strs;
+#endif
+#endif
+ DUK_DDD(DUK_DDDPRINT("created thread object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags));
+
+ /* make the new thread reachable */
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
+ DUK_HTHREAD_INCREF(thr, obj);
+ ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+ thr->valstack_top++;
+
+ /* important to do this *after* pushing, to make the thread reachable for gc */
+ if (!duk_hthread_init_stacks(thr->heap, obj)) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+
+ /* initialize built-ins - either by copying or creating new ones */
+ if (flags & DUK_THREAD_NEW_GLOBAL_ENV) {
+ duk_hthread_create_builtin_objects(obj);
+ } else {
+ duk_hthread_copy_builtin_objects(thr, obj);
+ }
+
+ /* default prototype (Note: 'obj' must be reachable) */
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
+
+ /* Initial stack size satisfies the stack spare constraints so there
+ * is no need to require stack here.
+ */
+ DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE >=
+ DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
+
+ return ret;
+}
+
+DUK_INTERNAL duk_idx_t duk_push_compiledfunction(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hcompiledfunction *obj;
+ duk_idx_t ret;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ /* Template functions are not strictly constructable (they don't
+ * have a "prototype" property for instance), so leave the
+ * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here.
+ */
+
+ obj = duk_hcompiledfunction_alloc(thr->heap,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_COMPILEDFUNCTION |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION));
+ if (!obj) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+
+ DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags));
+
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
+ DUK_HOBJECT_INCREF(thr, obj);
+ ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+ thr->valstack_top++;
+
+ /* default prototype (Note: 'obj' must be reachable) */
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+
+ return ret;
+}
+
+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) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hnativefunction *obj;
+ duk_idx_t ret;
+ duk_tval *tv_slot;
+ duk_int16_t func_nargs;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+ if (func == NULL) {
+ goto api_error;
+ }
+ if (nargs >= 0 && nargs < DUK_HNATIVEFUNCTION_NARGS_MAX) {
+ func_nargs = (duk_int16_t) nargs;
+ } else if (nargs == DUK_VARARGS) {
+ func_nargs = DUK_HNATIVEFUNCTION_NARGS_VARARGS;
+ } else {
+ goto api_error;
+ }
+
+ obj = duk_hnativefunction_alloc(thr->heap, flags);
+ if (!obj) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+
+ obj->func = func;
+ obj->nargs = func_nargs;
+
+ DUK_DDD(DUK_DDDPRINT("created native function object with flags: 0x%08lx, nargs=%ld",
+ (unsigned long) obj->obj.hdr.h_flags, (long) obj->nargs));
+
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
+ DUK_HOBJECT_INCREF(thr, obj);
+ ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+ thr->valstack_top++;
+
+ /* default prototype (Note: 'obj' must be reachable) */
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
+
+ return ret;
+
+ api_error:
+ DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS);
+ return 0; /* not reached */
+}
+
+DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
+ duk_uint_t flags;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+ DUK_HOBJECT_FLAG_NATIVEFUNCTION |
+ DUK_HOBJECT_FLAG_NEWENV |
+ DUK_HOBJECT_FLAG_STRICT |
+ DUK_HOBJECT_FLAG_NOTAIL |
+ DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
+
+ return duk__push_c_function_raw(ctx, func, nargs, flags);
+}
+
+DUK_INTERNAL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
+ duk_uint_t flags;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_CONSTRUCTABLE |
+ DUK_HOBJECT_FLAG_NATIVEFUNCTION |
+ DUK_HOBJECT_FLAG_NEWENV |
+ DUK_HOBJECT_FLAG_STRICT |
+ DUK_HOBJECT_FLAG_NOTAIL |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
+
+ (void) duk__push_c_function_raw(ctx, func, nargs, flags);
+}
+
+DUK_INTERNAL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
+ duk_uint_t flags;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_NATIVEFUNCTION |
+ DUK_HOBJECT_FLAG_NEWENV |
+ DUK_HOBJECT_FLAG_STRICT |
+ DUK_HOBJECT_FLAG_NOTAIL |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
+
+ (void) duk__push_c_function_raw(ctx, func, nargs, flags);
+}
+
+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) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval tv_tmp;
+ duk_small_uint_t lf_flags;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ if (nargs >= DUK_LFUNC_NARGS_MIN && nargs <= DUK_LFUNC_NARGS_MAX) {
+ /* as is */
+ } else if (nargs == DUK_VARARGS) {
+ nargs = DUK_LFUNC_NARGS_VARARGS;
+ } else {
+ goto api_error;
+ }
+ if (!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX)) {
+ goto api_error;
+ }
+ if (!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX)) {
+ goto api_error;
+ }
+
+ lf_flags = DUK_LFUNC_FLAGS_PACK(magic, length, nargs);
+ DUK_TVAL_SET_LIGHTFUNC(&tv_tmp, func, lf_flags);
+ duk_push_tval(ctx, &tv_tmp); /* XXX: direct valstack write */
+ DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
+ return ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
+
+ api_error:
+ DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS);
+ return 0; /* not reached */
+}
+
+DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_hbufferobject *obj;
+ duk_tval *tv_slot;
+
+ DUK_ASSERT(ctx != NULL);
+ DUK_ASSERT(prototype_bidx >= 0);
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ obj = duk_hbufferobject_alloc(thr->heap, hobject_flags_and_class);
+ if (!obj) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+
+ DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]);
+ DUK_ASSERT_HBUFFEROBJECT_VALID(obj);
+
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
+ DUK_HOBJECT_INCREF(thr, obj);
+ thr->valstack_top++;
+
+ return obj;
+}
+
+/* XXX: There's quite a bit of overlap with buffer creation handling in
+ * duk_bi_buffer.c. Look for overlap and refactor.
+ */
+#define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,isview) \
+ (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (isview))
+
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
+static const duk_uint32_t duk__bufobj_flags_lookup[] = {
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_DUKTAPE_BUFFER */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_NODEJS_BUFFER */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER, DUK_BIDX_ARRAYBUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW, DUK_BIDX_DATAVIEW_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_DATAVIEW */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY, DUK_BIDX_INT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT8, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY, DUK_BIDX_INT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT16, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY, DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT16, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY, DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT32, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY, DUK_BIDX_UINT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT32, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT32, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT64, 3, 1) /* DUK_BUFOBJ_FLOAT64ARRAY */
+};
+#else /* DUK_USE_BUFFEROBJECT_SUPPORT */
+/* Only allow Duktape.Buffer when support disabled. */
+static const duk_uint32_t duk__bufobj_flags_lookup[] = {
+ DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0) /* DUK_BUFOBJ_DUKTAPE_BUFFER */
+};
+#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
+#undef DUK__PACK_ARGS
+
+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) {
+ duk_hthread *thr;
+ duk_hbufferobject *h_bufobj;
+ duk_hbuffer *h_val;
+ duk_uint32_t tmp;
+ duk_uint_t classnum;
+ duk_uint_t protobidx;
+ duk_uint_t lookupidx;
+ duk_uint_t uint_offset, uint_length, uint_added;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK_UNREF(thr);
+
+ /* The underlying types for offset/length in duk_hbufferobject is
+ * duk_uint_t; make sure argument values fit and that offset + length
+ * does not wrap.
+ */
+ uint_offset = (duk_uint_t) byte_offset;
+ uint_length = (duk_uint_t) byte_length;
+ if (sizeof(duk_size_t) != sizeof(duk_uint_t)) {
+ if ((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length) {
+ goto range_error;
+ }
+ }
+ uint_added = uint_offset + uint_length;
+ if (uint_added < uint_offset) {
+ goto range_error;
+ }
+ DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length);
+
+ DUK_ASSERT_DISABLE(flags >= 0); /* flags is unsigned */
+ lookupidx = flags & 0x0f; /* 4 low bits */
+ if (lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t)) {
+ goto arg_error;
+ }
+ tmp = duk__bufobj_flags_lookup[lookupidx];
+ classnum = tmp >> 24;
+ protobidx = (tmp >> 16) & 0xff;
+
+ h_val = duk_require_hbuffer(ctx, idx_buffer);
+ DUK_ASSERT(h_val != NULL);
+
+ h_bufobj = duk_push_bufferobject_raw(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_BUFFEROBJECT |
+ DUK_HOBJECT_CLASS_AS_FLAGS(classnum),
+ protobidx);
+ DUK_ASSERT(h_bufobj != NULL);
+
+ h_bufobj->buf = h_val;
+ DUK_HBUFFER_INCREF(thr, h_val);
+ h_bufobj->offset = uint_offset;
+ h_bufobj->length = uint_length;
+ h_bufobj->shift = (tmp >> 4) & 0x0f;
+ h_bufobj->elem_type = (tmp >> 8) & 0xff;
+ h_bufobj->is_view = tmp & 0x0f;
+ DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
+
+#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
+ /* TypedArray views need an automatic ArrayBuffer which must be
+ * provided as .buffer property of the view. Just create a new
+ * ArrayBuffer sharing the same underlying buffer.
+ *
+ * The ArrayBuffer offset is always set to zero, so that if one
+ * accesses the ArrayBuffer at the view's .byteOffset, the value
+ * matches the view at index 0.
+ */
+ if (flags & DUK_BUFOBJ_CREATE_ARRBUF) {
+ h_bufobj = duk_push_bufferobject_raw(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_FLAG_BUFFEROBJECT |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
+ DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
+
+ DUK_ASSERT(h_bufobj != NULL);
+
+ h_bufobj->buf = h_val;
+ DUK_HBUFFER_INCREF(thr, h_val);
+ h_bufobj->offset = 0;
+ h_bufobj->length = uint_offset + uint_length; /* Wrap checked above. */
+ DUK_ASSERT(h_bufobj->shift == 0);
+ h_bufobj->elem_type = DUK_HBUFFEROBJECT_ELEM_UINT8;
+ DUK_ASSERT(h_bufobj->is_view == 0);
+ DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
+
+ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
+ duk_compact(ctx, -1);
+ }
+#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
+
+ return;
+
+ range_error:
+ DUK_ERROR_RANGE(thr, DUK_STR_INVALID_CALL_ARGS);
+ return; /* not reached */
+
+ arg_error:
+ DUK_ERROR_TYPE(thr, DUK_STR_INVALID_CALL_ARGS);
+ return; /* not reached */
+}
+
+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) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_idx_t ret;
+ duk_hobject *proto;
+#ifdef DUK_USE_AUGMENT_ERROR_CREATE
+ duk_bool_t noblame_fileline;
+#endif
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+ DUK_UNREF(filename);
+ DUK_UNREF(line);
+
+ /* Error code also packs a tracedata related flag. */
+#ifdef DUK_USE_AUGMENT_ERROR_CREATE
+ noblame_fileline = err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE;
+#endif
+ err_code = err_code & (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE);
+
+ /* error gets its 'name' from the prototype */
+ proto = duk_error_prototype_from_code(thr, err_code);
+ ret = duk_push_object_helper_proto(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR),
+ proto);
+
+ /* ... and its 'message' from an instance property */
+ if (fmt) {
+ duk_push_vsprintf(ctx, fmt, ap);
+ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
+ } else {
+ /* If no explicit message given, put error code into message field
+ * (as a number). This is not fully in keeping with the Ecmascript
+ * error model because messages are supposed to be strings (Error
+ * constructors use ToString() on their argument). However, it's
+ * probably more useful than having a separate 'code' property.
+ */
+ duk_push_int(ctx, err_code);
+ duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
+ }
+
+ /* XXX: .code = err_code disabled, not sure if useful */
+
+ /* Creation time error augmentation */
+#ifdef DUK_USE_AUGMENT_ERROR_CREATE
+ /* filename may be NULL in which case file/line is not recorded */
+ duk_err_augment_error_create(thr, thr, filename, line, noblame_fileline); /* may throw an error */
+#endif
+
+ return ret;
+}
+
+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, ...) {
+ va_list ap;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ va_start(ap, fmt);
+ ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+#if !defined(DUK_USE_VARIADIC_MACROS)
+DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) {
+ const char *filename = duk_api_global_filename;
+ duk_int_t line = duk_api_global_line;
+ va_list ap;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk_api_global_filename = NULL;
+ duk_api_global_line = 0;
+ va_start(ap, fmt);
+ ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+#endif /* DUK_USE_VARIADIC_MACROS */
+
+DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv_slot;
+ duk_hbuffer *h;
+ void *buf_data;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* check stack first */
+ if (thr->valstack_top >= thr->valstack_end) {
+ DUK_ERROR_API(thr, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
+ }
+
+ /* Check for maximum buffer length. */
+ if (size > DUK_HBUFFER_MAX_BYTELEN) {
+ DUK_ERROR_RANGE(thr, DUK_STR_BUFFER_TOO_LONG);
+ }
+
+ h = duk_hbuffer_alloc(thr->heap, size, flags, &buf_data);
+ if (!h) {
+ DUK_ERROR_ALLOC_DEFMSG(thr);
+ }
+
+ tv_slot = thr->valstack_top;
+ DUK_TVAL_SET_BUFFER(tv_slot, h);
+ DUK_HBUFFER_INCREF(thr, h);
+ thr->valstack_top++;
+
+ return (void *) buf_data;
+}
+
+#if defined(DUK_USE_ASSERTIONS)
+DUK_LOCAL void duk__validate_push_heapptr(duk_context *ctx, void *ptr) {
+ duk_heaphdr *h;
+ duk_heaphdr *curr;
+ duk_hthread *thr;
+ duk_bool_t found = 0;
+
+ thr = (duk_hthread *) ctx;
+ h = (duk_heaphdr *) ptr;
+ if (h == NULL) {
+ /* Allowed. */
+ return;
+ }
+ DUK_ASSERT(h != NULL);
+ DUK_ASSERT(DUK_HEAPHDR_HTYPE_VALID(h));
+
+ /* One particular problem case is where an object has been
+ * queued for finalization but the finalizer hasn't yet been
+ * executed.
+ *
+ * Corner case: we're running in a finalizer for object X, and
+ * user code calls duk_push_heapptr() for X itself. In this
+ * case X will be in finalize_list, and we can detect the case
+ * by seeing that X's FINALIZED flag is set (which is done before
+ * the finalizer starts executing).
+ */
+ for (curr = thr->heap->finalize_list;
+ curr != NULL;
+ curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) {
+ if (curr == h) {
+ if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) {
+ /* Object is currently being finalized. */
+ DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */
+ found = 1;
+ } else {
+ DUK_ASSERT(0);
+ }
+ }
+ }
+
+ /* Also check for the refzero_list; must not be there unless it is
+ * being finalized when duk_push_heapptr() is called.
+ *
+ * Corner case: similar to finalize_list.
+ */
+#if defined(DUK_USE_REFERENCE_COUNTING)
+ for (curr = thr->heap->refzero_list;
+ curr != NULL;
+ curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) {
+ if (curr == h) {
+ if (DUK_HEAPHDR_HAS_FINALIZED((duk_heaphdr *) h)) {
+ /* Object is currently being finalized. */
+ DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */
+ found = 1;
+ } else {
+ DUK_ASSERT(0);
+ }
+ }
+ }
+#endif
+
+ /* If not present in finalize_list or refzero_list, the pointer
+ * must be either in heap_allocated or the string table.
+ */
+ if (DUK_HEAPHDR_GET_TYPE(h) == DUK_HTYPE_STRING) {
+ /* String table assert check omitted from 1.x branch
+ * backport.
+ */
+ } else {
+ for (curr = thr->heap->heap_allocated;
+ curr != NULL;
+ curr = DUK_HEAPHDR_GET_NEXT(thr->heap, curr)) {
+ if (curr == h) {
+ DUK_ASSERT(found == 0); /* Would indicate corrupted lists. */
+ found = 1;
+ }
+ }
+ DUK_ASSERT(found != 0);
+ }
+}
+#endif /* DUK_USE_ASSERTIONS */
+
+DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_idx_t ret;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* Reviving an object using a heap pointer is a dangerous API
+ * operation: if the application doesn't guarantee that the
+ * pointer target is always reachable, difficult-to-diagnose
+ * problems may ensue. Try to validate the 'ptr' argument to
+ * the extent possible.
+ */
+
+#if defined(DUK_USE_ASSERTIONS)
+ duk__validate_push_heapptr(ctx, ptr);
+#endif
+
+ ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
+
+ if (ptr == NULL) {
+ goto push_undefined;
+ }
+
+ switch ((int) DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) {
+ case DUK_HTYPE_STRING:
+ duk_push_hstring(ctx, (duk_hstring *) ptr);
+ break;
+ case DUK_HTYPE_OBJECT:
+ duk_push_hobject(ctx, (duk_hobject *) ptr);
+ break;
+ case DUK_HTYPE_BUFFER:
+ duk_push_hbuffer(ctx, (duk_hbuffer *) ptr);
+ break;
+ default:
+ goto push_undefined;
+ }
+ return ret;
+
+ push_undefined:
+ duk_push_undefined(ctx);
+ return ret;
+}
+
+DUK_INTERNAL duk_idx_t duk_push_object_internal(duk_context *ctx) {
+ return duk_push_object_helper(ctx,
+ DUK_HOBJECT_FLAG_EXTENSIBLE |
+ DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
+ -1); /* no prototype */
+}
+
+DUK_INTERNAL void duk_push_hstring(duk_context *ctx, duk_hstring *h) {
+ duk_tval tv;
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(h != NULL);
+ DUK_TVAL_SET_STRING(&tv, h);
+ duk_push_tval(ctx, &tv);
+}
+
+DUK_INTERNAL void duk_push_hstring_stridx(duk_context *ctx, duk_small_int_t stridx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ DUK_UNREF(thr);
+ DUK_ASSERT(stridx >= 0 && stridx < DUK_HEAP_NUM_STRINGS);
+ duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
+}
+
+DUK_INTERNAL void duk_push_hobject(duk_context *ctx, duk_hobject *h) {
+ duk_tval tv;
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(h != NULL);
+ DUK_TVAL_SET_OBJECT(&tv, h);
+ duk_push_tval(ctx, &tv);
+}
+
+DUK_INTERNAL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h) {
+ duk_tval tv;
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(h != NULL);
+ DUK_TVAL_SET_BUFFER(&tv, h);
+ duk_push_tval(ctx, &tv);
+}
+
+DUK_INTERNAL void duk_push_hobject_bidx(duk_context *ctx, duk_small_int_t builtin_idx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT(builtin_idx >= 0 && builtin_idx < DUK_NUM_BUILTINS);
+ DUK_ASSERT(thr->builtins[builtin_idx] != NULL);
+ duk_push_hobject(ctx, thr->builtins[builtin_idx]);
+}
+
+/*
+ * Poppers
+ */
+
+DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ if (DUK_UNLIKELY(count < 0)) {
+ DUK_ERROR_API(thr, DUK_STR_INVALID_COUNT);
+ return;
+ }
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ if (DUK_UNLIKELY((duk_size_t) (thr->valstack_top - thr->valstack_bottom) < (duk_size_t) count)) {
+ DUK_ERROR_API(thr, DUK_STR_POP_TOO_MANY);
+ }
+
+ /*
+ * Must be very careful here, every DECREF may cause reallocation
+ * of our valstack.
+ */
+
+ /* XXX: inlined DECREF macro would be nice here: no NULL check,
+ * refzero queueing but no refzero algorithm run (= no pointer
+ * instability), inline code.
+ */
+
+ /* XXX: optimize loops */
+
+#if defined(DUK_USE_REFERENCE_COUNTING)
+ while (count > 0) {
+ count--;
+ tv = --thr->valstack_top; /* tv points to element just below prev top */
+ DUK_ASSERT(tv >= thr->valstack_bottom);
+ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */
+ }
+#else
+ tv = thr->valstack_top;
+ while (count > 0) {
+ count--;
+ tv--;
+ DUK_ASSERT(tv >= thr->valstack_bottom);
+ DUK_TVAL_SET_UNDEFINED(tv);
+ }
+ thr->valstack_top = tv;
+#endif
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+}
+
+/* Popping one element is called so often that when footprint is not an issue,
+ * compile a specialized function for it.
+ */
+#if defined(DUK_USE_PREFER_SIZE)
+DUK_EXTERNAL void duk_pop(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ duk_pop_n(ctx, 1);
+}
+#else
+DUK_EXTERNAL void duk_pop(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv;
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ if (DUK_UNLIKELY(thr->valstack_top == thr->valstack_bottom)) {
+ DUK_ERROR_API(thr, DUK_STR_POP_TOO_MANY);
+ }
+
+ tv = --thr->valstack_top; /* tv points to element just below prev top */
+ DUK_ASSERT(tv >= thr->valstack_bottom);
+#ifdef DUK_USE_REFERENCE_COUNTING
+ DUK_TVAL_SET_UNDEFINED_UPDREF(thr, tv); /* side effects */
+#else
+ DUK_TVAL_SET_UNDEFINED(tv);
+#endif
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+}
+#endif /* !DUK_USE_PREFER_SIZE */
+
+DUK_EXTERNAL void duk_pop_2(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ duk_pop_n(ctx, 2);
+}
+
+DUK_EXTERNAL void duk_pop_3(duk_context *ctx) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ duk_pop_n(ctx, 3);
+}
+
+/*
+ * Error throwing
+ */
+
+DUK_EXTERNAL void duk_throw(duk_context *ctx) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
+ DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
+ DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
+
+ if (thr->valstack_top == thr->valstack_bottom) {
+ DUK_ERROR_API(thr, DUK_STR_INVALID_CALL_ARGS);
+ }
+
+ /* Errors are augmented when they are created, not when they are
+ * thrown or re-thrown. The current error handler, however, runs
+ * just before an error is thrown.
+ */
+
+ /* Sync so that augmentation sees up-to-date activations, NULL
+ * thr->ptr_curr_pc so that it's not used if side effects occur
+ * in augmentation or longjmp handling.
+ */
+ duk_hthread_sync_and_null_currpc(thr);
+
+#if defined(DUK_USE_AUGMENT_ERROR_THROW)
+ DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
+ duk_err_augment_error_throw(thr);
+#endif
+ DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
+
+ duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW);
+
+ /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't
+ * need to check that here. If the value is NULL, a panic occurs because
+ * we can't return.
+ */
+
+ duk_err_longjmp(thr);
+ DUK_UNREACHABLE();
+}
+
+DUK_EXTERNAL void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char *err_msg) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(thr != NULL);
+ DUK_ASSERT(thr->heap != NULL);
+ DUK_ASSERT(thr->heap->fatal_func != NULL);
+
+ DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s",
+ (long) err_code, (const char *) err_msg));
+
+ /* fatal_func should be noreturn, but noreturn declarations on function
+ * pointers has a very spotty support apparently so it's not currently
+ * done.
+ */
+ thr->heap->fatal_func(ctx, err_code, err_msg);
+
+ DUK_PANIC(DUK_ERR_API_ERROR, "fatal handler returned");
+}
+
+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) {
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+ duk_throw(ctx);
+}
+
+DUK_EXTERNAL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
+ va_list ap;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ va_start(ap, fmt);
+ duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+ va_end(ap);
+ duk_throw(ctx);
+}
+
+#if !defined(DUK_USE_VARIADIC_MACROS)
+DUK_EXTERNAL void duk_error_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) {
+ const char *filename;
+ duk_int_t line;
+ va_list ap;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ filename = duk_api_global_filename;
+ line = duk_api_global_line;
+ duk_api_global_filename = NULL;
+ duk_api_global_line = 0;
+
+ va_start(ap, fmt);
+ duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
+ va_end(ap);
+ duk_throw(ctx);
+}
+#endif /* DUK_USE_VARIADIC_MACROS */
+
+/*
+ * Comparison
+ */
+
+DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
+ duk_hthread *thr = (duk_hthread *) ctx;
+ duk_tval *tv1, *tv2;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv1 = duk_get_tval(ctx, index1);
+ tv2 = duk_get_tval(ctx, index2);
+ if ((tv1 == NULL) || (tv2 == NULL)) {
+ return 0;
+ }
+
+ /* Coercion may be needed, the helper handles that by pushing the
+ * tagged values to the stack.
+ */
+ return duk_js_equals(thr, tv1, tv2);
+}
+
+DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
+ duk_tval *tv1, *tv2;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ tv1 = duk_get_tval(ctx, index1);
+ tv2 = duk_get_tval(ctx, index2);
+ if ((tv1 == NULL) || (tv2 == NULL)) {
+ return 0;
+ }
+
+ /* No coercions or other side effects, so safe */
+ return duk_js_strict_equals(tv1, tv2);
+}
+
+/*
+ * instanceof
+ */
+
+DUK_EXTERNAL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
+ duk_tval *tv1, *tv2;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+
+ /* Index validation is strict, which differs from duk_equals().
+ * The strict behavior mimics how instanceof itself works, e.g.
+ * it is a TypeError if rval is not a -callable- object. It would
+ * be somewhat inconsistent if rval would be allowed to be
+ * non-existent without a TypeError.
+ */
+ tv1 = duk_require_tval(ctx, index1);
+ DUK_ASSERT(tv1 != NULL);
+ tv2 = duk_require_tval(ctx, index2);
+ DUK_ASSERT(tv2 != NULL);
+
+ return duk_js_instanceof((duk_hthread *) ctx, tv1, tv2);
+}
+
+/*
+ * Lightfunc
+ */
+
+DUK_INTERNAL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv) {
+ duk_c_function func;
+
+ DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
+
+ /* Lightfunc name, includes Duktape/C native function pointer, which
+ * can often be used to locate the function from a symbol table.
+ * The name also includes the 16-bit duk_tval flags field because it
+ * includes the magic value. Because a single native function often
+ * provides different functionality depending on the magic value, it
+ * seems reasonably to include it in the name.
+ *
+ * On the other hand, a complicated name increases string table
+ * pressure in low memory environments (but only when function name
+ * is accessed).
+ */
+
+ func = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv);
+ duk_push_sprintf(ctx, "light_");
+ duk_push_string_funcptr(ctx, (duk_uint8_t *) &func, sizeof(func));
+ duk_push_sprintf(ctx, "_%04x", (unsigned int) DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv));
+ duk_concat(ctx, 3);
+}
+
+DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) {
+ DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
+
+ duk_push_string(ctx, "function ");
+ duk_push_lightfunc_name(ctx, tv);
+ duk_push_string(ctx, "() {\"light\"}");
+ duk_concat(ctx, 3);
+}
+
+/*
+ * Function pointers
+ *
+ * Printing function pointers is non-portable, so we do that by hex printing
+ * bytes from memory.
+ */
+
+DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz) {
+ duk_uint8_t buf[32 * 2];
+ duk_uint8_t *p, *q;
+ duk_small_uint_t i;
+ duk_small_uint_t t;
+
+ DUK_ASSERT(sz <= 32); /* sanity limit for function pointer size */
+
+ p = buf;
+#if defined(DUK_USE_INTEGER_LE)
+ q = ptr + sz;
+#else
+ q = ptr;
+#endif
+ for (i = 0; i < sz; i++) {
+#if defined(DUK_USE_INTEGER_LE)
+ t = *(--q);
+#else
+ t = *(q++);
+#endif
+ *p++ = duk_lc_digits[t >> 4];
+ *p++ = duk_lc_digits[t & 0x0f];
+ }
+
+ duk_push_lstring(ctx, (const char *) buf, sz * 2);
+}
+
+#if !defined(DUK_USE_PARANOID_ERRORS)
+/*
+ * Push readable string summarizing duk_tval. The operation is side effect
+ * free and will only throw from internal errors (e.g. out of memory).
+ * This is used by e.g. property access code to summarize a key/base safely,
+ * and is not intended to be fast (but small and safe).
+ */
+
+#define DUK__READABLE_STRING_MAXCHARS 32
+
+/* String sanitizer which escapes ASCII control characters and a few other
+ * ASCII characters, passes Unicode as is, and replaces invalid UTF-8 with
+ * question marks. No errors are thrown for any input string, except in out
+ * of memory situations.
+ */
+DUK_LOCAL void duk__push_hstring_readable_unicode(duk_context *ctx, duk_hstring *h_input) {
+ duk_hthread *thr;
+ const duk_uint8_t *p, *p_start, *p_end;
+ duk_uint8_t buf[DUK_UNICODE_MAX_XUTF8_LENGTH * DUK__READABLE_STRING_MAXCHARS +
+ 2 /*quotes*/ + 3 /*periods*/];
+ duk_uint8_t *q;
+ duk_ucodepoint_t cp;
+ duk_small_uint_t nchars;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ DUK_ASSERT(h_input != NULL);
+ thr = (duk_hthread *) ctx;
+
+ p_start = (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h_input);
+ p_end = p_start + DUK_HSTRING_GET_BYTELEN(h_input);
+ p = p_start;
+ q = buf;
+
+ nchars = 0;
+ *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE;
+ for (;;) {
+ if (p >= p_end) {
+ break;
+ }
+ if (nchars == DUK__READABLE_STRING_MAXCHARS) {
+ *q++ = (duk_uint8_t) DUK_ASC_PERIOD;
+ *q++ = (duk_uint8_t) DUK_ASC_PERIOD;
+ *q++ = (duk_uint8_t) DUK_ASC_PERIOD;
+ break;
+ }
+ if (duk_unicode_decode_xutf8(thr, &p, p_start, p_end, &cp)) {
+ if (cp < 0x20 || cp == 0x7f || cp == DUK_ASC_SINGLEQUOTE || cp == DUK_ASC_BACKSLASH) {
+ DUK_ASSERT(DUK_UNICODE_MAX_XUTF8_LENGTH >= 4); /* estimate is valid */
+ DUK_ASSERT((cp >> 4) <= 0x0f);
+ *q++ = (duk_uint8_t) DUK_ASC_BACKSLASH;
+ *q++ = (duk_uint8_t) DUK_ASC_LC_X;
+ *q++ = (duk_uint8_t) duk_lc_digits[cp >> 4];
+ *q++ = (duk_uint8_t) duk_lc_digits[cp & 0x0f];
+ } else {
+ q += duk_unicode_encode_xutf8(cp, q);
+ }
+ } else {
+ p++; /* advance manually */
+ *q++ = (duk_uint8_t) DUK_ASC_QUESTION;
+ }
+ nchars++;
+ }
+ *q++ = (duk_uint8_t) DUK_ASC_SINGLEQUOTE;
+
+ duk_push_lstring(ctx, (const char *) buf, (duk_size_t) (q - buf));
+}
+
+DUK_INTERNAL const char *duk_push_string_tval_readable(duk_context *ctx, duk_tval *tv) {
+ duk_hthread *thr;
+
+ DUK_ASSERT_CTX_VALID(ctx);
+ thr = (duk_hthread *) ctx;
+ DUK_UNREF(thr);
+
+ if (tv == NULL) {
+ duk_push_string(ctx, "none");
+ } else {
+ switch (DUK_TVAL_GET_TAG(tv)) {
+ case DUK_TAG_STRING: {
+ duk__push_hstring_readable_unicode(ctx, DUK_TVAL_GET_STRING(tv));
+ break;
+ }
+ case DUK_TAG_OBJECT: {
+ duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
+ DUK_ASSERT(h != NULL);
+ duk_push_hobject_class_string(ctx, h);
+ break;
+ }
+ case DUK_TAG_BUFFER: {
+ /* XXX: Hex encoded, length limited buffer summary here? */
+ duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
+ DUK_ASSERT(h != NULL);
+ duk_push_sprintf(ctx, "[buffer:%ld]", (long) DUK_HBUFFER_GET_SIZE(h));
+ break;
+ }
+ case DUK_TAG_POINTER: {
+ /* Surround with parentheses like in JX, ensures NULL pointer
+ * is distinguishable from null value ("(null)" vs "null").
+ */
+ duk_push_tval(ctx, tv);
+ duk_push_sprintf(ctx, "(%s)", duk_to_string(ctx, -1));
+ duk_remove(ctx, -2);
+ break;
+ }
+ default: {
+ duk_push_tval(ctx, tv);
+ break;
+ }
+ }
+ }
+
+ return duk_to_string(ctx, -1);
+}
+
+DUK_INTERNAL const char *duk_push_string_readable(duk_context *ctx, duk_idx_t index) {
+ DUK_ASSERT_CTX_VALID(ctx);
+ return duk_push_string_tval_readable(ctx, duk_get_tval(ctx, index));
+}
+#endif /* !DUK_USE_PARANOID_ERRORS */
+
+#undef DUK__CHECK_SPACE
+#undef DUK__PACK_ARGS
+#undef DUK__READABLE_STRING_MAXCHARS