]> git.proxmox.com Git - ceph.git/blame - ceph/src/civetweb/src/third_party/duktape-1.3.0/src-separate/duk_api_stack.c
bump version to 12.2.12-pve1
[ceph.git] / ceph / src / civetweb / src / third_party / duktape-1.3.0 / src-separate / duk_api_stack.c
CommitLineData
7c673cae
FG
1/*
2 * API calls related to general value stack manipulation: resizing the value
3 * stack, pushing and popping values, type checking and reading values,
4 * coercing values, etc.
5 *
6 * Also contains internal functions (such as duk_get_tval()), defined
7 * in duk_api_internal.h, with semantics similar to the public API.
8 */
9
10/* XXX: repetition of stack pre-checks -> helper or macro or inline */
11/* XXX: shared api error strings, and perhaps even throw code for rare cases? */
12
13#include "duk_internal.h"
14
15/*
16 * Forward declarations
17 */
18
19DUK_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);
20
21/*
22 * Global state for working around missing variadic macros
23 */
24
25#ifndef DUK_USE_VARIADIC_MACROS
26DUK_EXTERNAL const char *duk_api_global_filename = NULL;
27DUK_EXTERNAL duk_int_t duk_api_global_line = 0;
28#endif
29
30/*
31 * Helpers
32 */
33
34#if defined(DUK_USE_VALSTACK_UNSAFE)
35/* Faster but value stack overruns are memory unsafe. */
36#define DUK__CHECK_SPACE() do { \
37 DUK_ASSERT(!(thr->valstack_top >= thr->valstack_end)); \
38 } while (0)
39#else
40#define DUK__CHECK_SPACE() do { \
41 if (thr->valstack_top >= thr->valstack_end) { \
42 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK); \
43 } \
44 } while (0)
45#endif
46
47DUK_LOCAL duk_int_t duk__api_coerce_d2i(duk_context *ctx, duk_idx_t index, duk_bool_t require) {
48 duk_hthread *thr;
49 duk_tval *tv;
50 duk_small_int_t c;
51 duk_double_t d;
52
53 thr = (duk_hthread *) ctx;
54
55 tv = duk_get_tval(ctx, index);
56 if (tv == NULL) {
57 goto error_notnumber;
58 }
59
60 /*
61 * Special cases like NaN and +/- Infinity are handled explicitly
62 * because a plain C coercion from double to int handles these cases
63 * in undesirable ways. For instance, NaN may coerce to INT_MIN
64 * (not zero), and INT_MAX + 1 may coerce to INT_MIN (not INT_MAX).
65 *
66 * This double-to-int coercion differs from ToInteger() because it
67 * has a finite range (ToInteger() allows e.g. +/- Infinity). It
68 * also differs from ToInt32() because the INT_MIN/INT_MAX clamping
69 * depends on the size of the int type on the platform. In particular,
70 * on platforms with a 64-bit int type, the full range is allowed.
71 */
72
73#if defined(DUK_USE_FASTINT)
74 if (DUK_TVAL_IS_FASTINT(tv)) {
75 duk_int64_t t = DUK_TVAL_GET_FASTINT(tv);
76#if (DUK_INT_MAX <= 0x7fffffffL)
77 /* Clamping only necessary for 32-bit ints. */
78 if (t < DUK_INT_MIN) {
79 t = DUK_INT_MIN;
80 } else if (t > DUK_INT_MAX) {
81 t = DUK_INT_MAX;
82 }
83#endif
84 return (duk_int_t) t;
85 }
86#endif
87
88 if (DUK_TVAL_IS_NUMBER(tv)) {
89 d = DUK_TVAL_GET_NUMBER(tv);
90 c = (duk_small_int_t) DUK_FPCLASSIFY(d);
91 if (c == DUK_FP_NAN) {
92 return 0;
93 } else if (d < (duk_double_t) DUK_INT_MIN) {
94 /* covers -Infinity */
95 return DUK_INT_MIN;
96 } else if (d > (duk_double_t) DUK_INT_MAX) {
97 /* covers +Infinity */
98 return DUK_INT_MAX;
99 } else {
100 /* coerce towards zero */
101 return (duk_int_t) d;
102 }
103 }
104
105 error_notnumber:
106
107 if (require) {
108 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_NUMBER);
109 /* not reachable */
110 }
111 return 0;
112}
113
114DUK_LOCAL duk_uint_t duk__api_coerce_d2ui(duk_context *ctx, duk_idx_t index, duk_bool_t require) {
115 duk_hthread *thr;
116 duk_tval *tv;
117 duk_small_int_t c;
118 duk_double_t d;
119
120 /* Same as above but for unsigned int range. */
121
122 thr = (duk_hthread *) ctx;
123
124 tv = duk_get_tval(ctx, index);
125 if (tv == NULL) {
126 goto error_notnumber;
127 }
128
129#if defined(DUK_USE_FASTINT)
130 if (DUK_TVAL_IS_FASTINT(tv)) {
131 duk_int64_t t = DUK_TVAL_GET_FASTINT(tv);
132 if (t < 0) {
133 t = 0;
134 }
135#if (DUK_UINT_MAX <= 0xffffffffUL)
136 /* Clamping only necessary for 32-bit ints. */
137 else if (t > DUK_UINT_MAX) {
138 t = DUK_UINT_MAX;
139 }
140#endif
141 return (duk_uint_t) t;
142 }
143#endif
144
145 if (DUK_TVAL_IS_NUMBER(tv)) {
146 d = DUK_TVAL_GET_NUMBER(tv);
147 c = (duk_small_int_t) DUK_FPCLASSIFY(d);
148 if (c == DUK_FP_NAN) {
149 return 0;
150 } else if (d < 0.0) {
151 /* covers -Infinity */
152 return (duk_uint_t) 0;
153 } else if (d > (duk_double_t) DUK_UINT_MAX) {
154 /* covers +Infinity */
155 return (duk_uint_t) DUK_UINT_MAX;
156 } else {
157 /* coerce towards zero */
158 return (duk_uint_t) d;
159 }
160 }
161
162 error_notnumber:
163
164 if (require) {
165 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_NUMBER);
166 /* not reachable */
167 }
168 return 0;
169}
170
171/*
172 * Stack index validation/normalization and getting a stack duk_tval ptr.
173 *
174 * These are called by many API entrypoints so the implementations must be
175 * fast and "inlined".
176 *
177 * There's some repetition because of this; keep the functions in sync.
178 */
179
180DUK_EXTERNAL duk_idx_t duk_normalize_index(duk_context *ctx, duk_idx_t index) {
181 duk_hthread *thr = (duk_hthread *) ctx;
182 duk_idx_t vs_size;
183
184 DUK_ASSERT_CTX_VALID(ctx);
185 DUK_ASSERT(DUK_INVALID_INDEX < 0);
186
187 /* Care must be taken to avoid pointer wrapping in the index
188 * validation. For instance, on a 32-bit platform with 8-byte
189 * duk_tval the index 0x20000000UL would wrap the memory space
190 * once.
191 */
192
193 /* Assume value stack sizes (in elements) fits into duk_idx_t. */
194 vs_size = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
195 DUK_ASSERT(vs_size >= 0);
196
197 if (index < 0) {
198 index = vs_size + index;
199 if (DUK_UNLIKELY(index < 0)) {
200 /* Also catches index == DUK_INVALID_INDEX: vs_size >= 0
201 * so that vs_size + DUK_INVALID_INDEX cannot underflow
202 * and will always be negative.
203 */
204 return DUK_INVALID_INDEX;
205 }
206 } else {
207 /* since index non-negative */
208 DUK_ASSERT(index != DUK_INVALID_INDEX);
209
210 if (DUK_UNLIKELY(index >= vs_size)) {
211 return DUK_INVALID_INDEX;
212 }
213 }
214
215 DUK_ASSERT(index >= 0);
216 DUK_ASSERT(index < vs_size);
217 return index;
218}
219
220DUK_EXTERNAL duk_idx_t duk_require_normalize_index(duk_context *ctx, duk_idx_t index) {
221 duk_hthread *thr = (duk_hthread *) ctx;
222 duk_idx_t vs_size;
223
224 DUK_ASSERT_CTX_VALID(ctx);
225 DUK_ASSERT(DUK_INVALID_INDEX < 0);
226
227 vs_size = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
228 DUK_ASSERT(vs_size >= 0);
229
230 if (index < 0) {
231 index = vs_size + index;
232 if (DUK_UNLIKELY(index < 0)) {
233 goto invalid_index;
234 }
235 } else {
236 DUK_ASSERT(index != DUK_INVALID_INDEX);
237 if (DUK_UNLIKELY(index >= vs_size)) {
238 goto invalid_index;
239 }
240 }
241
242 DUK_ASSERT(index >= 0);
243 DUK_ASSERT(index < vs_size);
244 return index;
245
246 invalid_index:
247 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_INDEX);
248 return 0; /* unreachable */
249}
250
251DUK_INTERNAL duk_tval *duk_get_tval(duk_context *ctx, duk_idx_t index) {
252 duk_hthread *thr = (duk_hthread *) ctx;
253 duk_idx_t vs_size;
254
255 DUK_ASSERT_CTX_VALID(ctx);
256 DUK_ASSERT(DUK_INVALID_INDEX < 0);
257
258 vs_size = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
259 DUK_ASSERT(vs_size >= 0);
260
261 if (index < 0) {
262 index = vs_size + index;
263 if (DUK_UNLIKELY(index < 0)) {
264 return NULL;
265 }
266 } else {
267 DUK_ASSERT(index != DUK_INVALID_INDEX);
268 if (DUK_UNLIKELY(index >= vs_size)) {
269 return NULL;
270 }
271 }
272
273 DUK_ASSERT(index >= 0);
274 DUK_ASSERT(index < vs_size);
275 return thr->valstack_bottom + index;
276}
277
278DUK_INTERNAL duk_tval *duk_require_tval(duk_context *ctx, duk_idx_t index) {
279 duk_hthread *thr = (duk_hthread *) ctx;
280 duk_idx_t vs_size;
281
282 DUK_ASSERT_CTX_VALID(ctx);
283 DUK_ASSERT(DUK_INVALID_INDEX < 0);
284
285 vs_size = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
286 DUK_ASSERT(vs_size >= 0);
287
288 if (index < 0) {
289 index = vs_size + index;
290 if (DUK_UNLIKELY(index < 0)) {
291 goto invalid_index;
292 }
293 } else {
294 DUK_ASSERT(index != DUK_INVALID_INDEX);
295 if (DUK_UNLIKELY(index >= vs_size)) {
296 goto invalid_index;
297 }
298 }
299
300 DUK_ASSERT(index >= 0);
301 DUK_ASSERT(index < vs_size);
302 return thr->valstack_bottom + index;
303
304 invalid_index:
305 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_INDEX);
306 return NULL;
307}
308
309/* Non-critical. */
310DUK_EXTERNAL duk_bool_t duk_is_valid_index(duk_context *ctx, duk_idx_t index) {
311 DUK_ASSERT_CTX_VALID(ctx);
312 DUK_ASSERT(DUK_INVALID_INDEX < 0);
313
314 return (duk_normalize_index(ctx, index) >= 0);
315}
316
317/* Non-critical. */
318DUK_EXTERNAL void duk_require_valid_index(duk_context *ctx, duk_idx_t index) {
319 duk_hthread *thr = (duk_hthread *) ctx;
320
321 DUK_ASSERT_CTX_VALID(ctx);
322 DUK_ASSERT(DUK_INVALID_INDEX < 0);
323
324 if (duk_normalize_index(ctx, index) < 0) {
325 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_INDEX);
326 }
327}
328
329/*
330 * Value stack top handling
331 */
332
333DUK_EXTERNAL duk_idx_t duk_get_top(duk_context *ctx) {
334 duk_hthread *thr = (duk_hthread *) ctx;
335
336 DUK_ASSERT_CTX_VALID(ctx);
337
338 return (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
339}
340
341/* set stack top within currently allocated range, but don't reallocate */
342DUK_EXTERNAL void duk_set_top(duk_context *ctx, duk_idx_t index) {
343 duk_hthread *thr = (duk_hthread *) ctx;
344 duk_idx_t vs_size;
345 duk_idx_t vs_limit;
346 duk_idx_t count;
347 duk_tval tv_tmp;
348 duk_tval *tv;
349
350 DUK_ASSERT_CTX_VALID(ctx);
351 DUK_ASSERT(DUK_INVALID_INDEX < 0);
352
353 vs_size = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
354 vs_limit = (duk_idx_t) (thr->valstack_end - thr->valstack_bottom);
355
356 if (index < 0) {
357 /* Negative indices are always within allocated stack but
358 * must not go below zero index.
359 */
360 index = vs_size + index;
361 if (index < 0) {
362 /* Also catches index == DUK_INVALID_INDEX. */
363 goto invalid_index;
364 }
365 } else {
366 /* Positive index can be higher than valstack top but must
367 * not go above allocated stack (equality is OK).
368 */
369 if (index > vs_limit) {
370 goto invalid_index;
371 }
372 }
373 DUK_ASSERT(index >= 0);
374 DUK_ASSERT(index <= vs_limit);
375
376 if (index >= vs_size) {
377 /* Stack size increases or stays the same. Fill the new
378 * entries (if any) with undefined. No pointer stability
379 * issues here so we can use a running pointer.
380 */
381
382 tv = thr->valstack_top;
383 count = index - vs_size;
384 DUK_ASSERT(count >= 0);
385 while (count > 0) {
386 /* no need to decref previous or new value */
387 count--;
388 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED_UNUSED(tv));
389 DUK_TVAL_SET_UNDEFINED_ACTUAL(tv);
390 tv++;
391 }
392 thr->valstack_top = tv;
393 } else {
394 /* Stack size decreases, DECREF entries which are above the
395 * new top. Each DECREF potentially invalidates valstack
396 * pointers, so don't hold on to pointers. The valstack top
397 * must also be updated on every loop in case a GC is triggered.
398 */
399
400 /* XXX: Here it would be useful to have a DECREF macro which
401 * doesn't need a NULL check, and does refzero queueing without
402 * running the refzero algorithm. There would be no pointer
403 * instability in this case, and code could be inlined. After
404 * the loop, one call to refzero would be needed.
405 */
406
407 count = vs_size - index;
408 DUK_ASSERT(count > 0);
409
410 while (count > 0) {
411 count--;
412 tv = --thr->valstack_top; /* tv -> value just before prev top value */
413 DUK_ASSERT(tv >= thr->valstack_bottom);
414 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
415 DUK_TVAL_SET_UNDEFINED_UNUSED(tv);
416 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
417
418 /* XXX: fast primitive to set a bunch of values to UNDEFINED_UNUSED */
419
420 }
421 }
422 return;
423
424 invalid_index:
425 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_INDEX);
426}
427
428DUK_EXTERNAL duk_idx_t duk_get_top_index(duk_context *ctx) {
429 duk_hthread *thr = (duk_hthread *) ctx;
430 duk_idx_t ret;
431
432 DUK_ASSERT_CTX_VALID(ctx);
433
434 ret = ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
435 if (DUK_UNLIKELY(ret < 0)) {
436 /* Return invalid index; if caller uses this without checking
437 * in another API call, the index won't map to a valid stack
438 * entry.
439 */
440 return DUK_INVALID_INDEX;
441 }
442 return ret;
443}
444
445DUK_EXTERNAL duk_idx_t duk_require_top_index(duk_context *ctx) {
446 duk_hthread *thr = (duk_hthread *) ctx;
447 duk_idx_t ret;
448
449 DUK_ASSERT_CTX_VALID(ctx);
450
451 ret = ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
452 if (DUK_UNLIKELY(ret < 0)) {
453 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_INDEX);
454 }
455 return ret;
456}
457
458/*
459 * Value stack resizing.
460 *
461 * This resizing happens above the current "top": the value stack can be
462 * grown or shrunk, but the "top" is not affected. The value stack cannot
463 * be resized to a size below the current "top".
464 *
465 * The low level reallocation primitive must carefully recompute all value
466 * stack pointers, and must also work if ALL pointers are NULL. The resize
467 * is quite tricky because the valstack realloc may cause a mark-and-sweep,
468 * which may run finalizers. Running finalizers may resize the valstack
469 * recursively (the same value stack we're working on). So, after realloc
470 * returns, we know that the valstack "top" should still be the same (there
471 * should not be live values above the "top"), but its underlying size and
472 * pointer may have changed.
473 */
474
475/* XXX: perhaps refactor this to allow caller to specify some parameters, or
476 * at least a 'compact' flag which skips any spare or round-up .. useful for
477 * emergency gc.
478 */
479
480DUK_LOCAL duk_bool_t duk__resize_valstack(duk_context *ctx, duk_size_t new_size) {
481 duk_hthread *thr = (duk_hthread *) ctx;
482 duk_ptrdiff_t old_bottom_offset;
483 duk_ptrdiff_t old_top_offset;
484 duk_ptrdiff_t old_end_offset_post;
485#ifdef DUK_USE_DEBUG
486 duk_ptrdiff_t old_end_offset_pre;
487 duk_tval *old_valstack_pre;
488 duk_tval *old_valstack_post;
489#endif
490 duk_tval *new_valstack;
491 duk_tval *p;
492 duk_size_t new_alloc_size;
493
494 DUK_ASSERT_CTX_VALID(ctx);
495 DUK_ASSERT(thr != NULL);
496 DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
497 DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
498 DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
499 DUK_ASSERT((duk_size_t) (thr->valstack_top - thr->valstack) <= new_size); /* can't resize below 'top' */
500 DUK_ASSERT(new_size <= thr->valstack_max); /* valstack limit caller has check, prevents wrapping */
501 DUK_ASSERT(new_size <= DUK_SIZE_MAX / sizeof(duk_tval)); /* specific assert for wrapping */
502
503 /* get pointer offsets for tweaking below */
504 old_bottom_offset = (((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack));
505 old_top_offset = (((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack));
506#ifdef DUK_USE_DEBUG
507 old_end_offset_pre = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack)); /* not very useful, used for debugging */
508 old_valstack_pre = thr->valstack;
509#endif
510
511 /* Allocate a new valstack.
512 *
513 * Note: cannot use a plain DUK_REALLOC() because a mark-and-sweep may
514 * invalidate the original thr->valstack base pointer inside the realloc
515 * process. See doc/memory-management.rst.
516 */
517
518 new_alloc_size = sizeof(duk_tval) * new_size;
519 new_valstack = (duk_tval *) DUK_REALLOC_INDIRECT(thr->heap, duk_hthread_get_valstack_ptr, (void *) thr, new_alloc_size);
520 if (!new_valstack) {
521 /* Because new_size != 0, if condition doesn't need to be
522 * (new_valstack != NULL || new_size == 0).
523 */
524 DUK_ASSERT(new_size != 0);
525 DUK_D(DUK_DPRINT("failed to resize valstack to %lu entries (%lu bytes)",
526 (unsigned long) new_size, (unsigned long) new_alloc_size));
527 return 0;
528 }
529
530 /* Note: the realloc may have triggered a mark-and-sweep which may
531 * have resized our valstack internally. However, the mark-and-sweep
532 * MUST NOT leave the stack bottom/top in a different state. Particular
533 * assumptions and facts:
534 *
535 * - The thr->valstack pointer may be different after realloc,
536 * and the offset between thr->valstack_end <-> thr->valstack
537 * may have changed.
538 * - The offset between thr->valstack_bottom <-> thr->valstack
539 * and thr->valstack_top <-> thr->valstack MUST NOT have changed,
540 * because mark-and-sweep must adhere to a strict stack policy.
541 * In other words, logical bottom and top MUST NOT have changed.
542 * - All values above the top are unreachable but are initialized
543 * to UNDEFINED_UNUSED, up to the post-realloc valstack_end.
544 * - 'old_end_offset' must be computed after realloc to be correct.
545 */
546
547 DUK_ASSERT((((duk_uint8_t *) thr->valstack_bottom) - ((duk_uint8_t *) thr->valstack)) == old_bottom_offset);
548 DUK_ASSERT((((duk_uint8_t *) thr->valstack_top) - ((duk_uint8_t *) thr->valstack)) == old_top_offset);
549
550 /* success, fixup pointers */
551 old_end_offset_post = (((duk_uint8_t *) thr->valstack_end) - ((duk_uint8_t *) thr->valstack)); /* must be computed after realloc */
552#ifdef DUK_USE_DEBUG
553 old_valstack_post = thr->valstack;
554#endif
555 thr->valstack = new_valstack;
556 thr->valstack_end = new_valstack + new_size;
557 thr->valstack_bottom = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_bottom_offset);
558 thr->valstack_top = (duk_tval *) (void *) ((duk_uint8_t *) new_valstack + old_top_offset);
559
560 DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
561 DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
562 DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
563
564 /* useful for debugging */
565#ifdef DUK_USE_DEBUG
566 if (old_end_offset_pre != old_end_offset_post) {
567 DUK_D(DUK_DPRINT("valstack was resized during valstack_resize(), probably by mark-and-sweep; "
568 "end offset changed: %lu -> %lu",
569 (unsigned long) old_end_offset_pre,
570 (unsigned long) old_end_offset_post));
571 }
572 if (old_valstack_pre != old_valstack_post) {
573 DUK_D(DUK_DPRINT("valstack pointer changed during valstack_resize(), probably by mark-and-sweep: %p -> %p",
574 (void *) old_valstack_pre,
575 (void *) old_valstack_post));
576 }
577#endif
578
579 DUK_DD(DUK_DDPRINT("resized valstack to %lu elements (%lu bytes), bottom=%ld, top=%ld, "
580 "new pointers: start=%p end=%p bottom=%p top=%p",
581 (unsigned long) new_size, (unsigned long) new_alloc_size,
582 (long) (thr->valstack_bottom - thr->valstack),
583 (long) (thr->valstack_top - thr->valstack),
584 (void *) thr->valstack, (void *) thr->valstack_end,
585 (void *) thr->valstack_bottom, (void *) thr->valstack_top));
586
587 /* init newly allocated slots (only) */
588 p = (duk_tval *) (void *) ((duk_uint8_t *) thr->valstack + old_end_offset_post);
589 while (p < thr->valstack_end) {
590 /* never executed if new size is smaller */
591 DUK_TVAL_SET_UNDEFINED_UNUSED(p);
592 p++;
593 }
594
595 /* assertion check: we maintain elements above top in known state */
596#ifdef DUK_USE_ASSERTIONS
597 p = thr->valstack_top;
598 while (p < thr->valstack_end) {
599 /* everything above old valstack top should be preinitialized now */
600 DUK_ASSERT(DUK_TVAL_IS_UNDEFINED_UNUSED(p));
601 p++;
602 }
603#endif
604 return 1;
605}
606
607DUK_INTERNAL
608duk_bool_t duk_valstack_resize_raw(duk_context *ctx,
609 duk_size_t min_new_size,
610 duk_small_uint_t flags) {
611 duk_hthread *thr = (duk_hthread *) ctx;
612 duk_size_t old_size;
613 duk_size_t new_size;
614 duk_bool_t is_shrink = 0;
615 duk_small_uint_t shrink_flag = (flags & DUK_VSRESIZE_FLAG_SHRINK);
616 duk_small_uint_t compact_flag = (flags & DUK_VSRESIZE_FLAG_COMPACT);
617 duk_small_uint_t throw_flag = (flags & DUK_VSRESIZE_FLAG_THROW);
618
619 DUK_DDD(DUK_DDDPRINT("check valstack resize: min_new_size=%lu, curr_size=%ld, curr_top=%ld, "
620 "curr_bottom=%ld, shrink=%d, compact=%d, throw=%d",
621 (unsigned long) min_new_size,
622 (long) (thr->valstack_end - thr->valstack),
623 (long) (thr->valstack_top - thr->valstack),
624 (long) (thr->valstack_bottom - thr->valstack),
625 (int) shrink_flag, (int) compact_flag, (int) throw_flag));
626
627 DUK_ASSERT_CTX_VALID(ctx);
628 DUK_ASSERT(thr != NULL);
629 DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
630 DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
631 DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
632
633 old_size = (duk_size_t) (thr->valstack_end - thr->valstack);
634
635 if (min_new_size <= old_size) {
636 is_shrink = 1;
637 if (!shrink_flag ||
638 old_size - min_new_size < DUK_VALSTACK_SHRINK_THRESHOLD) {
639 DUK_DDD(DUK_DDDPRINT("no need to grow or shrink valstack"));
640 return 1;
641 }
642 }
643
644 new_size = min_new_size;
645 if (!compact_flag) {
646 if (is_shrink) {
647 /* shrink case; leave some spare */
648 new_size += DUK_VALSTACK_SHRINK_SPARE;
649 }
650
651 /* round up roughly to next 'grow step' */
652 new_size = (new_size / DUK_VALSTACK_GROW_STEP + 1) * DUK_VALSTACK_GROW_STEP;
653 }
654
655 DUK_DD(DUK_DDPRINT("want to %s valstack: %lu -> %lu elements (min_new_size %lu)",
656 (const char *) (new_size > old_size ? "grow" : "shrink"),
657 (unsigned long) old_size, (unsigned long) new_size,
658 (unsigned long) min_new_size));
659
660 if (new_size > thr->valstack_max) {
661 /* Note: may be triggered even if minimal new_size would not reach the limit,
662 * plan limit accordingly (taking DUK_VALSTACK_GROW_STEP into account).
663 */
664 if (throw_flag) {
665 DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_VALSTACK_LIMIT);
666 } else {
667 return 0;
668 }
669 }
670
671 /*
672 * When resizing the valstack, a mark-and-sweep may be triggered for
673 * the allocation of the new valstack. If the mark-and-sweep needs
674 * to use our thread for something, it may cause *the same valstack*
675 * to be resized recursively. This happens e.g. when mark-and-sweep
676 * finalizers are called. This is taken into account carefully in
677 * duk__resize_valstack().
678 *
679 * 'new_size' is known to be <= valstack_max, which ensures that
680 * size_t and pointer arithmetic won't wrap in duk__resize_valstack().
681 */
682
683 if (!duk__resize_valstack(ctx, new_size)) {
684 if (is_shrink) {
685 DUK_DD(DUK_DDPRINT("valstack resize failed, but is a shrink, ignore"));
686 return 1;
687 }
688
689 DUK_DD(DUK_DDPRINT("valstack resize failed"));
690
691 if (throw_flag) {
692 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_FAILED_TO_EXTEND_VALSTACK);
693 } else {
694 return 0;
695 }
696 }
697
698 DUK_DDD(DUK_DDDPRINT("valstack resize successful"));
699 return 1;
700}
701
702DUK_EXTERNAL duk_bool_t duk_check_stack(duk_context *ctx, duk_idx_t extra) {
703 duk_hthread *thr = (duk_hthread *) ctx;
704 duk_size_t min_new_size;
705
706 DUK_ASSERT_CTX_VALID(ctx);
707 DUK_ASSERT(thr != NULL);
708
709 if (DUK_UNLIKELY(extra < 0)) {
710 /* Clamping to zero makes the API more robust to calling code
711 * calculation errors.
712 */
713 extra = 0;
714 }
715
716 min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA;
717 return duk_valstack_resize_raw(ctx,
718 min_new_size, /* min_new_size */
719 0 /* no shrink */ | /* flags */
720 0 /* no compact */ |
721 0 /* no throw */);
722}
723
724DUK_EXTERNAL void duk_require_stack(duk_context *ctx, duk_idx_t extra) {
725 duk_hthread *thr = (duk_hthread *) ctx;
726 duk_size_t min_new_size;
727
728 DUK_ASSERT_CTX_VALID(ctx);
729 DUK_ASSERT(thr != NULL);
730
731 if (DUK_UNLIKELY(extra < 0)) {
732 /* Clamping to zero makes the API more robust to calling code
733 * calculation errors.
734 */
735 extra = 0;
736 }
737
738 min_new_size = (thr->valstack_top - thr->valstack) + extra + DUK_VALSTACK_INTERNAL_EXTRA;
739 (void) duk_valstack_resize_raw(ctx,
740 min_new_size, /* min_new_size */
741 0 /* no shrink */ | /* flags */
742 0 /* no compact */ |
743 DUK_VSRESIZE_FLAG_THROW);
744}
745
746DUK_EXTERNAL duk_bool_t duk_check_stack_top(duk_context *ctx, duk_idx_t top) {
747 duk_size_t min_new_size;
748
749 DUK_ASSERT_CTX_VALID(ctx);
750
751 if (DUK_UNLIKELY(top < 0)) {
752 /* Clamping to zero makes the API more robust to calling code
753 * calculation errors.
754 */
755 top = 0;
756 }
757
758 min_new_size = top + DUK_VALSTACK_INTERNAL_EXTRA;
759 return duk_valstack_resize_raw(ctx,
760 min_new_size, /* min_new_size */
761 0 /* no shrink */ | /* flags */
762 0 /* no compact */ |
763 0 /* no throw */);
764}
765
766DUK_EXTERNAL void duk_require_stack_top(duk_context *ctx, duk_idx_t top) {
767 duk_size_t min_new_size;
768
769 DUK_ASSERT_CTX_VALID(ctx);
770
771 if (DUK_UNLIKELY(top < 0)) {
772 /* Clamping to zero makes the API more robust to calling code
773 * calculation errors.
774 */
775 top = 0;
776 }
777
778 min_new_size = top + DUK_VALSTACK_INTERNAL_EXTRA;
779 (void) duk_valstack_resize_raw(ctx,
780 min_new_size, /* min_new_size */
781 0 /* no shrink */ | /* flags */
782 0 /* no compact */ |
783 DUK_VSRESIZE_FLAG_THROW);
784}
785
786/*
787 * Basic stack manipulation: swap, dup, insert, replace, etc
788 */
789
790DUK_EXTERNAL void duk_swap(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
791 duk_tval *tv1;
792 duk_tval *tv2;
793 duk_tval tv_tmp;
794
795 DUK_ASSERT_CTX_VALID(ctx);
796
797 tv1 = duk_require_tval(ctx, index1);
798 DUK_ASSERT(tv1 != NULL);
799 tv2 = duk_require_tval(ctx, index2);
800 DUK_ASSERT(tv2 != NULL);
801
802 /* If tv1==tv2 this is a NOP, no check is needed */
803 DUK_TVAL_SET_TVAL(&tv_tmp, tv1);
804 DUK_TVAL_SET_TVAL(tv1, tv2);
805 DUK_TVAL_SET_TVAL(tv2, &tv_tmp);
806}
807
808DUK_EXTERNAL void duk_swap_top(duk_context *ctx, duk_idx_t index) {
809 DUK_ASSERT_CTX_VALID(ctx);
810
811 duk_swap(ctx, index, -1);
812}
813
814DUK_EXTERNAL void duk_dup(duk_context *ctx, duk_idx_t from_index) {
815 duk_hthread *thr;
816 duk_tval *tv_from;
817 duk_tval *tv_to;
818
819 DUK_ASSERT_CTX_VALID(ctx);
820 thr = (duk_hthread *) ctx;
821 DUK__CHECK_SPACE();
822
823 tv_from = duk_require_tval(ctx, from_index);
824 tv_to = thr->valstack_top++;
825 DUK_ASSERT(tv_from != NULL);
826 DUK_ASSERT(tv_to != NULL);
827 DUK_TVAL_SET_TVAL(tv_to, tv_from);
828 DUK_TVAL_INCREF(thr, tv_to); /* no side effects */
829}
830
831DUK_EXTERNAL void duk_dup_top(duk_context *ctx) {
832 duk_hthread *thr;
833 duk_tval *tv_from;
834 duk_tval *tv_to;
835
836 DUK_ASSERT_CTX_VALID(ctx);
837 thr = (duk_hthread *) ctx;
838 DUK__CHECK_SPACE();
839
840 if (thr->valstack_top - thr->valstack_bottom <= 0) {
841 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_INDEX);
842 }
843 tv_from = thr->valstack_top - 1;
844 tv_to = thr->valstack_top++;
845 DUK_ASSERT(tv_from != NULL);
846 DUK_ASSERT(tv_to != NULL);
847 DUK_TVAL_SET_TVAL(tv_to, tv_from);
848 DUK_TVAL_INCREF(thr, tv_to); /* no side effects */
849}
850
851DUK_EXTERNAL void duk_insert(duk_context *ctx, duk_idx_t to_index) {
852 duk_tval *p;
853 duk_tval *q;
854 duk_tval tv_tmp;
855 duk_size_t nbytes;
856
857 DUK_ASSERT_CTX_VALID(ctx);
858
859 p = duk_require_tval(ctx, to_index);
860 DUK_ASSERT(p != NULL);
861 q = duk_require_tval(ctx, -1);
862 DUK_ASSERT(q != NULL);
863
864 DUK_ASSERT(q >= p);
865
866 /* nbytes
867 * <--------->
868 * [ ... | p | x | x | q ]
869 * => [ ... | q | p | x | x ]
870 */
871
872 nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */
873
874 DUK_DDD(DUK_DDDPRINT("duk_insert: to_index=%ld, p=%p, q=%p, nbytes=%lu",
875 (long) to_index, (void *) p, (void *) q, (unsigned long) nbytes));
876
877 /* No net refcount changes. */
878
879 if (nbytes > 0) {
880 DUK_TVAL_SET_TVAL(&tv_tmp, q);
881 DUK_ASSERT(nbytes > 0);
882 DUK_MEMMOVE((void *) (p + 1), (void *) p, nbytes);
883 DUK_TVAL_SET_TVAL(p, &tv_tmp);
884 } else {
885 /* nop: insert top to top */
886 DUK_ASSERT(nbytes == 0);
887 DUK_ASSERT(p == q);
888 }
889}
890
891DUK_EXTERNAL void duk_replace(duk_context *ctx, duk_idx_t to_index) {
892 duk_hthread *thr = (duk_hthread *) ctx;
893 duk_tval *tv1;
894 duk_tval *tv2;
895 duk_tval tv_tmp;
896
897 DUK_ASSERT_CTX_VALID(ctx);
898
899 tv1 = duk_require_tval(ctx, -1);
900 DUK_ASSERT(tv1 != NULL);
901 tv2 = duk_require_tval(ctx, to_index);
902 DUK_ASSERT(tv2 != NULL);
903
904 /* For tv1 == tv2, both pointing to stack top, the end result
905 * is same as duk_pop(ctx).
906 */
907
908 DUK_TVAL_SET_TVAL(&tv_tmp, tv2);
909 DUK_TVAL_SET_TVAL(tv2, tv1);
910 DUK_TVAL_SET_UNDEFINED_UNUSED(tv1);
911 thr->valstack_top--;
912 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
913}
914
915DUK_EXTERNAL void duk_copy(duk_context *ctx, duk_idx_t from_index, duk_idx_t to_index) {
916 duk_hthread *thr = (duk_hthread *) ctx;
917 duk_tval *tv1;
918 duk_tval *tv2;
919 duk_tval tv_tmp;
920
921 DUK_ASSERT_CTX_VALID(ctx);
922 DUK_UNREF(thr); /* w/o refcounting */
923
924 tv1 = duk_require_tval(ctx, from_index);
925 DUK_ASSERT(tv1 != NULL);
926 tv2 = duk_require_tval(ctx, to_index);
927 DUK_ASSERT(tv2 != NULL);
928
929 /* For tv1 == tv2, this is a no-op (no explicit check needed). */
930
931 DUK_TVAL_SET_TVAL(&tv_tmp, tv2);
932 DUK_TVAL_SET_TVAL(tv2, tv1);
933 DUK_TVAL_INCREF(thr, tv2); /* no side effects */
934 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
935}
936
937DUK_EXTERNAL void duk_remove(duk_context *ctx, duk_idx_t index) {
938 duk_hthread *thr = (duk_hthread *) ctx;
939 duk_tval *p;
940 duk_tval *q;
941#ifdef DUK_USE_REFERENCE_COUNTING
942 duk_tval tv_tmp;
943#endif
944 duk_size_t nbytes;
945
946 DUK_ASSERT_CTX_VALID(ctx);
947
948 p = duk_require_tval(ctx, index);
949 DUK_ASSERT(p != NULL);
950 q = duk_require_tval(ctx, -1);
951 DUK_ASSERT(q != NULL);
952
953 DUK_ASSERT(q >= p);
954
955 /* nbytes zero size case
956 * <--------->
957 * [ ... | p | x | x | q ] [ ... | p==q ]
958 * => [ ... | x | x | q ] [ ... ]
959 */
960
961#ifdef DUK_USE_REFERENCE_COUNTING
962 /* use a temp: decref only when valstack reachable values are correct */
963 DUK_TVAL_SET_TVAL(&tv_tmp, p);
964#endif
965
966 nbytes = (duk_size_t) (((duk_uint8_t *) q) - ((duk_uint8_t *) p)); /* Note: 'q' is top-1 */
967 DUK_MEMMOVE(p, p + 1, nbytes); /* zero size not an issue: pointers are valid */
968
969 DUK_TVAL_SET_UNDEFINED_UNUSED(q);
970 thr->valstack_top--;
971
972#ifdef DUK_USE_REFERENCE_COUNTING
973 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
974#endif
975}
976
977/*
978 * Stack slice primitives
979 */
980
981DUK_EXTERNAL void duk_xcopymove_raw(duk_context *to_ctx, duk_context *from_ctx, duk_idx_t count, duk_bool_t is_copy) {
982 duk_hthread *to_thr = (duk_hthread *) to_ctx;
983 duk_hthread *from_thr = (duk_hthread *) from_ctx;
984 void *src;
985 duk_size_t nbytes;
986 duk_tval *p;
987 duk_tval *q;
988
989 /* XXX: several pointer comparison issues here */
990
991 DUK_ASSERT_CTX_VALID(to_ctx);
992 DUK_ASSERT_CTX_VALID(from_ctx);
993 DUK_ASSERT(to_ctx != NULL);
994 DUK_ASSERT(from_ctx != NULL);
995
996 if (to_ctx == from_ctx) {
997 DUK_ERROR(to_thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_CONTEXT);
998 return;
999 }
1000 if ((count < 0) ||
1001 (count > (duk_idx_t) to_thr->valstack_max)) {
1002 /* Maximum value check ensures 'nbytes' won't wrap below. */
1003 DUK_ERROR(to_thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_COUNT);
1004 return;
1005 }
1006
1007 nbytes = sizeof(duk_tval) * count;
1008 if (nbytes == 0) {
1009 return;
1010 }
1011 DUK_ASSERT(to_thr->valstack_top <= to_thr->valstack_end);
1012 if ((duk_size_t) ((duk_uint8_t *) to_thr->valstack_end - (duk_uint8_t *) to_thr->valstack_top) < nbytes) {
1013 DUK_ERROR(to_thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
1014 }
1015 src = (void *) ((duk_uint8_t *) from_thr->valstack_top - nbytes);
1016 if (src < (void *) from_thr->valstack_bottom) {
1017 DUK_ERROR(to_thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_COUNT);
1018 }
1019
1020 /* copy values (no overlap even if to_ctx == from_ctx; that's not
1021 * allowed now anyway)
1022 */
1023 DUK_ASSERT(nbytes > 0);
1024 DUK_MEMCPY((void *) to_thr->valstack_top, src, nbytes);
1025
1026 p = to_thr->valstack_top;
1027 to_thr->valstack_top = (duk_tval *) (void *) (((duk_uint8_t *) p) + nbytes);
1028
1029 if (is_copy) {
1030 /* incref copies, keep originals */
1031 q = to_thr->valstack_top;
1032 while (p < q) {
1033 DUK_TVAL_INCREF(to_thr, p); /* no side effects */
1034 p++;
1035 }
1036 } else {
1037 /* no net refcount change */
1038 p = from_thr->valstack_top;
1039 q = (duk_tval *) (void *) (((duk_uint8_t *) p) - nbytes);
1040 from_thr->valstack_top = q;
1041
1042 /* elements above stack top are kept UNUSED */
1043 while (p > q) {
1044 p--;
1045 DUK_TVAL_SET_UNDEFINED_UNUSED(p);
1046
1047 /* XXX: fast primitive to set a bunch of values to UNDEFINED_UNUSED */
1048 }
1049 }
1050}
1051
1052/*
1053 * Get/require
1054 */
1055
1056DUK_EXTERNAL void duk_require_undefined(duk_context *ctx, duk_idx_t index) {
1057 duk_hthread *thr = (duk_hthread *) ctx;
1058 duk_tval *tv;
1059
1060 DUK_ASSERT_CTX_VALID(ctx);
1061
1062 tv = duk_get_tval(ctx, index);
1063 if (tv && DUK_TVAL_IS_UNDEFINED(tv)) {
1064 /* Note: accept both 'actual' and 'unused' undefined */
1065 return;
1066 }
1067 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_UNDEFINED);
1068}
1069
1070DUK_EXTERNAL void duk_require_null(duk_context *ctx, duk_idx_t index) {
1071 duk_hthread *thr = (duk_hthread *) ctx;
1072 duk_tval *tv;
1073
1074 DUK_ASSERT_CTX_VALID(ctx);
1075
1076 tv = duk_get_tval(ctx, index);
1077 if (tv && DUK_TVAL_IS_NULL(tv)) {
1078 return;
1079 }
1080 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_NULL);
1081 return; /* not reachable */
1082}
1083
1084DUK_EXTERNAL duk_bool_t duk_get_boolean(duk_context *ctx, duk_idx_t index) {
1085 duk_bool_t ret = 0; /* default: false */
1086 duk_tval *tv;
1087
1088 DUK_ASSERT_CTX_VALID(ctx);
1089
1090 tv = duk_get_tval(ctx, index);
1091 if (tv && DUK_TVAL_IS_BOOLEAN(tv)) {
1092 ret = DUK_TVAL_GET_BOOLEAN(tv);
1093 }
1094
1095 DUK_ASSERT(ret == 0 || ret == 1);
1096 return ret;
1097}
1098
1099DUK_EXTERNAL duk_bool_t duk_require_boolean(duk_context *ctx, duk_idx_t index) {
1100 duk_hthread *thr = (duk_hthread *) ctx;
1101 duk_tval *tv;
1102
1103 DUK_ASSERT_CTX_VALID(ctx);
1104
1105 tv = duk_get_tval(ctx, index);
1106 if (tv && DUK_TVAL_IS_BOOLEAN(tv)) {
1107 duk_bool_t ret = DUK_TVAL_GET_BOOLEAN(tv);
1108 DUK_ASSERT(ret == 0 || ret == 1);
1109 return ret;
1110 }
1111
1112 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_BOOLEAN);
1113 return 0; /* not reachable */
1114}
1115
1116DUK_EXTERNAL duk_double_t duk_get_number(duk_context *ctx, duk_idx_t index) {
1117 duk_double_union ret;
1118 duk_tval *tv;
1119
1120 DUK_ASSERT_CTX_VALID(ctx);
1121
1122 ret.d = DUK_DOUBLE_NAN; /* default: NaN */
1123 tv = duk_get_tval(ctx, index);
1124 if (tv && DUK_TVAL_IS_NUMBER(tv)) {
1125 ret.d = DUK_TVAL_GET_NUMBER(tv);
1126 }
1127
1128 /*
1129 * Number should already be in NaN-normalized form, but let's
1130 * normalize anyway.
1131 */
1132
1133 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret);
1134 return ret.d;
1135}
1136
1137DUK_EXTERNAL duk_double_t duk_require_number(duk_context *ctx, duk_idx_t index) {
1138 duk_hthread *thr = (duk_hthread *) ctx;
1139 duk_tval *tv;
1140
1141 DUK_ASSERT_CTX_VALID(ctx);
1142
1143 tv = duk_get_tval(ctx, index);
1144 if (tv && DUK_TVAL_IS_NUMBER(tv)) {
1145 duk_double_union ret;
1146 ret.d = DUK_TVAL_GET_NUMBER(tv);
1147
1148 /*
1149 * Number should already be in NaN-normalized form,
1150 * but let's normalize anyway.
1151 */
1152
1153 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&ret);
1154 return ret.d;
1155 }
1156
1157 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_NUMBER);
1158 return DUK_DOUBLE_NAN; /* not reachable */
1159}
1160
1161DUK_EXTERNAL duk_int_t duk_get_int(duk_context *ctx, duk_idx_t index) {
1162 /* Custom coercion for API */
1163 DUK_ASSERT_CTX_VALID(ctx);
1164 return (duk_int_t) duk__api_coerce_d2i(ctx, index, 0 /*require*/);
1165}
1166
1167DUK_EXTERNAL duk_uint_t duk_get_uint(duk_context *ctx, duk_idx_t index) {
1168 /* Custom coercion for API */
1169 DUK_ASSERT_CTX_VALID(ctx);
1170 return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 0 /*require*/);
1171}
1172
1173DUK_EXTERNAL duk_int_t duk_require_int(duk_context *ctx, duk_idx_t index) {
1174 /* Custom coercion for API */
1175 DUK_ASSERT_CTX_VALID(ctx);
1176 return (duk_int_t) duk__api_coerce_d2i(ctx, index, 1 /*require*/);
1177}
1178
1179DUK_EXTERNAL duk_uint_t duk_require_uint(duk_context *ctx, duk_idx_t index) {
1180 /* Custom coercion for API */
1181 DUK_ASSERT_CTX_VALID(ctx);
1182 return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 1 /*require*/);
1183}
1184
1185DUK_EXTERNAL const char *duk_get_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
1186 const char *ret;
1187 duk_tval *tv;
1188
1189 DUK_ASSERT_CTX_VALID(ctx);
1190
1191 /* default: NULL, length 0 */
1192 ret = NULL;
1193 if (out_len) {
1194 *out_len = 0;
1195 }
1196
1197 tv = duk_get_tval(ctx, index);
1198 if (tv && DUK_TVAL_IS_STRING(tv)) {
1199 /* Here we rely on duk_hstring instances always being zero
1200 * terminated even if the actual string is not.
1201 */
1202 duk_hstring *h = DUK_TVAL_GET_STRING(tv);
1203 DUK_ASSERT(h != NULL);
1204 ret = (const char *) DUK_HSTRING_GET_DATA(h);
1205 if (out_len) {
1206 *out_len = DUK_HSTRING_GET_BYTELEN(h);
1207 }
1208 }
1209
1210 return ret;
1211}
1212
1213DUK_EXTERNAL const char *duk_require_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
1214 duk_hthread *thr = (duk_hthread *) ctx;
1215 const char *ret;
1216
1217 DUK_ASSERT_CTX_VALID(ctx);
1218
1219 /* Note: this check relies on the fact that even a zero-size string
1220 * has a non-NULL pointer.
1221 */
1222 ret = duk_get_lstring(ctx, index, out_len);
1223 if (ret) {
1224 return ret;
1225 }
1226
1227 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_STRING);
1228 return NULL; /* not reachable */
1229}
1230
1231DUK_EXTERNAL const char *duk_get_string(duk_context *ctx, duk_idx_t index) {
1232 DUK_ASSERT_CTX_VALID(ctx);
1233
1234 return duk_get_lstring(ctx, index, NULL);
1235}
1236
1237DUK_EXTERNAL const char *duk_require_string(duk_context *ctx, duk_idx_t index) {
1238 DUK_ASSERT_CTX_VALID(ctx);
1239
1240 return duk_require_lstring(ctx, index, NULL);
1241}
1242
1243DUK_EXTERNAL void *duk_get_pointer(duk_context *ctx, duk_idx_t index) {
1244 duk_tval *tv;
1245
1246 DUK_ASSERT_CTX_VALID(ctx);
1247
1248 tv = duk_get_tval(ctx, index);
1249 if (tv && DUK_TVAL_IS_POINTER(tv)) {
1250 void *p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */
1251 return (void *) p;
1252 }
1253
1254 return NULL;
1255}
1256
1257DUK_EXTERNAL void *duk_require_pointer(duk_context *ctx, duk_idx_t index) {
1258 duk_hthread *thr = (duk_hthread *) ctx;
1259 duk_tval *tv;
1260
1261 DUK_ASSERT_CTX_VALID(ctx);
1262
1263 /* Note: here we must be wary of the fact that a pointer may be
1264 * valid and be a NULL.
1265 */
1266 tv = duk_get_tval(ctx, index);
1267 if (tv && DUK_TVAL_IS_POINTER(tv)) {
1268 void *p = DUK_TVAL_GET_POINTER(tv); /* may be NULL */
1269 return (void *) p;
1270 }
1271
1272 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_POINTER);
1273 return NULL; /* not reachable */
1274}
1275
1276#if 0 /*unused*/
1277DUK_INTERNAL void *duk_get_voidptr(duk_context *ctx, duk_idx_t index) {
1278 duk_tval *tv;
1279
1280 DUK_ASSERT_CTX_VALID(ctx);
1281
1282 tv = duk_get_tval(ctx, index);
1283 if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
1284 duk_heaphdr *h = DUK_TVAL_GET_HEAPHDR(tv);
1285 DUK_ASSERT(h != NULL);
1286 return (void *) h;
1287 }
1288
1289 return NULL;
1290}
1291#endif
1292
1293DUK_LOCAL void *duk__get_buffer_helper(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_bool_t throw_flag) {
1294 duk_hthread *thr = (duk_hthread *) ctx;
1295 duk_tval *tv;
1296
1297 DUK_ASSERT_CTX_VALID(ctx);
1298 DUK_UNREF(thr);
1299
1300 if (out_size != NULL) {
1301 *out_size = 0;
1302 }
1303
1304 tv = duk_get_tval(ctx, index);
1305 if (tv && DUK_TVAL_IS_BUFFER(tv)) {
1306 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
1307 DUK_ASSERT(h != NULL);
1308 if (out_size) {
1309 *out_size = DUK_HBUFFER_GET_SIZE(h);
1310 }
1311 return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */
1312 }
1313
1314 if (throw_flag) {
1315 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_BUFFER);
1316 }
1317 return NULL;
1318}
1319
1320DUK_EXTERNAL void *duk_get_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
1321 return duk__get_buffer_helper(ctx, index, out_size, 0 /*throw_flag*/);
1322}
1323
1324DUK_EXTERNAL void *duk_require_buffer(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
1325 return duk__get_buffer_helper(ctx, index, out_size, 1 /*throw_flag*/);
1326}
1327
1328DUK_LOCAL void *duk__get_buffer_data_helper(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_bool_t throw_flag) {
1329 duk_hthread *thr = (duk_hthread *) ctx;
1330 duk_tval *tv;
1331
1332 DUK_ASSERT_CTX_VALID(ctx);
1333 DUK_UNREF(thr);
1334
1335 if (out_size != NULL) {
1336 *out_size = 0;
1337 }
1338
1339 tv = duk_get_tval(ctx, index);
1340 if (tv == NULL) {
1341 goto fail;
1342 }
1343
1344 if (DUK_TVAL_IS_BUFFER(tv)) {
1345 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
1346 DUK_ASSERT(h != NULL);
1347 if (out_size) {
1348 *out_size = DUK_HBUFFER_GET_SIZE(h);
1349 }
1350 return (void *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h); /* may be NULL (but only if size is 0) */
1351 } else if (DUK_TVAL_IS_OBJECT(tv)) {
1352 duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
1353 DUK_ASSERT(h != NULL);
1354 if (DUK_HOBJECT_IS_BUFFEROBJECT(h)) {
1355 /* XXX: this is probably a useful shared helper: for a
1356 * duk_hbufferobject, get a validated buffer pointer/length.
1357 */
1358 duk_hbufferobject *h_bufobj = (duk_hbufferobject *) h;
1359 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
1360
1361 if (h_bufobj->buf != NULL &&
1362 DUK_HBUFFEROBJECT_VALID_SLICE(h_bufobj)) {
1363 duk_uint8_t *p;
1364
1365 p = (duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_bufobj->buf);
1366 if (out_size != NULL) {
1367 *out_size = (duk_size_t) h_bufobj->length;
1368 }
1369 return (void *) (p + h_bufobj->offset);
1370 }
1371 /* if slice not fully valid, treat as error */
1372 }
1373 }
1374
1375 fail:
1376 if (throw_flag) {
1377 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_BUFFER);
1378 }
1379 return NULL;
1380}
1381
1382DUK_EXTERNAL void *duk_get_buffer_data(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
1383 return duk__get_buffer_data_helper(ctx, index, out_size, 0 /*throw_flag*/);
1384}
1385
1386DUK_EXTERNAL void *duk_require_buffer_data(duk_context *ctx, duk_idx_t index, duk_size_t *out_size) {
1387 return duk__get_buffer_data_helper(ctx, index, out_size, 1 /*throw_flag*/);
1388}
1389
1390/* Raw helper for getting a value from the stack, checking its tag, and possible its object class.
1391 * The tag cannot be a number because numbers don't have an internal tag in the packed representation.
1392 */
1393DUK_INTERNAL duk_heaphdr *duk_get_tagged_heaphdr_raw(duk_context *ctx, duk_idx_t index, duk_uint_t flags_and_tag) {
1394 duk_hthread *thr = (duk_hthread *) ctx;
1395 duk_tval *tv;
1396 duk_small_uint_t tag = flags_and_tag & 0xffffU; /* tags can be up to 16 bits */
1397
1398 DUK_ASSERT_CTX_VALID(ctx);
1399
1400 tv = duk_get_tval(ctx, index);
1401 if (tv && (DUK_TVAL_GET_TAG(tv) == tag)) {
1402 duk_heaphdr *ret;
1403
1404 /* Note: tag comparison in general doesn't work for numbers,
1405 * but it does work for everything else (heap objects here).
1406 */
1407 ret = DUK_TVAL_GET_HEAPHDR(tv);
1408 DUK_ASSERT(ret != NULL); /* tagged null pointers should never occur */
1409
1410 /* If class check has been requested, tag must also be DUK_TAG_OBJECT.
1411 * This allows us to just check the class check flag without checking
1412 * the tag also.
1413 */
1414 DUK_ASSERT((flags_and_tag & DUK_GETTAGGED_FLAG_CHECK_CLASS) == 0 ||
1415 tag == DUK_TAG_OBJECT);
1416
1417 if ((flags_and_tag & DUK_GETTAGGED_FLAG_CHECK_CLASS) == 0 || /* no class check */
1418 (duk_int_t) DUK_HOBJECT_GET_CLASS_NUMBER((duk_hobject *) ret) == /* or class check matches */
1419 (duk_int_t) ((flags_and_tag >> DUK_GETTAGGED_CLASS_SHIFT) & 0xff)) {
1420 return ret;
1421 }
1422 }
1423
1424 if (flags_and_tag & DUK_GETTAGGED_FLAG_ALLOW_NULL) {
1425 return (duk_heaphdr *) NULL;
1426 }
1427
1428 /* Formatting the tag number here is not very useful: the tag value
1429 * is Duktape internal (not the same as DUK_TYPE_xxx) and even depends
1430 * on the duk_tval layout. If anything, add a human readable type here.
1431 */
1432 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
1433 return NULL; /* not reachable */
1434}
1435
1436DUK_INTERNAL duk_hstring *duk_get_hstring(duk_context *ctx, duk_idx_t index) {
1437 return (duk_hstring *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_STRING | DUK_GETTAGGED_FLAG_ALLOW_NULL);
1438}
1439
1440DUK_INTERNAL duk_hstring *duk_require_hstring(duk_context *ctx, duk_idx_t index) {
1441 return (duk_hstring *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_STRING);
1442}
1443
1444DUK_INTERNAL duk_hobject *duk_get_hobject(duk_context *ctx, duk_idx_t index) {
1445 return (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT | DUK_GETTAGGED_FLAG_ALLOW_NULL);
1446}
1447
1448DUK_INTERNAL duk_hobject *duk_require_hobject(duk_context *ctx, duk_idx_t index) {
1449 return (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
1450}
1451
1452DUK_INTERNAL duk_hbuffer *duk_get_hbuffer(duk_context *ctx, duk_idx_t index) {
1453 return (duk_hbuffer *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_BUFFER | DUK_GETTAGGED_FLAG_ALLOW_NULL);
1454}
1455
1456DUK_INTERNAL duk_hbuffer *duk_require_hbuffer(duk_context *ctx, duk_idx_t index) {
1457 return (duk_hbuffer *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_BUFFER);
1458}
1459
1460DUK_INTERNAL duk_hthread *duk_get_hthread(duk_context *ctx, duk_idx_t index) {
1461 duk_hobject *h = (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT | DUK_GETTAGGED_FLAG_ALLOW_NULL);
1462 if (h != NULL && !DUK_HOBJECT_IS_THREAD(h)) {
1463 h = NULL;
1464 }
1465 return (duk_hthread *) h;
1466}
1467
1468DUK_INTERNAL duk_hthread *duk_require_hthread(duk_context *ctx, duk_idx_t index) {
1469 duk_hthread *thr = (duk_hthread *) ctx;
1470 duk_hobject *h = (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
1471 DUK_ASSERT(h != NULL);
1472 if (!DUK_HOBJECT_IS_THREAD(h)) {
1473 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_THREAD);
1474 }
1475 return (duk_hthread *) h;
1476}
1477
1478DUK_INTERNAL duk_hcompiledfunction *duk_get_hcompiledfunction(duk_context *ctx, duk_idx_t index) {
1479 duk_hobject *h = (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT | DUK_GETTAGGED_FLAG_ALLOW_NULL);
1480 if (h != NULL && !DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
1481 h = NULL;
1482 }
1483 return (duk_hcompiledfunction *) h;
1484}
1485
1486DUK_INTERNAL duk_hcompiledfunction *duk_require_hcompiledfunction(duk_context *ctx, duk_idx_t index) {
1487 duk_hthread *thr = (duk_hthread *) ctx;
1488 duk_hobject *h = (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
1489 DUK_ASSERT(h != NULL);
1490 if (!DUK_HOBJECT_IS_COMPILEDFUNCTION(h)) {
1491 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_COMPILEDFUNCTION);
1492 }
1493 return (duk_hcompiledfunction *) h;
1494}
1495
1496DUK_INTERNAL duk_hnativefunction *duk_get_hnativefunction(duk_context *ctx, duk_idx_t index) {
1497 duk_hobject *h = (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT | DUK_GETTAGGED_FLAG_ALLOW_NULL);
1498 if (h != NULL && !DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
1499 h = NULL;
1500 }
1501 return (duk_hnativefunction *) h;
1502}
1503
1504DUK_INTERNAL duk_hnativefunction *duk_require_hnativefunction(duk_context *ctx, duk_idx_t index) {
1505 duk_hthread *thr = (duk_hthread *) ctx;
1506 duk_hobject *h = (duk_hobject *) duk_get_tagged_heaphdr_raw(ctx, index, DUK_TAG_OBJECT);
1507 DUK_ASSERT(h != NULL);
1508 if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
1509 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_NATIVEFUNCTION);
1510 }
1511 return (duk_hnativefunction *) h;
1512}
1513
1514DUK_EXTERNAL duk_c_function duk_get_c_function(duk_context *ctx, duk_idx_t index) {
1515 duk_tval *tv;
1516 duk_hobject *h;
1517 duk_hnativefunction *f;
1518
1519 DUK_ASSERT_CTX_VALID(ctx);
1520
1521 tv = duk_get_tval(ctx, index);
1522 if (!tv) {
1523 return NULL;
1524 }
1525 if (!DUK_TVAL_IS_OBJECT(tv)) {
1526 return NULL;
1527 }
1528 h = DUK_TVAL_GET_OBJECT(tv);
1529 DUK_ASSERT(h != NULL);
1530
1531 if (!DUK_HOBJECT_IS_NATIVEFUNCTION(h)) {
1532 return NULL;
1533 }
1534 DUK_ASSERT(DUK_HOBJECT_HAS_NATIVEFUNCTION(h));
1535 f = (duk_hnativefunction *) h;
1536
1537 return f->func;
1538}
1539
1540DUK_EXTERNAL duk_c_function duk_require_c_function(duk_context *ctx, duk_idx_t index) {
1541 duk_hthread *thr = (duk_hthread *) ctx;
1542 duk_c_function ret;
1543
1544 DUK_ASSERT_CTX_VALID(ctx);
1545
1546 ret = duk_get_c_function(ctx, index);
1547 if (!ret) {
1548 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_C_FUNCTION);
1549 }
1550 return ret;
1551}
1552
1553DUK_EXTERNAL duk_context *duk_get_context(duk_context *ctx, duk_idx_t index) {
1554 DUK_ASSERT_CTX_VALID(ctx);
1555
1556 return (duk_context *) duk_get_hthread(ctx, index);
1557}
1558
1559DUK_EXTERNAL duk_context *duk_require_context(duk_context *ctx, duk_idx_t index) {
1560 DUK_ASSERT_CTX_VALID(ctx);
1561
1562 return (duk_context *) duk_require_hthread(ctx, index);
1563}
1564
1565DUK_EXTERNAL void *duk_get_heapptr(duk_context *ctx, duk_idx_t index) {
1566 duk_tval *tv;
1567 void *ret;
1568
1569 DUK_ASSERT_CTX_VALID(ctx);
1570
1571 tv = duk_get_tval(ctx, index);
1572 if (tv && DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
1573 ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
1574 DUK_ASSERT(ret != NULL);
1575 return ret;
1576 }
1577
1578 return (void *) NULL;
1579}
1580
1581DUK_EXTERNAL void *duk_require_heapptr(duk_context *ctx, duk_idx_t index) {
1582 duk_hthread *thr = (duk_hthread *) ctx;
1583 duk_tval *tv;
1584 void *ret;
1585
1586 DUK_ASSERT_CTX_VALID(ctx);
1587
1588 tv = duk_require_tval(ctx, index);
1589 DUK_ASSERT(tv != NULL);
1590 if (DUK_TVAL_IS_HEAP_ALLOCATED(tv)) {
1591 ret = (void *) DUK_TVAL_GET_HEAPHDR(tv);
1592 DUK_ASSERT(ret != NULL);
1593 return ret;
1594 }
1595
1596 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
1597 return (void *) NULL; /* not reachable */
1598}
1599
1600#if 0
1601/* This would be pointless: we'd return NULL for both lightfuncs and
1602 * unexpected types.
1603 */
1604duk_hobject *duk_get_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) {
1605}
1606#endif
1607
1608/* Useful for internal call sites where we either expect an object (function)
1609 * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
1610 * to an object). Return value is NULL if value is neither an object nor a
1611 * lightfunc.
1612 */
1613duk_hobject *duk_get_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) {
1614 duk_tval *tv;
1615
1616 DUK_ASSERT_CTX_VALID(ctx);
1617
1618 tv = duk_require_tval(ctx, index);
1619 DUK_ASSERT(tv != NULL);
1620 if (DUK_TVAL_IS_OBJECT(tv)) {
1621 return DUK_TVAL_GET_OBJECT(tv);
1622 } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
1623 duk_to_object(ctx, index);
1624 return duk_require_hobject(ctx, index);
1625 }
1626
1627 return NULL;
1628}
1629
1630/* Useful for internal call sites where we either expect an object (function)
1631 * or a lightfunc. Returns NULL for a lightfunc.
1632 */
1633DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc(duk_context *ctx, duk_idx_t index) {
1634 duk_hthread *thr = (duk_hthread *) ctx;
1635 duk_tval *tv;
1636
1637 DUK_ASSERT_CTX_VALID(ctx);
1638
1639 tv = duk_require_tval(ctx, index);
1640 DUK_ASSERT(tv != NULL);
1641 if (DUK_TVAL_IS_OBJECT(tv)) {
1642 return DUK_TVAL_GET_OBJECT(tv);
1643 } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
1644 return NULL;
1645 }
1646
1647 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
1648 return NULL; /* not reachable */
1649}
1650
1651/* Useful for internal call sites where we either expect an object (function)
1652 * or a lightfunc. Accepts an object (returned as is) or a lightfunc (coerced
1653 * to an object). Return value is never NULL.
1654 */
1655DUK_INTERNAL duk_hobject *duk_require_hobject_or_lfunc_coerce(duk_context *ctx, duk_idx_t index) {
1656 duk_hthread *thr = (duk_hthread *) ctx;
1657 duk_tval *tv;
1658
1659 DUK_ASSERT_CTX_VALID(ctx);
1660
1661 tv = duk_require_tval(ctx, index);
1662 if (DUK_TVAL_IS_OBJECT(tv)) {
1663 return DUK_TVAL_GET_OBJECT(tv);
1664 } else if (DUK_TVAL_IS_LIGHTFUNC(tv)) {
1665 duk_to_object(ctx, index);
1666 return duk_require_hobject(ctx, index);
1667 }
1668
1669 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
1670 return NULL; /* not reachable */
1671}
1672
1673DUK_EXTERNAL duk_size_t duk_get_length(duk_context *ctx, duk_idx_t index) {
1674 duk_tval *tv;
1675
1676 DUK_ASSERT_CTX_VALID(ctx);
1677
1678 tv = duk_get_tval(ctx, index);
1679 if (!tv) {
1680 return 0;
1681 }
1682
1683 switch (DUK_TVAL_GET_TAG(tv)) {
1684 case DUK_TAG_UNDEFINED:
1685 case DUK_TAG_NULL:
1686 case DUK_TAG_BOOLEAN:
1687 case DUK_TAG_POINTER:
1688 return 0;
1689 case DUK_TAG_STRING: {
1690 duk_hstring *h = DUK_TVAL_GET_STRING(tv);
1691 DUK_ASSERT(h != NULL);
1692 return (duk_size_t) DUK_HSTRING_GET_CHARLEN(h);
1693 }
1694 case DUK_TAG_OBJECT: {
1695 duk_hobject *h = DUK_TVAL_GET_OBJECT(tv);
1696 DUK_ASSERT(h != NULL);
1697 return (duk_size_t) duk_hobject_get_length((duk_hthread *) ctx, h);
1698 }
1699 case DUK_TAG_BUFFER: {
1700 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
1701 DUK_ASSERT(h != NULL);
1702 return (duk_size_t) DUK_HBUFFER_GET_SIZE(h);
1703 }
1704 case DUK_TAG_LIGHTFUNC: {
1705 duk_small_uint_t lf_flags;
1706 lf_flags = DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv);
1707 return (duk_size_t) DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
1708 }
1709#if defined(DUK_USE_FASTINT)
1710 case DUK_TAG_FASTINT:
1711#endif
1712 default:
1713 /* number */
1714 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
1715 return 0;
1716 }
1717
1718 DUK_UNREACHABLE();
1719}
1720
1721DUK_INTERNAL void duk_set_length(duk_context *ctx, duk_idx_t index, duk_size_t length) {
1722 duk_hthread *thr = (duk_hthread *) ctx;
1723 duk_hobject *h;
1724
1725 DUK_ASSERT_CTX_VALID(ctx);
1726
1727 h = duk_get_hobject(ctx, index);
1728 if (!h) {
1729 return;
1730 }
1731
1732 duk_hobject_set_length(thr, h, (duk_uint32_t) length); /* XXX: typing */
1733}
1734
1735/*
1736 * Conversions and coercions
1737 *
1738 * The conversion/coercions are in-place operations on the value stack.
1739 * Some operations are implemented here directly, while others call a
1740 * helper in duk_js_ops.c after validating arguments.
1741 */
1742
1743/* E5 Section 8.12.8 */
1744
1745DUK_LOCAL duk_bool_t duk__defaultvalue_coerce_attempt(duk_context *ctx, duk_idx_t index, duk_small_int_t func_stridx) {
1746 if (duk_get_prop_stridx(ctx, index, func_stridx)) {
1747 /* [ ... func ] */
1748 if (duk_is_callable(ctx, -1)) {
1749 duk_dup(ctx, index); /* -> [ ... func this ] */
1750 duk_call_method(ctx, 0); /* -> [ ... retval ] */
1751 if (duk_is_primitive(ctx, -1)) {
1752 duk_replace(ctx, index);
1753 return 1;
1754 }
1755 /* [ ... retval ]; popped below */
1756 }
1757 }
1758 duk_pop(ctx); /* [ ... func/retval ] -> [ ... ] */
1759 return 0;
1760}
1761
1762DUK_EXTERNAL void duk_to_defaultvalue(duk_context *ctx, duk_idx_t index, duk_int_t hint) {
1763 duk_hthread *thr = (duk_hthread *) ctx;
1764 duk_hobject *obj;
1765 /* inline initializer for coercers[] is not allowed by old compilers like BCC */
1766 duk_small_int_t coercers[2];
1767
1768 DUK_ASSERT_CTX_VALID(ctx);
1769 DUK_ASSERT(thr != NULL);
1770
1771 coercers[0] = DUK_STRIDX_VALUE_OF;
1772 coercers[1] = DUK_STRIDX_TO_STRING;
1773
1774 index = duk_require_normalize_index(ctx, index);
1775 obj = duk_require_hobject_or_lfunc(ctx, index);
1776
1777 if (hint == DUK_HINT_NONE) {
1778 if (obj != NULL && DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_DATE) {
1779 hint = DUK_HINT_STRING;
1780 } else {
1781 hint = DUK_HINT_NUMBER;
1782 }
1783 }
1784
1785 if (hint == DUK_HINT_STRING) {
1786 coercers[0] = DUK_STRIDX_TO_STRING;
1787 coercers[1] = DUK_STRIDX_VALUE_OF;
1788 }
1789
1790 if (duk__defaultvalue_coerce_attempt(ctx, index, coercers[0])) {
1791 return;
1792 }
1793
1794 if (duk__defaultvalue_coerce_attempt(ctx, index, coercers[1])) {
1795 return;
1796 }
1797
1798 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_DEFAULTVALUE_COERCE_FAILED);
1799}
1800
1801DUK_EXTERNAL void duk_to_undefined(duk_context *ctx, duk_idx_t index) {
1802 duk_hthread *thr = (duk_hthread *) ctx;
1803 duk_tval *tv;
1804 duk_tval tv_tmp;
1805
1806 DUK_ASSERT_CTX_VALID(ctx);
1807 DUK_UNREF(thr);
1808
1809 tv = duk_require_tval(ctx, index);
1810 DUK_ASSERT(tv != NULL);
1811 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1812 DUK_TVAL_SET_UNDEFINED_ACTUAL(tv); /* no need to incref */
1813 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1814}
1815
1816DUK_EXTERNAL void duk_to_null(duk_context *ctx, duk_idx_t index) {
1817 duk_hthread *thr = (duk_hthread *) ctx;
1818 duk_tval *tv;
1819 duk_tval tv_tmp;
1820
1821 DUK_ASSERT_CTX_VALID(ctx);
1822 DUK_UNREF(thr);
1823
1824 tv = duk_require_tval(ctx, index);
1825 DUK_ASSERT(tv != NULL);
1826 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1827 DUK_TVAL_SET_NULL(tv); /* no need to incref */
1828 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1829}
1830
1831/* E5 Section 9.1 */
1832DUK_EXTERNAL void duk_to_primitive(duk_context *ctx, duk_idx_t index, duk_int_t hint) {
1833 DUK_ASSERT_CTX_VALID(ctx);
1834 DUK_ASSERT(hint == DUK_HINT_NONE || hint == DUK_HINT_NUMBER || hint == DUK_HINT_STRING);
1835
1836 index = duk_require_normalize_index(ctx, index);
1837
1838 if (!duk_check_type_mask(ctx, index, DUK_TYPE_MASK_OBJECT |
1839 DUK_TYPE_MASK_LIGHTFUNC)) {
1840 /* everything except object stay as is */
1841 return;
1842 }
1843 duk_to_defaultvalue(ctx, index, hint);
1844}
1845
1846/* E5 Section 9.2 */
1847DUK_EXTERNAL duk_bool_t duk_to_boolean(duk_context *ctx, duk_idx_t index) {
1848 duk_hthread *thr = (duk_hthread *) ctx;
1849 duk_tval *tv;
1850 duk_tval tv_tmp;
1851 duk_bool_t val;
1852
1853 DUK_ASSERT_CTX_VALID(ctx);
1854 DUK_UNREF(thr);
1855
1856 index = duk_require_normalize_index(ctx, index);
1857
1858 tv = duk_require_tval(ctx, index);
1859 DUK_ASSERT(tv != NULL);
1860
1861 val = duk_js_toboolean(tv);
1862 DUK_ASSERT(val == 0 || val == 1);
1863
1864 /* Note: no need to re-lookup tv, conversion is side effect free */
1865 DUK_ASSERT(tv != NULL);
1866 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1867 DUK_TVAL_SET_BOOLEAN(tv, val); /* no need to incref */
1868 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1869 return val;
1870}
1871
1872DUK_EXTERNAL duk_double_t duk_to_number(duk_context *ctx, duk_idx_t index) {
1873 duk_hthread *thr = (duk_hthread *) ctx;
1874 duk_tval *tv;
1875 duk_tval tv_tmp;
1876 duk_double_t d;
1877
1878 DUK_ASSERT_CTX_VALID(ctx);
1879
1880 tv = duk_require_tval(ctx, index);
1881 DUK_ASSERT(tv != NULL);
1882 /* XXX: fastint? */
1883 d = duk_js_tonumber(thr, tv);
1884
1885 /* Note: need to re-lookup because ToNumber() may have side effects */
1886 tv = duk_require_tval(ctx, index);
1887 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1888 DUK_TVAL_SET_NUMBER(tv, d); /* no need to incref */
1889 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1890 return d;
1891}
1892
1893/* XXX: combine all the integer conversions: they share everything
1894 * but the helper function for coercion.
1895 */
1896
1897typedef duk_double_t (*duk__toint_coercer)(duk_hthread *thr, duk_tval *tv);
1898
1899DUK_LOCAL duk_double_t duk__to_int_uint_helper(duk_context *ctx, duk_idx_t index, duk__toint_coercer coerce_func) {
1900 duk_hthread *thr = (duk_hthread *) ctx;
1901 duk_tval *tv;
1902 duk_tval tv_tmp;
1903 duk_double_t d;
1904
1905 DUK_ASSERT_CTX_VALID(ctx);
1906
1907 tv = duk_require_tval(ctx, index);
1908 DUK_ASSERT(tv != NULL);
1909 d = coerce_func(thr, tv);
1910
1911 /* XXX: fastint? */
1912
1913 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1914 tv = duk_require_tval(ctx, index);
1915 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1916 DUK_TVAL_SET_NUMBER(tv, d); /* no need to incref */
1917 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1918 return d;
1919}
1920
1921DUK_EXTERNAL duk_int_t duk_to_int(duk_context *ctx, duk_idx_t index) {
1922 /* Value coercion (in stack): ToInteger(), E5 Section 9.4
1923 * API return value coercion: custom
1924 */
1925 DUK_ASSERT_CTX_VALID(ctx);
1926 (void) duk__to_int_uint_helper(ctx, index, duk_js_tointeger);
1927 return (duk_int_t) duk__api_coerce_d2i(ctx, index, 0 /*require*/);
1928}
1929
1930DUK_EXTERNAL duk_uint_t duk_to_uint(duk_context *ctx, duk_idx_t index) {
1931 /* Value coercion (in stack): ToInteger(), E5 Section 9.4
1932 * API return value coercion: custom
1933 */
1934 DUK_ASSERT_CTX_VALID(ctx);
1935 (void) duk__to_int_uint_helper(ctx, index, duk_js_tointeger);
1936 return (duk_uint_t) duk__api_coerce_d2ui(ctx, index, 0 /*require*/);
1937}
1938
1939DUK_EXTERNAL duk_int32_t duk_to_int32(duk_context *ctx, duk_idx_t index) {
1940 duk_hthread *thr = (duk_hthread *) ctx;
1941 duk_tval *tv;
1942 duk_tval tv_tmp;
1943 duk_int32_t ret;
1944
1945 DUK_ASSERT_CTX_VALID(ctx);
1946
1947 tv = duk_require_tval(ctx, index);
1948 DUK_ASSERT(tv != NULL);
1949 ret = duk_js_toint32(thr, tv);
1950
1951 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1952 tv = duk_require_tval(ctx, index);
1953#if defined(DUK_USE_FASTINT)
1954 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1955 DUK_TVAL_SET_FASTINT_I32(tv, ret); /* no need to incref */
1956 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1957 return ret;
1958#else
1959 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1960 DUK_TVAL_SET_NUMBER(tv, (duk_double_t) ret); /* no need to incref */
1961 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1962 return ret;
1963#endif
1964}
1965
1966DUK_EXTERNAL duk_uint32_t duk_to_uint32(duk_context *ctx, duk_idx_t index) {
1967 duk_hthread *thr = (duk_hthread *) ctx;
1968 duk_tval *tv;
1969 duk_tval tv_tmp;
1970 duk_uint32_t ret;
1971
1972 DUK_ASSERT_CTX_VALID(ctx);
1973
1974 tv = duk_require_tval(ctx, index);
1975 DUK_ASSERT(tv != NULL);
1976 ret = duk_js_touint32(thr, tv);
1977
1978 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
1979 tv = duk_require_tval(ctx, index);
1980#if defined(DUK_USE_FASTINT)
1981 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1982 DUK_TVAL_SET_FASTINT_U32(tv, ret); /* no need to incref */
1983 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1984 return ret;
1985#else
1986 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
1987 DUK_TVAL_SET_NUMBER(tv, (duk_double_t) ret); /* no need to incref */
1988 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
1989#endif
1990 return ret;
1991}
1992
1993DUK_EXTERNAL duk_uint16_t duk_to_uint16(duk_context *ctx, duk_idx_t index) {
1994 duk_hthread *thr = (duk_hthread *) ctx;
1995 duk_tval *tv;
1996 duk_tval tv_tmp;
1997 duk_uint16_t ret;
1998
1999 DUK_ASSERT_CTX_VALID(ctx);
2000
2001 tv = duk_require_tval(ctx, index);
2002 DUK_ASSERT(tv != NULL);
2003 ret = duk_js_touint16(thr, tv);
2004
2005 /* Relookup in case coerce_func() has side effects, e.g. ends up coercing an object */
2006 tv = duk_require_tval(ctx, index);
2007#if defined(DUK_USE_FASTINT)
2008 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
2009 DUK_TVAL_SET_FASTINT_U32(tv, ret); /* no need to incref */
2010 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
2011 return ret;
2012#else
2013 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
2014 DUK_TVAL_SET_NUMBER(tv, (duk_double_t) ret); /* no need to incref */
2015 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
2016#endif
2017 return ret;
2018}
2019
2020#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
2021/* Special coercion for Uint8ClampedArray. */
2022DUK_INTERNAL duk_uint8_t duk_to_uint8clamped(duk_context *ctx, duk_idx_t index) {
2023 duk_double_t d;
2024 duk_double_t t;
2025 duk_uint8_t ret;
2026
2027 /* XXX: Simplify this algorithm, should be possible to come up with
2028 * a shorter and faster algorithm by inspecting IEEE representation
2029 * directly.
2030 */
2031
2032 d = duk_to_number(ctx, index);
2033 if (d <= 0.0) {
2034 return 0;
2035 } else if (d >= 255) {
2036 return 255;
2037 } else if (DUK_ISNAN(d)) {
2038 /* Avoid NaN-to-integer coercion as it is compiler specific. */
2039 return 0;
2040 }
2041
2042 t = d - DUK_FLOOR(d);
2043 if (t == 0.5) {
2044 /* Exact halfway, round to even. */
2045 ret = (duk_uint8_t) d;
2046 ret = (ret + 1) & 0xfe; /* Example: d=3.5, t=0.5 -> ret = (3 + 1) & 0xfe = 4 & 0xfe = 4
2047 * Example: d=4.5, t=0.5 -> ret = (4 + 1) & 0xfe = 5 & 0xfe = 4
2048 */
2049 } else {
2050 /* Not halfway, round to nearest. */
2051 ret = (duk_uint8_t) (d + 0.5);
2052 }
2053 return ret;
2054}
2055#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
2056
2057DUK_EXTERNAL const char *duk_to_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
2058 DUK_ASSERT_CTX_VALID(ctx);
2059
2060 (void) duk_to_string(ctx, index);
2061 return duk_require_lstring(ctx, index, out_len);
2062}
2063
2064DUK_LOCAL duk_ret_t duk__safe_to_string_raw(duk_context *ctx) {
2065 DUK_ASSERT_CTX_VALID(ctx);
2066
2067 duk_to_string(ctx, -1);
2068 return 1;
2069}
2070
2071DUK_EXTERNAL const char *duk_safe_to_lstring(duk_context *ctx, duk_idx_t index, duk_size_t *out_len) {
2072 DUK_ASSERT_CTX_VALID(ctx);
2073
2074 index = duk_require_normalize_index(ctx, index);
2075
2076 /* We intentionally ignore the duk_safe_call() return value and only
2077 * check the output type. This way we don't also need to check that
2078 * the returned value is indeed a string in the success case.
2079 */
2080
2081 duk_dup(ctx, index);
2082 (void) duk_safe_call(ctx, duk__safe_to_string_raw, 1 /*nargs*/, 1 /*nrets*/);
2083 if (!duk_is_string(ctx, -1)) {
2084 /* Error: try coercing error to string once. */
2085 (void) duk_safe_call(ctx, duk__safe_to_string_raw, 1 /*nargs*/, 1 /*nrets*/);
2086 if (!duk_is_string(ctx, -1)) {
2087 /* Double error */
2088 duk_pop(ctx);
2089 duk_push_hstring_stridx(ctx, DUK_STRIDX_UC_ERROR);
2090 } else {
2091 ;
2092 }
2093 } else {
2094 ;
2095 }
2096 DUK_ASSERT(duk_is_string(ctx, -1));
2097
2098 duk_replace(ctx, index);
2099 return duk_require_lstring(ctx, index, out_len);
2100}
2101
2102/* XXX: other variants like uint, u32 etc */
2103DUK_INTERNAL duk_int_t duk_to_int_clamped_raw(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval, duk_bool_t *out_clamped) {
2104 duk_hthread *thr = (duk_hthread *) ctx;
2105 duk_tval *tv;
2106 duk_tval tv_tmp;
2107 duk_double_t d, dmin, dmax;
2108 duk_int_t res;
2109 duk_bool_t clamped = 0;
2110
2111 DUK_ASSERT_CTX_VALID(ctx);
2112
2113 tv = duk_require_tval(ctx, index);
2114 DUK_ASSERT(tv != NULL);
2115 d = duk_js_tointeger(thr, tv); /* E5 Section 9.4, ToInteger() */
2116
2117 dmin = (duk_double_t) minval;
2118 dmax = (duk_double_t) maxval;
2119
2120 if (d < dmin) {
2121 clamped = 1;
2122 res = minval;
2123 d = dmin;
2124 } else if (d > dmax) {
2125 clamped = 1;
2126 res = maxval;
2127 d = dmax;
2128 } else {
2129 res = (duk_int_t) d;
2130 }
2131 /* 'd' and 'res' agree here */
2132
2133 /* Relookup in case duk_js_tointeger() ends up e.g. coercing an object. */
2134 tv = duk_require_tval(ctx, index);
2135 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
2136#if defined(DUK_USE_FASTINT)
2137#if (DUK_INT_MAX <= 0x7fffffffL)
2138 DUK_TVAL_SET_FASTINT_I32(tv, res);
2139#else
2140 /* Clamping needed if duk_int_t is 64 bits. */
2141 if (res >= DUK_FASTINT_MIN && res <= DUK_FASTINT_MAX) {
2142 DUK_TVAL_SET_FASTINT(tv, res);
2143 } else {
2144 DUK_TVAL_SET_NUMBER(tv, d);
2145 }
2146#endif
2147#else
2148 DUK_TVAL_SET_NUMBER(tv, d); /* no need to incref */
2149#endif
2150 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
2151
2152 if (out_clamped) {
2153 *out_clamped = clamped;
2154 } else {
2155 /* coerced value is updated to value stack even when RangeError thrown */
2156 if (clamped) {
2157 DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_NUMBER_OUTSIDE_RANGE);
2158 }
2159 }
2160
2161 return res;
2162}
2163
2164DUK_INTERNAL duk_int_t duk_to_int_clamped(duk_context *ctx, duk_idx_t index, duk_idx_t minval, duk_idx_t maxval) {
2165 duk_bool_t dummy;
2166 return duk_to_int_clamped_raw(ctx, index, minval, maxval, &dummy);
2167}
2168
2169DUK_INTERNAL duk_int_t duk_to_int_check_range(duk_context *ctx, duk_idx_t index, duk_int_t minval, duk_int_t maxval) {
2170 return duk_to_int_clamped_raw(ctx, index, minval, maxval, NULL); /* out_clamped==NULL -> RangeError if outside range */
2171}
2172
2173DUK_EXTERNAL const char *duk_to_string(duk_context *ctx, duk_idx_t index) {
2174 duk_hthread *thr = (duk_hthread *) ctx;
2175 duk_tval *tv;
2176
2177 DUK_ASSERT_CTX_VALID(ctx);
2178 DUK_UNREF(thr);
2179
2180 index = duk_require_normalize_index(ctx, index);
2181
2182 tv = duk_require_tval(ctx, index);
2183 DUK_ASSERT(tv != NULL);
2184
2185 switch (DUK_TVAL_GET_TAG(tv)) {
2186 case DUK_TAG_UNDEFINED: {
2187 duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_UNDEFINED);
2188 break;
2189 }
2190 case DUK_TAG_NULL: {
2191 duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL);
2192 break;
2193 }
2194 case DUK_TAG_BOOLEAN: {
2195 if (DUK_TVAL_GET_BOOLEAN(tv)) {
2196 duk_push_hstring_stridx(ctx, DUK_STRIDX_TRUE);
2197 } else {
2198 duk_push_hstring_stridx(ctx, DUK_STRIDX_FALSE);
2199 }
2200 break;
2201 }
2202 case DUK_TAG_STRING: {
2203 /* nop */
2204 goto skip_replace;
2205 }
2206 case DUK_TAG_OBJECT: {
2207 duk_to_primitive(ctx, index, DUK_HINT_STRING);
2208 return duk_to_string(ctx, index); /* Note: recursive call */
2209 }
2210 case DUK_TAG_BUFFER: {
2211 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
2212
2213 /* Note: this allows creation of internal strings. */
2214
2215 DUK_ASSERT(h != NULL);
2216 duk_push_lstring(ctx,
2217 (const char *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h),
2218 (duk_size_t) DUK_HBUFFER_GET_SIZE(h));
2219 break;
2220 }
2221 case DUK_TAG_POINTER: {
2222 void *ptr = DUK_TVAL_GET_POINTER(tv);
2223 if (ptr != NULL) {
2224 duk_push_sprintf(ctx, DUK_STR_FMT_PTR, (void *) ptr);
2225 } else {
2226 /* Represent a null pointer as 'null' to be consistent with
2227 * the JX format variant. Native '%p' format for a NULL
2228 * pointer may be e.g. '(nil)'.
2229 */
2230 duk_push_hstring_stridx(ctx, DUK_STRIDX_LC_NULL);
2231 }
2232 break;
2233 }
2234 case DUK_TAG_LIGHTFUNC: {
2235 /* Should match Function.prototype.toString() */
2236 duk_push_lightfunc_tostring(ctx, tv);
2237 break;
2238 }
2239#if defined(DUK_USE_FASTINT)
2240 case DUK_TAG_FASTINT:
2241#endif
2242 default: {
2243 /* number */
2244 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
2245 duk_push_tval(ctx, tv);
2246 duk_numconv_stringify(ctx,
2247 10 /*radix*/,
2248 0 /*precision:shortest*/,
2249 0 /*force_exponential*/);
2250 break;
2251 }
2252 }
2253
2254 duk_replace(ctx, index);
2255
2256 skip_replace:
2257 return duk_require_string(ctx, index);
2258}
2259
2260DUK_INTERNAL duk_hstring *duk_to_hstring(duk_context *ctx, duk_idx_t index) {
2261 duk_hstring *ret;
2262 DUK_ASSERT_CTX_VALID(ctx);
2263 duk_to_string(ctx, index);
2264 ret = duk_get_hstring(ctx, index);
2265 DUK_ASSERT(ret != NULL);
2266 return ret;
2267}
2268
2269DUK_EXTERNAL void *duk_to_buffer_raw(duk_context *ctx, duk_idx_t index, duk_size_t *out_size, duk_uint_t mode) {
2270 duk_hthread *thr = (duk_hthread *) ctx;
2271 duk_hbuffer *h_buf;
2272 const duk_uint8_t *src_data;
2273 duk_size_t src_size;
2274 duk_uint8_t *dst_data;
2275
2276 DUK_ASSERT_CTX_VALID(ctx);
2277 DUK_UNREF(thr);
2278
2279 index = duk_require_normalize_index(ctx, index);
2280
2281 h_buf = duk_get_hbuffer(ctx, index);
2282 if (h_buf != NULL) {
2283 /* Buffer is kept as is, with the fixed/dynamic nature of the
2284 * buffer only changed if requested. An external buffer
2285 * is converted into a non-external dynamic buffer in a
2286 * duk_to_dynamic_buffer() call.
2287 */
2288 duk_uint_t tmp;
2289
2290 src_data = (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(thr->heap, h_buf);
2291 src_size = DUK_HBUFFER_GET_SIZE(h_buf);
2292
2293 tmp = (DUK_HBUFFER_HAS_DYNAMIC(h_buf) ? DUK_BUF_MODE_DYNAMIC : DUK_BUF_MODE_FIXED);
2294 if ((tmp == mode && !DUK_HBUFFER_HAS_EXTERNAL(h_buf)) ||
2295 mode == DUK_BUF_MODE_DONTCARE) {
2296 /* Note: src_data may be NULL if input is a zero-size
2297 * dynamic buffer.
2298 */
2299 dst_data = (duk_uint8_t *) src_data;
2300 goto skip_copy;
2301 }
2302 } else {
2303 /* Non-buffer value is first ToString() coerced, then converted
2304 * to a buffer (fixed buffer is used unless a dynamic buffer is
2305 * explicitly requested).
2306 */
2307
2308 src_data = (const duk_uint8_t *) duk_to_lstring(ctx, index, &src_size);
2309 }
2310
2311 dst_data = (duk_uint8_t *) duk_push_buffer(ctx, src_size, (mode == DUK_BUF_MODE_DYNAMIC) /*dynamic*/);
2312 if (DUK_LIKELY(src_size > 0)) {
2313 /* When src_size == 0, src_data may be NULL (if source
2314 * buffer is dynamic), and dst_data may be NULL (if
2315 * target buffer is dynamic). Avoid zero-size memcpy()
2316 * with an invalid pointer.
2317 */
2318 DUK_MEMCPY(dst_data, src_data, src_size);
2319 }
2320 duk_replace(ctx, index);
2321 skip_copy:
2322
2323 if (out_size) {
2324 *out_size = src_size;
2325 }
2326 return dst_data;
2327}
2328
2329DUK_EXTERNAL void *duk_to_pointer(duk_context *ctx, duk_idx_t index) {
2330 duk_tval *tv;
2331 void *res;
2332
2333 DUK_ASSERT_CTX_VALID(ctx);
2334
2335 index = duk_require_normalize_index(ctx, index);
2336
2337 tv = duk_require_tval(ctx, index);
2338 DUK_ASSERT(tv != NULL);
2339
2340 switch (DUK_TVAL_GET_TAG(tv)) {
2341 case DUK_TAG_UNDEFINED:
2342 case DUK_TAG_NULL:
2343 case DUK_TAG_BOOLEAN:
2344 res = NULL;
2345 break;
2346 case DUK_TAG_POINTER:
2347 res = DUK_TVAL_GET_POINTER(tv);
2348 break;
2349 case DUK_TAG_STRING:
2350 case DUK_TAG_OBJECT:
2351 case DUK_TAG_BUFFER:
2352 /* Heap allocated: return heap pointer which is NOT useful
2353 * for the caller, except for debugging.
2354 */
2355 res = (void *) DUK_TVAL_GET_HEAPHDR(tv);
2356 break;
2357 case DUK_TAG_LIGHTFUNC:
2358 /* Function pointers do not always cast correctly to void *
2359 * (depends on memory and segmentation model for instance),
2360 * so they coerce to NULL.
2361 */
2362 res = NULL;
2363 break;
2364#if defined(DUK_USE_FASTINT)
2365 case DUK_TAG_FASTINT:
2366#endif
2367 default:
2368 /* number */
2369 res = NULL;
2370 break;
2371 }
2372
2373 duk_push_pointer(ctx, res);
2374 duk_replace(ctx, index);
2375 return res;
2376}
2377
2378DUK_EXTERNAL void duk_to_object(duk_context *ctx, duk_idx_t index) {
2379 duk_hthread *thr = (duk_hthread *) ctx;
2380 duk_tval *tv;
2381 duk_uint_t flags = 0; /* shared flags for a subset of types */
2382 duk_small_int_t proto = 0;
2383
2384 DUK_ASSERT_CTX_VALID(ctx);
2385
2386 index = duk_require_normalize_index(ctx, index);
2387
2388 tv = duk_require_tval(ctx, index);
2389 DUK_ASSERT(tv != NULL);
2390
2391 switch (DUK_TVAL_GET_TAG(tv)) {
2392 case DUK_TAG_UNDEFINED:
2393 case DUK_TAG_NULL: {
2394 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_OBJECT_COERCIBLE);
2395 break;
2396 }
2397 case DUK_TAG_BOOLEAN: {
2398 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
2399 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BOOLEAN);
2400 proto = DUK_BIDX_BOOLEAN_PROTOTYPE;
2401 goto create_object;
2402 }
2403 case DUK_TAG_STRING: {
2404 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
2405 DUK_HOBJECT_FLAG_EXOTIC_STRINGOBJ |
2406 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_STRING);
2407 proto = DUK_BIDX_STRING_PROTOTYPE;
2408 goto create_object;
2409 }
2410 case DUK_TAG_OBJECT: {
2411 /* nop */
2412 break;
2413 }
2414 case DUK_TAG_BUFFER: {
2415 /* A plain buffer coerces to a Duktape.Buffer because it's the
2416 * object counterpart of the plain buffer value. But it might
2417 * still make more sense to produce an ArrayBuffer here?
2418 */
2419
2420 duk_hbufferobject *h_bufobj;
2421 duk_hbuffer *h_val;
2422
2423 h_val = DUK_TVAL_GET_BUFFER(tv);
2424 DUK_ASSERT(h_val != NULL);
2425
2426 h_bufobj = duk_push_bufferobject_raw(ctx,
2427 DUK_HOBJECT_FLAG_EXTENSIBLE |
2428 DUK_HOBJECT_FLAG_BUFFEROBJECT |
2429 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_BUFFER),
2430 DUK_BIDX_BUFFER_PROTOTYPE);
2431 DUK_ASSERT(h_bufobj != NULL);
2432 DUK_ASSERT(DUK_HOBJECT_HAS_EXTENSIBLE((duk_hobject *) h_bufobj));
2433 DUK_ASSERT(DUK_HOBJECT_IS_BUFFEROBJECT((duk_hobject *) h_bufobj));
2434
2435 h_bufobj->buf = h_val;
2436 DUK_HBUFFER_INCREF(thr, h_val);
2437 DUK_ASSERT(h_bufobj->offset == 0);
2438 h_bufobj->length = (duk_uint_t) DUK_HBUFFER_GET_SIZE(h_val);
2439 DUK_ASSERT(h_bufobj->shift == 0);
2440 DUK_ASSERT(h_bufobj->elem_type == DUK_HBUFFEROBJECT_ELEM_UINT8);
2441
2442 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
2443 goto replace_value;
2444 }
2445 case DUK_TAG_POINTER: {
2446 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
2447 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_POINTER);
2448 proto = DUK_BIDX_POINTER_PROTOTYPE;
2449 goto create_object;
2450 }
2451 case DUK_TAG_LIGHTFUNC: {
2452 /* Lightfunc coerces to a Function instance with concrete
2453 * properties. Since 'length' is virtual for Duktape/C
2454 * functions, don't need to define that.
2455 *
2456 * The result is made extensible to mimic what happens to
2457 * strings:
2458 * > Object.isExtensible(Object('foo'))
2459 * true
2460 */
2461 duk_small_uint_t lf_flags;
2462 duk_small_uint_t nargs;
2463 duk_small_uint_t lf_len;
2464 duk_c_function func;
2465 duk_hnativefunction *nf;
2466
2467 DUK_TVAL_GET_LIGHTFUNC(tv, func, lf_flags);
2468
2469 nargs = DUK_LFUNC_FLAGS_GET_NARGS(lf_flags);
2470 if (nargs == DUK_LFUNC_NARGS_VARARGS) {
2471 nargs = DUK_VARARGS;
2472 }
2473 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
2474 DUK_HOBJECT_FLAG_CONSTRUCTABLE |
2475 DUK_HOBJECT_FLAG_NATIVEFUNCTION |
2476 DUK_HOBJECT_FLAG_NEWENV |
2477 DUK_HOBJECT_FLAG_STRICT |
2478 DUK_HOBJECT_FLAG_NOTAIL |
2479 /* DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC: omitted here intentionally */
2480 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
2481 (void) duk__push_c_function_raw(ctx, func, (duk_idx_t) nargs, flags);
2482
2483 lf_len = DUK_LFUNC_FLAGS_GET_LENGTH(lf_flags);
2484 if (lf_len != nargs) {
2485 /* Explicit length is only needed if it differs from 'nargs'. */
2486 duk_push_int(ctx, (duk_int_t) lf_len);
2487 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LENGTH, DUK_PROPDESC_FLAGS_NONE);
2488 }
2489 duk_push_lightfunc_name(ctx, tv);
2490 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE);
2491
2492 nf = duk_get_hnativefunction(ctx, -1);
2493 DUK_ASSERT(nf != NULL);
2494 nf->magic = (duk_int16_t) DUK_LFUNC_FLAGS_GET_MAGIC(lf_flags);
2495
2496 /* Enable DUKFUNC exotic behavior once properties are set up. */
2497 DUK_HOBJECT_SET_EXOTIC_DUKFUNC((duk_hobject *) nf);
2498 goto replace_value;
2499 }
2500#if defined(DUK_USE_FASTINT)
2501 case DUK_TAG_FASTINT:
2502#endif
2503 default: {
2504 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
2505 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_NUMBER);
2506 proto = DUK_BIDX_NUMBER_PROTOTYPE;
2507 goto create_object;
2508 }
2509 }
2510 return;
2511
2512 create_object:
2513 (void) duk_push_object_helper(ctx, flags, proto);
2514
2515 /* Note: Boolean prototype's internal value property is not writable,
2516 * but duk_xdef_prop_stridx() disregards the write protection. Boolean
2517 * instances are immutable.
2518 *
2519 * String and buffer special behaviors are already enabled which is not
2520 * ideal, but a write to the internal value is not affected by them.
2521 */
2522 duk_dup(ctx, index);
2523 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_NONE);
2524
2525 replace_value:
2526 duk_replace(ctx, index);
2527}
2528
2529/*
2530 * Type checking
2531 */
2532
2533DUK_LOCAL duk_bool_t duk__tag_check(duk_context *ctx, duk_idx_t index, duk_small_uint_t tag) {
2534 duk_tval *tv;
2535
2536 tv = duk_get_tval(ctx, index);
2537 if (!tv) {
2538 return 0;
2539 }
2540 return (DUK_TVAL_GET_TAG(tv) == tag);
2541}
2542
2543DUK_LOCAL duk_bool_t duk__obj_flag_any_default_false(duk_context *ctx, duk_idx_t index, duk_uint_t flag_mask) {
2544 duk_hobject *obj;
2545
2546 DUK_ASSERT_CTX_VALID(ctx);
2547
2548 obj = duk_get_hobject(ctx, index);
2549 if (obj) {
2550 return (DUK_HEAPHDR_CHECK_FLAG_BITS((duk_heaphdr *) obj, flag_mask) ? 1 : 0);
2551 }
2552 return 0;
2553}
2554
2555DUK_EXTERNAL duk_int_t duk_get_type(duk_context *ctx, duk_idx_t index) {
2556 duk_tval *tv;
2557
2558 DUK_ASSERT_CTX_VALID(ctx);
2559
2560 tv = duk_get_tval(ctx, index);
2561 if (!tv) {
2562 return DUK_TYPE_NONE;
2563 }
2564 switch (DUK_TVAL_GET_TAG(tv)) {
2565 case DUK_TAG_UNDEFINED:
2566 return DUK_TYPE_UNDEFINED;
2567 case DUK_TAG_NULL:
2568 return DUK_TYPE_NULL;
2569 case DUK_TAG_BOOLEAN:
2570 return DUK_TYPE_BOOLEAN;
2571 case DUK_TAG_STRING:
2572 return DUK_TYPE_STRING;
2573 case DUK_TAG_OBJECT:
2574 return DUK_TYPE_OBJECT;
2575 case DUK_TAG_BUFFER:
2576 return DUK_TYPE_BUFFER;
2577 case DUK_TAG_POINTER:
2578 return DUK_TYPE_POINTER;
2579 case DUK_TAG_LIGHTFUNC:
2580 return DUK_TYPE_LIGHTFUNC;
2581#if defined(DUK_USE_FASTINT)
2582 case DUK_TAG_FASTINT:
2583#endif
2584 default:
2585 /* Note: number has no explicit tag (in 8-byte representation) */
2586 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
2587 return DUK_TYPE_NUMBER;
2588 }
2589 DUK_UNREACHABLE();
2590}
2591
2592DUK_EXTERNAL duk_bool_t duk_check_type(duk_context *ctx, duk_idx_t index, duk_int_t type) {
2593 DUK_ASSERT_CTX_VALID(ctx);
2594
2595 return (duk_get_type(ctx, index) == type) ? 1 : 0;
2596}
2597
2598DUK_EXTERNAL duk_uint_t duk_get_type_mask(duk_context *ctx, duk_idx_t index) {
2599 duk_tval *tv;
2600
2601 DUK_ASSERT_CTX_VALID(ctx);
2602
2603 tv = duk_get_tval(ctx, index);
2604 if (!tv) {
2605 return DUK_TYPE_MASK_NONE;
2606 }
2607 switch (DUK_TVAL_GET_TAG(tv)) {
2608 case DUK_TAG_UNDEFINED:
2609 return DUK_TYPE_MASK_UNDEFINED;
2610 case DUK_TAG_NULL:
2611 return DUK_TYPE_MASK_NULL;
2612 case DUK_TAG_BOOLEAN:
2613 return DUK_TYPE_MASK_BOOLEAN;
2614 case DUK_TAG_STRING:
2615 return DUK_TYPE_MASK_STRING;
2616 case DUK_TAG_OBJECT:
2617 return DUK_TYPE_MASK_OBJECT;
2618 case DUK_TAG_BUFFER:
2619 return DUK_TYPE_MASK_BUFFER;
2620 case DUK_TAG_POINTER:
2621 return DUK_TYPE_MASK_POINTER;
2622 case DUK_TAG_LIGHTFUNC:
2623 return DUK_TYPE_MASK_LIGHTFUNC;
2624#if defined(DUK_USE_FASTINT)
2625 case DUK_TAG_FASTINT:
2626#endif
2627 default:
2628 /* Note: number has no explicit tag (in 8-byte representation) */
2629 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv));
2630 return DUK_TYPE_MASK_NUMBER;
2631 }
2632 DUK_UNREACHABLE();
2633}
2634
2635DUK_EXTERNAL duk_bool_t duk_check_type_mask(duk_context *ctx, duk_idx_t index, duk_uint_t mask) {
2636 duk_hthread *thr = (duk_hthread *) ctx;
2637
2638 DUK_ASSERT_CTX_VALID(ctx);
2639
2640 if (duk_get_type_mask(ctx, index) & mask) {
2641 return 1;
2642 }
2643 if (mask & DUK_TYPE_MASK_THROW) {
2644 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_UNEXPECTED_TYPE);
2645 DUK_UNREACHABLE();
2646 }
2647 return 0;
2648}
2649
2650DUK_EXTERNAL duk_bool_t duk_is_undefined(duk_context *ctx, duk_idx_t index) {
2651 DUK_ASSERT_CTX_VALID(ctx);
2652 return duk__tag_check(ctx, index, DUK_TAG_UNDEFINED);
2653}
2654
2655DUK_EXTERNAL duk_bool_t duk_is_null(duk_context *ctx, duk_idx_t index) {
2656 DUK_ASSERT_CTX_VALID(ctx);
2657 return duk__tag_check(ctx, index, DUK_TAG_NULL);
2658}
2659
2660DUK_EXTERNAL duk_bool_t duk_is_null_or_undefined(duk_context *ctx, duk_idx_t index) {
2661 duk_tval *tv;
2662 duk_small_uint_t tag;
2663
2664 DUK_ASSERT_CTX_VALID(ctx);
2665
2666 tv = duk_get_tval(ctx, index);
2667 if (!tv) {
2668 return 0;
2669 }
2670 tag = DUK_TVAL_GET_TAG(tv);
2671 return (tag == DUK_TAG_UNDEFINED) || (tag == DUK_TAG_NULL);
2672}
2673
2674DUK_EXTERNAL duk_bool_t duk_is_boolean(duk_context *ctx, duk_idx_t index) {
2675 DUK_ASSERT_CTX_VALID(ctx);
2676 return duk__tag_check(ctx, index, DUK_TAG_BOOLEAN);
2677}
2678
2679DUK_EXTERNAL duk_bool_t duk_is_number(duk_context *ctx, duk_idx_t index) {
2680 duk_tval *tv;
2681
2682 DUK_ASSERT_CTX_VALID(ctx);
2683
2684 /*
2685 * Number is special because it doesn't have a specific
2686 * tag in the 8-byte representation.
2687 */
2688
2689 /* XXX: shorter version for 12-byte representation? */
2690
2691 tv = duk_get_tval(ctx, index);
2692 if (!tv) {
2693 return 0;
2694 }
2695 return DUK_TVAL_IS_NUMBER(tv);
2696}
2697
2698DUK_EXTERNAL duk_bool_t duk_is_nan(duk_context *ctx, duk_idx_t index) {
2699 /* XXX: This will now return false for non-numbers, even though they would
2700 * coerce to NaN (as a general rule). In particular, duk_get_number()
2701 * returns a NaN for non-numbers, so should this function also return
2702 * true for non-numbers?
2703 */
2704
2705 duk_tval *tv;
2706
2707 DUK_ASSERT_CTX_VALID(ctx);
2708
2709 tv = duk_get_tval(ctx, index);
2710 if (!tv || !DUK_TVAL_IS_NUMBER(tv)) {
2711 return 0;
2712 }
2713 return DUK_ISNAN(DUK_TVAL_GET_NUMBER(tv));
2714}
2715
2716DUK_EXTERNAL duk_bool_t duk_is_string(duk_context *ctx, duk_idx_t index) {
2717 DUK_ASSERT_CTX_VALID(ctx);
2718 return duk__tag_check(ctx, index, DUK_TAG_STRING);
2719}
2720
2721DUK_EXTERNAL duk_bool_t duk_is_object(duk_context *ctx, duk_idx_t index) {
2722 DUK_ASSERT_CTX_VALID(ctx);
2723 return duk__tag_check(ctx, index, DUK_TAG_OBJECT);
2724}
2725
2726DUK_EXTERNAL duk_bool_t duk_is_buffer(duk_context *ctx, duk_idx_t index) {
2727 DUK_ASSERT_CTX_VALID(ctx);
2728 return duk__tag_check(ctx, index, DUK_TAG_BUFFER);
2729}
2730
2731DUK_EXTERNAL duk_bool_t duk_is_pointer(duk_context *ctx, duk_idx_t index) {
2732 DUK_ASSERT_CTX_VALID(ctx);
2733 return duk__tag_check(ctx, index, DUK_TAG_POINTER);
2734}
2735
2736DUK_EXTERNAL duk_bool_t duk_is_lightfunc(duk_context *ctx, duk_idx_t index) {
2737 DUK_ASSERT_CTX_VALID(ctx);
2738 return duk__tag_check(ctx, index, DUK_TAG_LIGHTFUNC);
2739}
2740
2741DUK_EXTERNAL duk_bool_t duk_is_array(duk_context *ctx, duk_idx_t index) {
2742 duk_hobject *obj;
2743
2744 DUK_ASSERT_CTX_VALID(ctx);
2745
2746 obj = duk_get_hobject(ctx, index);
2747 if (obj) {
2748 return (DUK_HOBJECT_GET_CLASS_NUMBER(obj) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0);
2749 }
2750 return 0;
2751}
2752
2753DUK_EXTERNAL duk_bool_t duk_is_function(duk_context *ctx, duk_idx_t index) {
2754 duk_tval *tv;
2755
2756 DUK_ASSERT_CTX_VALID(ctx);
2757
2758 tv = duk_get_tval(ctx, index);
2759 if (tv && DUK_TVAL_IS_LIGHTFUNC(tv)) {
2760 return 1;
2761 }
2762 return duk__obj_flag_any_default_false(ctx,
2763 index,
2764 DUK_HOBJECT_FLAG_COMPILEDFUNCTION |
2765 DUK_HOBJECT_FLAG_NATIVEFUNCTION |
2766 DUK_HOBJECT_FLAG_BOUND);
2767}
2768
2769DUK_EXTERNAL duk_bool_t duk_is_c_function(duk_context *ctx, duk_idx_t index) {
2770 DUK_ASSERT_CTX_VALID(ctx);
2771 return duk__obj_flag_any_default_false(ctx,
2772 index,
2773 DUK_HOBJECT_FLAG_NATIVEFUNCTION);
2774}
2775
2776DUK_EXTERNAL duk_bool_t duk_is_ecmascript_function(duk_context *ctx, duk_idx_t index) {
2777 DUK_ASSERT_CTX_VALID(ctx);
2778 return duk__obj_flag_any_default_false(ctx,
2779 index,
2780 DUK_HOBJECT_FLAG_COMPILEDFUNCTION);
2781}
2782
2783DUK_EXTERNAL duk_bool_t duk_is_bound_function(duk_context *ctx, duk_idx_t index) {
2784 DUK_ASSERT_CTX_VALID(ctx);
2785 return duk__obj_flag_any_default_false(ctx,
2786 index,
2787 DUK_HOBJECT_FLAG_BOUND);
2788}
2789
2790DUK_EXTERNAL duk_bool_t duk_is_thread(duk_context *ctx, duk_idx_t index) {
2791 DUK_ASSERT_CTX_VALID(ctx);
2792 return duk__obj_flag_any_default_false(ctx,
2793 index,
2794 DUK_HOBJECT_FLAG_THREAD);
2795}
2796
2797DUK_EXTERNAL duk_bool_t duk_is_callable(duk_context *ctx, duk_idx_t index) {
2798 /* XXX: currently same as duk_is_function() */
2799 DUK_ASSERT_CTX_VALID(ctx);
2800 return duk_is_function(ctx, index);
2801}
2802
2803DUK_EXTERNAL duk_bool_t duk_is_fixed_buffer(duk_context *ctx, duk_idx_t index) {
2804 duk_tval *tv;
2805
2806 DUK_ASSERT_CTX_VALID(ctx);
2807
2808 tv = duk_get_tval(ctx, index);
2809 if (tv && DUK_TVAL_IS_BUFFER(tv)) {
2810 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
2811 DUK_ASSERT(h != NULL);
2812 return (DUK_HBUFFER_HAS_DYNAMIC(h) ? 0 : 1);
2813 }
2814 return 0;
2815}
2816
2817DUK_EXTERNAL duk_bool_t duk_is_dynamic_buffer(duk_context *ctx, duk_idx_t index) {
2818 duk_tval *tv;
2819
2820 DUK_ASSERT_CTX_VALID(ctx);
2821
2822 tv = duk_get_tval(ctx, index);
2823 if (tv && DUK_TVAL_IS_BUFFER(tv)) {
2824 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
2825 DUK_ASSERT(h != NULL);
2826 return (DUK_HBUFFER_HAS_DYNAMIC(h) && !DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0);
2827 }
2828 return 0;
2829}
2830
2831DUK_EXTERNAL duk_bool_t duk_is_external_buffer(duk_context *ctx, duk_idx_t index) {
2832 duk_tval *tv;
2833
2834 DUK_ASSERT_CTX_VALID(ctx);
2835
2836 tv = duk_get_tval(ctx, index);
2837 if (tv && DUK_TVAL_IS_BUFFER(tv)) {
2838 duk_hbuffer *h = DUK_TVAL_GET_BUFFER(tv);
2839 DUK_ASSERT(h != NULL);
2840 return (DUK_HBUFFER_HAS_DYNAMIC(h) && DUK_HBUFFER_HAS_EXTERNAL(h) ? 1 : 0);
2841 }
2842 return 0;
2843}
2844
2845DUK_EXTERNAL duk_errcode_t duk_get_error_code(duk_context *ctx, duk_idx_t index) {
2846 duk_hthread *thr = (duk_hthread *) ctx;
2847 duk_hobject *h;
2848 duk_uint_t sanity;
2849
2850 DUK_ASSERT_CTX_VALID(ctx);
2851
2852 h = duk_get_hobject(ctx, index);
2853
2854 sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
2855 do {
2856 if (!h) {
2857 return DUK_ERR_NONE;
2858 }
2859 if (h == thr->builtins[DUK_BIDX_EVAL_ERROR_PROTOTYPE]) {
2860 return DUK_ERR_EVAL_ERROR;
2861 }
2862 if (h == thr->builtins[DUK_BIDX_RANGE_ERROR_PROTOTYPE]) {
2863 return DUK_ERR_RANGE_ERROR;
2864 }
2865 if (h == thr->builtins[DUK_BIDX_REFERENCE_ERROR_PROTOTYPE]) {
2866 return DUK_ERR_REFERENCE_ERROR;
2867 }
2868 if (h == thr->builtins[DUK_BIDX_SYNTAX_ERROR_PROTOTYPE]) {
2869 return DUK_ERR_SYNTAX_ERROR;
2870 }
2871 if (h == thr->builtins[DUK_BIDX_TYPE_ERROR_PROTOTYPE]) {
2872 return DUK_ERR_TYPE_ERROR;
2873 }
2874 if (h == thr->builtins[DUK_BIDX_URI_ERROR_PROTOTYPE]) {
2875 return DUK_ERR_URI_ERROR;
2876 }
2877 if (h == thr->builtins[DUK_BIDX_ERROR_PROTOTYPE]) {
2878 return DUK_ERR_ERROR;
2879 }
2880
2881 h = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h);
2882 } while (--sanity > 0);
2883
2884 return DUK_ERR_NONE;
2885}
2886
2887/*
2888 * Pushers
2889 */
2890
2891DUK_INTERNAL void duk_push_tval(duk_context *ctx, duk_tval *tv) {
2892 duk_hthread *thr;
2893 duk_tval *tv_slot;
2894
2895 DUK_ASSERT_CTX_VALID(ctx);
2896 DUK_ASSERT(tv != NULL);
2897 thr = (duk_hthread *) ctx;
2898 DUK__CHECK_SPACE();
2899 tv_slot = thr->valstack_top++;
2900 DUK_TVAL_SET_TVAL(tv_slot, tv);
2901 DUK_TVAL_INCREF(thr, tv); /* no side effects */
2902}
2903
2904#if defined(DUK_USE_DEBUGGER_SUPPORT)
2905/* Right now only needed by the debugger. */
2906DUK_INTERNAL void duk_push_unused(duk_context *ctx) {
2907 duk_hthread *thr;
2908 duk_tval *tv_slot;
2909
2910 DUK_ASSERT_CTX_VALID(ctx);
2911 thr = (duk_hthread *) ctx;
2912 DUK__CHECK_SPACE();
2913 tv_slot = thr->valstack_top++;
2914 DUK_TVAL_SET_UNDEFINED_UNUSED(tv_slot);
2915}
2916#endif
2917
2918DUK_EXTERNAL void duk_push_undefined(duk_context *ctx) {
2919 duk_hthread *thr;
2920 duk_tval *tv_slot;
2921
2922 DUK_ASSERT_CTX_VALID(ctx);
2923 thr = (duk_hthread *) ctx;
2924 DUK__CHECK_SPACE();
2925 tv_slot = thr->valstack_top++;
2926 DUK_TVAL_SET_UNDEFINED_ACTUAL(tv_slot);
2927}
2928
2929DUK_EXTERNAL void duk_push_null(duk_context *ctx) {
2930 duk_hthread *thr;
2931 duk_tval *tv_slot;
2932
2933 DUK_ASSERT_CTX_VALID(ctx);
2934 thr = (duk_hthread *) ctx;
2935 DUK__CHECK_SPACE();
2936 tv_slot = thr->valstack_top++;
2937 DUK_TVAL_SET_NULL(tv_slot);
2938}
2939
2940DUK_EXTERNAL void duk_push_boolean(duk_context *ctx, duk_bool_t val) {
2941 duk_hthread *thr;
2942 duk_tval *tv_slot;
2943 duk_small_int_t b;
2944
2945 DUK_ASSERT_CTX_VALID(ctx);
2946 thr = (duk_hthread *) ctx;
2947 DUK__CHECK_SPACE();
2948 b = (val ? 1 : 0); /* ensure value is 1 or 0 (not other non-zero) */
2949 tv_slot = thr->valstack_top++;
2950 DUK_TVAL_SET_BOOLEAN(tv_slot, b);
2951}
2952
2953DUK_EXTERNAL void duk_push_true(duk_context *ctx) {
2954 duk_hthread *thr;
2955 duk_tval *tv_slot;
2956
2957 DUK_ASSERT_CTX_VALID(ctx);
2958 thr = (duk_hthread *) ctx;
2959 DUK__CHECK_SPACE();
2960 tv_slot = thr->valstack_top++;
2961 DUK_TVAL_SET_BOOLEAN_TRUE(tv_slot);
2962}
2963
2964DUK_EXTERNAL void duk_push_false(duk_context *ctx) {
2965 duk_hthread *thr;
2966 duk_tval *tv_slot;
2967
2968 DUK_ASSERT_CTX_VALID(ctx);
2969 thr = (duk_hthread *) ctx;
2970 DUK__CHECK_SPACE();
2971 tv_slot = thr->valstack_top++;
2972 DUK_TVAL_SET_BOOLEAN_FALSE(tv_slot);
2973}
2974
2975/* normalize NaN which may not match our canonical internal NaN */
2976DUK_EXTERNAL void duk_push_number(duk_context *ctx, duk_double_t val) {
2977 duk_hthread *thr;
2978 duk_tval *tv_slot;
2979 duk_double_union du;
2980
2981 DUK_ASSERT_CTX_VALID(ctx);
2982 thr = (duk_hthread *) ctx;
2983 DUK__CHECK_SPACE();
2984 du.d = val;
2985 DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du);
2986 tv_slot = thr->valstack_top++;
2987 DUK_TVAL_SET_NUMBER(tv_slot, du.d);
2988}
2989
2990DUK_EXTERNAL void duk_push_int(duk_context *ctx, duk_int_t val) {
2991#if defined(DUK_USE_FASTINT)
2992 duk_hthread *thr;
2993 duk_tval *tv_slot;
2994
2995 DUK_ASSERT_CTX_VALID(ctx);
2996 thr = (duk_hthread *) ctx;
2997 DUK__CHECK_SPACE();
2998 tv_slot = thr->valstack_top++;
2999#if DUK_INT_MAX <= 0x7fffffffL
3000 DUK_TVAL_SET_FASTINT_I32(tv_slot, (duk_int32_t) val);
3001#else
3002 if (val >= DUK_FASTINT_MIN && val <= DUK_FASTINT_MAX) {
3003 DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val);
3004 } else {
3005 duk_double_t = (duk_double_t) val;
3006 DUK_TVAL_SET_NUMBER(tv_slot, d);
3007 }
3008#endif
3009#else /* DUK_USE_FASTINT */
3010 duk_hthread *thr;
3011 duk_tval *tv_slot;
3012 duk_double_t d;
3013
3014 DUK_ASSERT_CTX_VALID(ctx);
3015 thr = (duk_hthread *) ctx;
3016 DUK__CHECK_SPACE();
3017 d = (duk_double_t) val;
3018 tv_slot = thr->valstack_top++;
3019 DUK_TVAL_SET_NUMBER(tv_slot, d);
3020#endif /* DUK_USE_FASTINT */
3021}
3022
3023DUK_EXTERNAL void duk_push_uint(duk_context *ctx, duk_uint_t val) {
3024#if defined(DUK_USE_FASTINT)
3025 duk_hthread *thr;
3026 duk_tval *tv_slot;
3027
3028 DUK_ASSERT_CTX_VALID(ctx);
3029 thr = (duk_hthread *) ctx;
3030 DUK__CHECK_SPACE();
3031 tv_slot = thr->valstack_top++;
3032#if DUK_UINT_MAX <= 0xffffffffUL
3033 DUK_TVAL_SET_FASTINT_U32(tv_slot, (duk_uint32_t) val);
3034#else
3035 if (val <= DUK_FASTINT_MAX) { /* val is unsigned so >= 0 */
3036 /* XXX: take advantage of val being unsigned, no need to mask */
3037 DUK_TVAL_SET_FASTINT(tv_slot, (duk_int64_t) val);
3038 } else {
3039 duk_double_t = (duk_double_t) val;
3040 DUK_TVAL_SET_NUMBER(tv_slot, d);
3041 }
3042#endif
3043#else /* DUK_USE_FASTINT */
3044 duk_hthread *thr;
3045 duk_tval *tv_slot;
3046 duk_double_t d;
3047
3048 DUK_ASSERT_CTX_VALID(ctx);
3049 thr = (duk_hthread *) ctx;
3050 DUK__CHECK_SPACE();
3051 d = (duk_double_t) val;
3052 tv_slot = thr->valstack_top++;
3053 DUK_TVAL_SET_NUMBER(tv_slot, d);
3054#endif /* DUK_USE_FASTINT */
3055}
3056
3057DUK_EXTERNAL void duk_push_nan(duk_context *ctx) {
3058 duk_hthread *thr;
3059 duk_tval *tv_slot;
3060 duk_double_union du;
3061
3062 DUK_ASSERT_CTX_VALID(ctx);
3063 thr = (duk_hthread *) ctx;
3064 DUK__CHECK_SPACE();
3065 DUK_DBLUNION_SET_NAN(&du);
3066 DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
3067 tv_slot = thr->valstack_top++;
3068 DUK_TVAL_SET_NUMBER(tv_slot, du.d);
3069}
3070
3071DUK_EXTERNAL const char *duk_push_lstring(duk_context *ctx, const char *str, duk_size_t len) {
3072 duk_hthread *thr = (duk_hthread *) ctx;
3073 duk_hstring *h;
3074 duk_tval *tv_slot;
3075
3076 DUK_ASSERT_CTX_VALID(ctx);
3077
3078 /* check stack before interning (avoid hanging temp) */
3079 if (thr->valstack_top >= thr->valstack_end) {
3080 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3081 }
3082
3083 /* NULL with zero length represents an empty string; NULL with higher
3084 * length is also now trated like an empty string although it is
3085 * a bit dubious. This is unlike duk_push_string() which pushes a
3086 * 'null' if the input string is a NULL.
3087 */
3088 if (!str) {
3089 len = 0;
3090 }
3091
3092 /* Check for maximum string length */
3093 if (len > DUK_HSTRING_MAX_BYTELEN) {
3094 DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_STRING_TOO_LONG);
3095 }
3096
3097 h = duk_heap_string_intern_checked(thr, (duk_uint8_t *) str, (duk_uint32_t) len);
3098 DUK_ASSERT(h != NULL);
3099
3100 tv_slot = thr->valstack_top++;
3101 DUK_TVAL_SET_STRING(tv_slot, h);
3102 DUK_HSTRING_INCREF(thr, h); /* no side effects */
3103
3104 return (const char *) DUK_HSTRING_GET_DATA(h);
3105}
3106
3107DUK_EXTERNAL const char *duk_push_string(duk_context *ctx, const char *str) {
3108 DUK_ASSERT_CTX_VALID(ctx);
3109
3110 if (str) {
3111 return duk_push_lstring(ctx, str, DUK_STRLEN(str));
3112 } else {
3113 duk_push_null(ctx);
3114 return NULL;
3115 }
3116}
3117
3118#ifdef DUK_USE_FILE_IO
3119/* This is a bit clunky because it is ANSI C portable. Should perhaps
3120 * relocate to another file because this is potentially platform
3121 * dependent.
3122 */
3123DUK_EXTERNAL const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
3124 duk_hthread *thr = (duk_hthread *) ctx;
3125 duk_file *f = NULL;
3126 char *buf;
3127 long sz; /* ANSI C typing */
3128
3129 DUK_ASSERT_CTX_VALID(ctx);
3130
3131 if (!path) {
3132 goto fail;
3133 }
3134 f = DUK_FOPEN(path, "rb");
3135 if (!f) {
3136 goto fail;
3137 }
3138 if (DUK_FSEEK(f, 0, SEEK_END) < 0) {
3139 goto fail;
3140 }
3141 sz = DUK_FTELL(f);
3142 if (sz < 0) {
3143 goto fail;
3144 }
3145 if (DUK_FSEEK(f, 0, SEEK_SET) < 0) {
3146 goto fail;
3147 }
3148 buf = (char *) duk_push_fixed_buffer(ctx, (duk_size_t) sz);
3149 DUK_ASSERT(buf != NULL);
3150 if ((duk_size_t) DUK_FREAD(buf, 1, (size_t) sz, f) != (duk_size_t) sz) {
3151 goto fail;
3152 }
3153 (void) DUK_FCLOSE(f); /* ignore fclose() error */
3154 f = NULL;
3155 return duk_to_string(ctx, -1);
3156
3157 fail:
3158 if (f) {
3159 DUK_FCLOSE(f);
3160 }
3161
3162 if (flags != 0) {
3163 DUK_ASSERT(flags == DUK_STRING_PUSH_SAFE); /* only flag now */
3164 duk_push_undefined(ctx);
3165 } else {
3166 /* XXX: string not shared because it is conditional */
3167 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "read file error");
3168 }
3169 return NULL;
3170}
3171#else
3172DUK_EXTERNAL const char *duk_push_string_file_raw(duk_context *ctx, const char *path, duk_uint_t flags) {
3173 duk_hthread *thr = (duk_hthread *) ctx;
3174 DUK_ASSERT_CTX_VALID(ctx);
3175 DUK_UNREF(path);
3176
3177 if (flags != 0) {
3178 DUK_ASSERT(flags == DUK_STRING_PUSH_SAFE); /* only flag now */
3179 duk_push_undefined(ctx);
3180 } else {
3181 /* XXX: string not shared because it is conditional */
3182 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, "file I/O disabled");
3183 }
3184 return NULL;
3185}
3186#endif /* DUK_USE_FILE_IO */
3187
3188DUK_EXTERNAL void duk_push_pointer(duk_context *ctx, void *val) {
3189 duk_hthread *thr;
3190 duk_tval *tv_slot;
3191
3192 DUK_ASSERT_CTX_VALID(ctx);
3193 thr = (duk_hthread *) ctx;
3194 DUK__CHECK_SPACE();
3195 tv_slot = thr->valstack_top++;
3196 DUK_TVAL_SET_POINTER(tv_slot, val);
3197}
3198
3199#define DUK__PUSH_THIS_FLAG_CHECK_COERC (1 << 0)
3200#define DUK__PUSH_THIS_FLAG_TO_OBJECT (1 << 1)
3201#define DUK__PUSH_THIS_FLAG_TO_STRING (1 << 2)
3202
3203DUK_LOCAL void duk__push_this_helper(duk_context *ctx, duk_small_uint_t flags) {
3204 duk_hthread *thr = (duk_hthread *) ctx;
3205
3206 DUK_ASSERT(thr != NULL);
3207 DUK_ASSERT_CTX_VALID(ctx);
3208 DUK_ASSERT_DISABLE(thr->callstack_top >= 0); /* avoid warning (unsigned) */
3209 DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
3210
3211 if (thr->callstack_top == 0) {
3212 if (flags & DUK__PUSH_THIS_FLAG_CHECK_COERC) {
3213 goto type_error;
3214 }
3215 duk_push_undefined(ctx);
3216 } else {
3217 duk_tval tv_tmp;
3218 duk_tval *tv;
3219
3220 /* 'this' binding is just before current activation's bottom */
3221 DUK_ASSERT(thr->valstack_bottom > thr->valstack);
3222 tv = thr->valstack_bottom - 1;
3223 if (flags & DUK__PUSH_THIS_FLAG_CHECK_COERC) {
3224 if (DUK_TVAL_IS_UNDEFINED(tv) || DUK_TVAL_IS_NULL(tv)) {
3225 goto type_error;
3226 }
3227 }
3228
3229 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
3230 duk_push_tval(ctx, &tv_tmp);
3231 }
3232
3233 if (flags & DUK__PUSH_THIS_FLAG_TO_OBJECT) {
3234 duk_to_object(ctx, -1);
3235 } else if (flags & DUK__PUSH_THIS_FLAG_TO_STRING) {
3236 duk_to_string(ctx, -1);
3237 }
3238
3239 return;
3240
3241 type_error:
3242 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_NOT_OBJECT_COERCIBLE);
3243}
3244
3245DUK_EXTERNAL void duk_push_this(duk_context *ctx) {
3246 DUK_ASSERT_CTX_VALID(ctx);
3247
3248 duk__push_this_helper(ctx, 0 /*flags*/);
3249}
3250
3251DUK_INTERNAL void duk_push_this_check_object_coercible(duk_context *ctx) {
3252 DUK_ASSERT_CTX_VALID(ctx);
3253
3254 duk__push_this_helper(ctx, DUK__PUSH_THIS_FLAG_CHECK_COERC /*flags*/);
3255}
3256
3257DUK_INTERNAL duk_hobject *duk_push_this_coercible_to_object(duk_context *ctx) {
3258 duk_hobject *h;
3259
3260 DUK_ASSERT_CTX_VALID(ctx);
3261
3262 duk__push_this_helper(ctx, DUK__PUSH_THIS_FLAG_CHECK_COERC |
3263 DUK__PUSH_THIS_FLAG_TO_OBJECT /*flags*/);
3264 h = duk_get_hobject(ctx, -1);
3265 DUK_ASSERT(h != NULL);
3266 return h;
3267}
3268
3269DUK_INTERNAL duk_hstring *duk_push_this_coercible_to_string(duk_context *ctx) {
3270 duk_hstring *h;
3271
3272 DUK_ASSERT_CTX_VALID(ctx);
3273
3274 duk__push_this_helper(ctx, DUK__PUSH_THIS_FLAG_CHECK_COERC |
3275 DUK__PUSH_THIS_FLAG_TO_STRING /*flags*/);
3276 h = duk_get_hstring(ctx, -1);
3277 DUK_ASSERT(h != NULL);
3278 return h;
3279}
3280
3281DUK_INTERNAL duk_tval *duk_get_borrowed_this_tval(duk_context *ctx) {
3282 duk_hthread *thr;
3283
3284 DUK_ASSERT(ctx != NULL);
3285 thr = (duk_hthread *) ctx;
3286
3287 DUK_ASSERT(thr->callstack_top > 0); /* caller required to know */
3288 DUK_ASSERT(thr->valstack_bottom > thr->valstack); /* consequence of above */
3289 DUK_ASSERT(thr->valstack_bottom - 1 >= thr->valstack); /* 'this' binding exists */
3290
3291 return thr->valstack_bottom - 1;
3292}
3293
3294DUK_EXTERNAL void duk_push_current_function(duk_context *ctx) {
3295 duk_hthread *thr = (duk_hthread *) ctx;
3296 duk_activation *act;
3297
3298 DUK_ASSERT_CTX_VALID(ctx);
3299 DUK_ASSERT(thr != NULL);
3300 DUK_ASSERT_DISABLE(thr->callstack_top >= 0);
3301 DUK_ASSERT(thr->callstack_top <= thr->callstack_size);
3302
3303 act = duk_hthread_get_current_activation(thr);
3304 if (act) {
3305 duk_push_tval(ctx, &act->tv_func);
3306 } else {
3307 duk_push_undefined(ctx);
3308 }
3309}
3310
3311DUK_EXTERNAL void duk_push_current_thread(duk_context *ctx) {
3312 duk_hthread *thr = (duk_hthread *) ctx;
3313
3314 DUK_ASSERT_CTX_VALID(ctx);
3315 DUK_ASSERT(thr != NULL);
3316
3317 if (thr->heap->curr_thread) {
3318 duk_push_hobject(ctx, (duk_hobject *) thr->heap->curr_thread);
3319 } else {
3320 duk_push_undefined(ctx);
3321 }
3322}
3323
3324DUK_EXTERNAL void duk_push_global_object(duk_context *ctx) {
3325 DUK_ASSERT_CTX_VALID(ctx);
3326
3327 duk_push_hobject_bidx(ctx, DUK_BIDX_GLOBAL);
3328}
3329
3330/* XXX: size optimize */
3331DUK_LOCAL void duk__push_stash(duk_context *ctx) {
3332 DUK_ASSERT_CTX_VALID(ctx);
3333 if (!duk_get_prop_stridx(ctx, -1, DUK_STRIDX_INT_VALUE)) {
3334 DUK_DDD(DUK_DDDPRINT("creating heap/global/thread stash on first use"));
3335 duk_pop(ctx);
3336 duk_push_object_internal(ctx);
3337 duk_dup_top(ctx);
3338 duk_xdef_prop_stridx(ctx, -3, DUK_STRIDX_INT_VALUE, DUK_PROPDESC_FLAGS_C); /* [ ... parent stash stash ] -> [ ... parent stash ] */
3339 }
3340 duk_remove(ctx, -2);
3341}
3342
3343DUK_EXTERNAL void duk_push_heap_stash(duk_context *ctx) {
3344 duk_hthread *thr = (duk_hthread *) ctx;
3345 duk_heap *heap;
3346 DUK_ASSERT_CTX_VALID(ctx);
3347 heap = thr->heap;
3348 DUK_ASSERT(heap->heap_object != NULL);
3349 duk_push_hobject(ctx, heap->heap_object);
3350 duk__push_stash(ctx);
3351}
3352
3353DUK_EXTERNAL void duk_push_global_stash(duk_context *ctx) {
3354 DUK_ASSERT_CTX_VALID(ctx);
3355 duk_push_global_object(ctx);
3356 duk__push_stash(ctx);
3357}
3358
3359DUK_EXTERNAL void duk_push_thread_stash(duk_context *ctx, duk_context *target_ctx) {
3360 duk_hthread *thr = (duk_hthread *) ctx;
3361 DUK_ASSERT_CTX_VALID(ctx);
3362 if (!target_ctx) {
3363 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_CALL_ARGS);
3364 return; /* not reached */
3365 }
3366 duk_push_hobject(ctx, (duk_hobject *) target_ctx);
3367 duk__push_stash(ctx);
3368}
3369
3370/* XXX: duk_ssize_t would be useful here */
3371DUK_LOCAL duk_int_t duk__try_push_vsprintf(duk_context *ctx, void *buf, duk_size_t sz, const char *fmt, va_list ap) {
3372 duk_int_t len;
3373
3374 DUK_ASSERT_CTX_VALID(ctx);
3375 DUK_UNREF(ctx);
3376
3377 /* NUL terminator handling doesn't matter here */
3378 len = DUK_VSNPRINTF((char *) buf, sz, fmt, ap);
3379 if (len < (duk_int_t) sz) {
3380 /* Return value of 'sz' or more indicates output was (potentially)
3381 * truncated.
3382 */
3383 return (duk_int_t) len;
3384 }
3385 return -1;
3386}
3387
3388DUK_EXTERNAL const char *duk_push_vsprintf(duk_context *ctx, const char *fmt, va_list ap) {
3389 duk_hthread *thr = (duk_hthread *) ctx;
3390 duk_uint8_t stack_buf[DUK_PUSH_SPRINTF_INITIAL_SIZE];
3391 duk_size_t sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
3392 duk_bool_t pushed_buf = 0;
3393 void *buf;
3394 duk_int_t len; /* XXX: duk_ssize_t */
3395 const char *res;
3396
3397 DUK_ASSERT_CTX_VALID(ctx);
3398
3399 /* special handling of fmt==NULL */
3400 if (!fmt) {
3401 duk_hstring *h_str;
3402 duk_push_hstring_stridx(ctx, DUK_STRIDX_EMPTY_STRING);
3403 h_str = DUK_HTHREAD_STRING_EMPTY_STRING(thr); /* rely on interning, must be this string */
3404 return (const char *) DUK_HSTRING_GET_DATA(h_str);
3405 }
3406
3407 /* initial estimate based on format string */
3408 sz = DUK_STRLEN(fmt) + 16; /* format plus something to avoid just missing */
3409 if (sz < DUK_PUSH_SPRINTF_INITIAL_SIZE) {
3410 sz = DUK_PUSH_SPRINTF_INITIAL_SIZE;
3411 }
3412 DUK_ASSERT(sz > 0);
3413
3414 /* Try to make do with a stack buffer to avoid allocating a temporary buffer.
3415 * This works 99% of the time which is quite nice.
3416 */
3417 for (;;) {
3418 va_list ap_copy; /* copied so that 'ap' can be reused */
3419
3420 if (sz <= sizeof(stack_buf)) {
3421 buf = stack_buf;
3422 } else if (!pushed_buf) {
3423 pushed_buf = 1;
3424 buf = duk_push_dynamic_buffer(ctx, sz);
3425 } else {
3426 buf = duk_resize_buffer(ctx, -1, sz);
3427 }
3428 DUK_ASSERT(buf != NULL);
3429
3430 DUK_VA_COPY(ap_copy, ap);
3431 len = duk__try_push_vsprintf(ctx, buf, sz, fmt, ap_copy);
3432 va_end(ap_copy);
3433 if (len >= 0) {
3434 break;
3435 }
3436
3437 /* failed, resize and try again */
3438 sz = sz * 2;
3439 if (sz >= DUK_PUSH_SPRINTF_SANITY_LIMIT) {
3440 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_SPRINTF_TOO_LONG);
3441 }
3442 }
3443
3444 /* Cannot use duk_to_string() on the buffer because it is usually
3445 * larger than 'len'. Also, 'buf' is usually a stack buffer.
3446 */
3447 res = duk_push_lstring(ctx, (const char *) buf, (duk_size_t) len); /* [ buf? res ] */
3448 if (pushed_buf) {
3449 duk_remove(ctx, -2);
3450 }
3451 return res;
3452}
3453
3454DUK_EXTERNAL const char *duk_push_sprintf(duk_context *ctx, const char *fmt, ...) {
3455 va_list ap;
3456 const char *ret;
3457
3458 DUK_ASSERT_CTX_VALID(ctx);
3459
3460 /* allow fmt==NULL */
3461 va_start(ap, fmt);
3462 ret = duk_push_vsprintf(ctx, fmt, ap);
3463 va_end(ap);
3464
3465 return ret;
3466}
3467
3468DUK_INTERNAL duk_idx_t duk_push_object_helper(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
3469 duk_hthread *thr = (duk_hthread *) ctx;
3470 duk_tval *tv_slot;
3471 duk_hobject *h;
3472 duk_idx_t ret;
3473
3474 DUK_ASSERT_CTX_VALID(ctx);
3475 DUK_ASSERT(prototype_bidx == -1 ||
3476 (prototype_bidx >= 0 && prototype_bidx < DUK_NUM_BUILTINS));
3477
3478 /* check stack first */
3479 if (thr->valstack_top >= thr->valstack_end) {
3480 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3481 }
3482
3483 h = duk_hobject_alloc(thr->heap, hobject_flags_and_class);
3484 if (!h) {
3485 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
3486 }
3487
3488 DUK_DDD(DUK_DDDPRINT("created object with flags: 0x%08lx", (unsigned long) h->hdr.h_flags));
3489
3490 tv_slot = thr->valstack_top;
3491 DUK_TVAL_SET_OBJECT(tv_slot, h);
3492 DUK_HOBJECT_INCREF(thr, h); /* no side effects */
3493 ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
3494 thr->valstack_top++;
3495
3496 /* object is now reachable */
3497
3498 if (prototype_bidx >= 0) {
3499 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, thr->builtins[prototype_bidx]);
3500 } else {
3501 DUK_ASSERT(prototype_bidx == -1);
3502 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
3503 }
3504
3505 return ret;
3506}
3507
3508DUK_INTERNAL duk_idx_t duk_push_object_helper_proto(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_hobject *proto) {
3509 duk_hthread *thr = (duk_hthread *) ctx;
3510 duk_idx_t ret;
3511 duk_hobject *h;
3512
3513 DUK_ASSERT_CTX_VALID(ctx);
3514
3515 ret = duk_push_object_helper(ctx, hobject_flags_and_class, -1);
3516 h = duk_get_hobject(ctx, -1);
3517 DUK_ASSERT(h != NULL);
3518 DUK_ASSERT(DUK_HOBJECT_GET_PROTOTYPE(thr->heap, h) == NULL);
3519 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, h, proto);
3520 return ret;
3521}
3522
3523DUK_EXTERNAL duk_idx_t duk_push_object(duk_context *ctx) {
3524 DUK_ASSERT_CTX_VALID(ctx);
3525
3526 return duk_push_object_helper(ctx,
3527 DUK_HOBJECT_FLAG_EXTENSIBLE |
3528 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
3529 DUK_BIDX_OBJECT_PROTOTYPE);
3530}
3531
3532DUK_EXTERNAL duk_idx_t duk_push_array(duk_context *ctx) {
3533 duk_hthread *thr = (duk_hthread *) ctx;
3534 duk_hobject *obj;
3535 duk_idx_t ret;
3536
3537 DUK_ASSERT_CTX_VALID(ctx);
3538
3539 ret = duk_push_object_helper(ctx,
3540 DUK_HOBJECT_FLAG_EXTENSIBLE |
3541 DUK_HOBJECT_FLAG_ARRAY_PART |
3542 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAY),
3543 DUK_BIDX_ARRAY_PROTOTYPE);
3544
3545 obj = duk_require_hobject(ctx, ret);
3546
3547 /*
3548 * An array must have a 'length' property (E5 Section 15.4.5.2).
3549 * The special array behavior flag must only be enabled once the
3550 * length property has been added.
3551 *
3552 * The internal property must be a number (and preferably a
3553 * fastint if fastint support is enabled).
3554 */
3555
3556 duk_push_int(ctx, 0);
3557#if defined(DUK_USE_FASTINT)
3558 DUK_ASSERT(DUK_TVAL_IS_FASTINT(duk_require_tval(ctx, -1)));
3559#endif
3560
3561 duk_hobject_define_property_internal(thr,
3562 obj,
3563 DUK_HTHREAD_STRING_LENGTH(thr),
3564 DUK_PROPDESC_FLAGS_W);
3565 DUK_HOBJECT_SET_EXOTIC_ARRAY(obj);
3566
3567 return ret;
3568}
3569
3570DUK_EXTERNAL duk_idx_t duk_push_thread_raw(duk_context *ctx, duk_uint_t flags) {
3571 duk_hthread *thr = (duk_hthread *) ctx;
3572 duk_hthread *obj;
3573 duk_idx_t ret;
3574 duk_tval *tv_slot;
3575
3576 DUK_ASSERT_CTX_VALID(ctx);
3577
3578 /* check stack first */
3579 if (thr->valstack_top >= thr->valstack_end) {
3580 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3581 }
3582
3583 obj = duk_hthread_alloc(thr->heap,
3584 DUK_HOBJECT_FLAG_EXTENSIBLE |
3585 DUK_HOBJECT_FLAG_THREAD |
3586 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_THREAD));
3587 if (!obj) {
3588 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
3589 }
3590 obj->state = DUK_HTHREAD_STATE_INACTIVE;
3591#if defined(DUK_USE_HEAPPTR16)
3592 obj->strs16 = thr->strs16;
3593#else
3594 obj->strs = thr->strs;
3595#endif
3596 DUK_DDD(DUK_DDDPRINT("created thread object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags));
3597
3598 /* make the new thread reachable */
3599 tv_slot = thr->valstack_top;
3600 DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
3601 DUK_HTHREAD_INCREF(thr, obj);
3602 ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
3603 thr->valstack_top++;
3604
3605 /* important to do this *after* pushing, to make the thread reachable for gc */
3606 if (!duk_hthread_init_stacks(thr->heap, obj)) {
3607 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
3608 }
3609
3610 /* initialize built-ins - either by copying or creating new ones */
3611 if (flags & DUK_THREAD_NEW_GLOBAL_ENV) {
3612 duk_hthread_create_builtin_objects(obj);
3613 } else {
3614 duk_hthread_copy_builtin_objects(thr, obj);
3615 }
3616
3617 /* default prototype (Note: 'obj' must be reachable) */
3618 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, obj->builtins[DUK_BIDX_THREAD_PROTOTYPE]);
3619
3620 /* Initial stack size satisfies the stack spare constraints so there
3621 * is no need to require stack here.
3622 */
3623 DUK_ASSERT(DUK_VALSTACK_INITIAL_SIZE >=
3624 DUK_VALSTACK_API_ENTRY_MINIMUM + DUK_VALSTACK_INTERNAL_EXTRA);
3625
3626 return ret;
3627}
3628
3629DUK_INTERNAL duk_idx_t duk_push_compiledfunction(duk_context *ctx) {
3630 duk_hthread *thr = (duk_hthread *) ctx;
3631 duk_hcompiledfunction *obj;
3632 duk_idx_t ret;
3633 duk_tval *tv_slot;
3634
3635 DUK_ASSERT_CTX_VALID(ctx);
3636
3637 /* check stack first */
3638 if (thr->valstack_top >= thr->valstack_end) {
3639 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3640 }
3641
3642 /* Template functions are not strictly constructable (they don't
3643 * have a "prototype" property for instance), so leave the
3644 * DUK_HOBJECT_FLAG_CONSRUCTABLE flag cleared here.
3645 */
3646
3647 obj = duk_hcompiledfunction_alloc(thr->heap,
3648 DUK_HOBJECT_FLAG_EXTENSIBLE |
3649 DUK_HOBJECT_FLAG_COMPILEDFUNCTION |
3650 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION));
3651 if (!obj) {
3652 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
3653 }
3654
3655 DUK_DDD(DUK_DDDPRINT("created compiled function object with flags: 0x%08lx", (unsigned long) obj->obj.hdr.h_flags));
3656
3657 tv_slot = thr->valstack_top;
3658 DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
3659 DUK_HOBJECT_INCREF(thr, obj);
3660 ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
3661 thr->valstack_top++;
3662
3663 /* default prototype (Note: 'obj' must be reachable) */
3664 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
3665
3666 return ret;
3667}
3668
3669DUK_LOCAL duk_idx_t duk__push_c_function_raw(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_uint_t flags) {
3670 duk_hthread *thr = (duk_hthread *) ctx;
3671 duk_hnativefunction *obj;
3672 duk_idx_t ret;
3673 duk_tval *tv_slot;
3674 duk_uint16_t func_nargs;
3675
3676 DUK_ASSERT_CTX_VALID(ctx);
3677
3678 /* check stack first */
3679 if (thr->valstack_top >= thr->valstack_end) {
3680 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3681 }
3682 if (func == NULL) {
3683 goto api_error;
3684 }
3685 if (nargs >= 0 && nargs < DUK_HNATIVEFUNCTION_NARGS_MAX) {
3686 func_nargs = (duk_uint16_t) nargs;
3687 } else if (nargs == DUK_VARARGS) {
3688 func_nargs = DUK_HNATIVEFUNCTION_NARGS_VARARGS;
3689 } else {
3690 goto api_error;
3691 }
3692
3693 obj = duk_hnativefunction_alloc(thr->heap, flags);
3694 if (!obj) {
3695 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
3696 }
3697
3698 obj->func = func;
3699 obj->nargs = func_nargs;
3700
3701 DUK_DDD(DUK_DDDPRINT("created native function object with flags: 0x%08lx, nargs=%ld",
3702 (unsigned long) obj->obj.hdr.h_flags, (long) obj->nargs));
3703
3704 tv_slot = thr->valstack_top;
3705 DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
3706 DUK_HOBJECT_INCREF(thr, obj);
3707 ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
3708 thr->valstack_top++;
3709
3710 /* default prototype (Note: 'obj' must be reachable) */
3711 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE]);
3712
3713 return ret;
3714
3715 api_error:
3716 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_CALL_ARGS);
3717 return 0; /* not reached */
3718}
3719
3720DUK_EXTERNAL duk_idx_t duk_push_c_function(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
3721 duk_uint_t flags;
3722
3723 DUK_ASSERT_CTX_VALID(ctx);
3724
3725 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
3726 DUK_HOBJECT_FLAG_CONSTRUCTABLE |
3727 DUK_HOBJECT_FLAG_NATIVEFUNCTION |
3728 DUK_HOBJECT_FLAG_NEWENV |
3729 DUK_HOBJECT_FLAG_STRICT |
3730 DUK_HOBJECT_FLAG_NOTAIL |
3731 DUK_HOBJECT_FLAG_EXOTIC_DUKFUNC |
3732 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
3733
3734 return duk__push_c_function_raw(ctx, func, nargs, flags);
3735}
3736
3737DUK_INTERNAL void duk_push_c_function_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
3738 duk_uint_t flags;
3739
3740 DUK_ASSERT_CTX_VALID(ctx);
3741
3742 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
3743 DUK_HOBJECT_FLAG_CONSTRUCTABLE |
3744 DUK_HOBJECT_FLAG_NATIVEFUNCTION |
3745 DUK_HOBJECT_FLAG_NEWENV |
3746 DUK_HOBJECT_FLAG_STRICT |
3747 DUK_HOBJECT_FLAG_NOTAIL |
3748 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
3749
3750 (void) duk__push_c_function_raw(ctx, func, nargs, flags);
3751}
3752
3753DUK_INTERNAL void duk_push_c_function_noconstruct_noexotic(duk_context *ctx, duk_c_function func, duk_int_t nargs) {
3754 duk_uint_t flags;
3755
3756 DUK_ASSERT_CTX_VALID(ctx);
3757
3758 flags = DUK_HOBJECT_FLAG_EXTENSIBLE |
3759 DUK_HOBJECT_FLAG_NATIVEFUNCTION |
3760 DUK_HOBJECT_FLAG_NEWENV |
3761 DUK_HOBJECT_FLAG_STRICT |
3762 DUK_HOBJECT_FLAG_NOTAIL |
3763 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_FUNCTION);
3764
3765 (void) duk__push_c_function_raw(ctx, func, nargs, flags);
3766}
3767
3768DUK_EXTERNAL duk_idx_t duk_push_c_lightfunc(duk_context *ctx, duk_c_function func, duk_idx_t nargs, duk_idx_t length, duk_int_t magic) {
3769 duk_hthread *thr = (duk_hthread *) ctx;
3770 duk_tval tv_tmp;
3771 duk_small_uint_t lf_flags;
3772
3773 DUK_ASSERT_CTX_VALID(ctx);
3774
3775 /* check stack first */
3776 if (thr->valstack_top >= thr->valstack_end) {
3777 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3778 }
3779
3780 if (nargs >= DUK_LFUNC_NARGS_MIN && nargs <= DUK_LFUNC_NARGS_MAX) {
3781 /* as is */
3782 } else if (nargs == DUK_VARARGS) {
3783 nargs = DUK_LFUNC_NARGS_VARARGS;
3784 } else {
3785 goto api_error;
3786 }
3787 if (!(length >= DUK_LFUNC_LENGTH_MIN && length <= DUK_LFUNC_LENGTH_MAX)) {
3788 goto api_error;
3789 }
3790 if (!(magic >= DUK_LFUNC_MAGIC_MIN && magic <= DUK_LFUNC_MAGIC_MAX)) {
3791 goto api_error;
3792 }
3793
3794 lf_flags = DUK_LFUNC_FLAGS_PACK(magic, length, nargs);
3795 DUK_TVAL_SET_LIGHTFUNC(&tv_tmp, func, lf_flags);
3796 duk_push_tval(ctx, &tv_tmp); /* XXX: direct valstack write */
3797 DUK_ASSERT(thr->valstack_top != thr->valstack_bottom);
3798 return ((duk_idx_t) (thr->valstack_top - thr->valstack_bottom)) - 1;
3799
3800 api_error:
3801 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_CALL_ARGS);
3802 return 0; /* not reached */
3803}
3804
3805DUK_INTERNAL duk_hbufferobject *duk_push_bufferobject_raw(duk_context *ctx, duk_uint_t hobject_flags_and_class, duk_small_int_t prototype_bidx) {
3806 duk_hthread *thr = (duk_hthread *) ctx;
3807 duk_hbufferobject *obj;
3808 duk_tval *tv_slot;
3809
3810 DUK_ASSERT(ctx != NULL);
3811 DUK_ASSERT(prototype_bidx >= 0);
3812
3813 /* check stack first */
3814 if (thr->valstack_top >= thr->valstack_end) {
3815 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
3816 }
3817
3818 obj = duk_hbufferobject_alloc(thr->heap, hobject_flags_and_class);
3819 if (!obj) {
3820 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
3821 }
3822
3823 DUK_HOBJECT_SET_PROTOTYPE_UPDREF(thr, (duk_hobject *) obj, thr->builtins[prototype_bidx]);
3824 DUK_ASSERT_HBUFFEROBJECT_VALID(obj);
3825
3826 tv_slot = thr->valstack_top;
3827 DUK_TVAL_SET_OBJECT(tv_slot, (duk_hobject *) obj);
3828 DUK_HOBJECT_INCREF(thr, obj);
3829 thr->valstack_top++;
3830
3831 return obj;
3832}
3833
3834/* XXX: There's quite a bit of overlap with buffer creation handling in
3835 * duk_bi_buffer.c. Look for overlap and refactor.
3836 */
3837#define DUK__PACK_ARGS(classnum,protobidx,elemtype,elemshift,isview) \
3838 (((classnum) << 24) | ((protobidx) << 16) | ((elemtype) << 8) | ((elemshift) << 4) | (isview))
3839
3840#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
3841static const duk_uint32_t duk__bufobj_flags_lookup[] = {
3842 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_DUKTAPE_BUFFER */
3843 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_NODEJS_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_NODEJS_BUFFER */
3844 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_ARRAYBUFFER, DUK_BIDX_ARRAYBUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0), /* DUK_BUFOBJ_ARRAYBUFFER */
3845 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_DATAVIEW, DUK_BIDX_DATAVIEW_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_DATAVIEW */
3846 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT8ARRAY, DUK_BIDX_INT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT8, 0, 1), /* DUK_BUFOBJ_INT8ARRAY */
3847 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8ARRAY, DUK_BIDX_UINT8ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 1), /* DUK_BUFOBJ_UINT8ARRAY */
3848 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT8CLAMPEDARRAY, DUK_BIDX_UINT8CLAMPEDARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8CLAMPED, 0, 1), /* DUK_BUFOBJ_UINT8CLAMPEDARRAY */
3849 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT16ARRAY, DUK_BIDX_INT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT16, 1, 1), /* DUK_BUFOBJ_INT16ARRAY */
3850 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT16ARRAY, DUK_BIDX_UINT16ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT16, 1, 1), /* DUK_BUFOBJ_UINT16ARRAY */
3851 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_INT32ARRAY, DUK_BIDX_INT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_INT32, 2, 1), /* DUK_BUFOBJ_INT32ARRAY */
3852 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_UINT32ARRAY, DUK_BIDX_UINT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT32, 2, 1), /* DUK_BUFOBJ_UINT32ARRAY */
3853 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT32ARRAY, DUK_BIDX_FLOAT32ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT32, 2, 1), /* DUK_BUFOBJ_FLOAT32ARRAY */
3854 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_FLOAT64ARRAY, DUK_BIDX_FLOAT64ARRAY_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_FLOAT64, 3, 1) /* DUK_BUFOBJ_FLOAT64ARRAY */
3855};
3856#else /* DUK_USE_BUFFEROBJECT_SUPPORT */
3857/* Only allow Duktape.Buffer when support disabled. */
3858static const duk_uint32_t duk__bufobj_flags_lookup[] = {
3859 DUK__PACK_ARGS(DUK_HOBJECT_CLASS_BUFFER, DUK_BIDX_BUFFER_PROTOTYPE, DUK_HBUFFEROBJECT_ELEM_UINT8, 0, 0) /* DUK_BUFOBJ_DUKTAPE_BUFFER */
3860};
3861#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
3862#undef DUK__PACK_ARGS
3863
3864DUK_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) {
3865 duk_hthread *thr;
3866 duk_hbufferobject *h_bufobj;
3867 duk_hbuffer *h_val;
3868 duk_uint32_t tmp;
3869 duk_uint_t classnum;
3870 duk_uint_t protobidx;
3871 duk_uint_t lookupidx;
3872 duk_uint_t uint_offset, uint_length, uint_added;
3873
3874 DUK_ASSERT_CTX_VALID(ctx);
3875 thr = (duk_hthread *) ctx;
3876 DUK_UNREF(thr);
3877
3878 /* The underlying types for offset/length in duk_hbufferobject is
3879 * duk_uint_t; make sure argument values fit and that offset + length
3880 * does not wrap.
3881 */
3882 uint_offset = (duk_uint_t) byte_offset;
3883 uint_length = (duk_uint_t) byte_length;
3884 if (sizeof(duk_size_t) != sizeof(duk_uint_t)) {
3885 if ((duk_size_t) uint_offset != byte_offset || (duk_size_t) uint_length != byte_length) {
3886 goto range_error;
3887 }
3888 }
3889 uint_added = uint_offset + uint_length;
3890 if (uint_added < uint_offset) {
3891 goto range_error;
3892 }
3893 DUK_ASSERT(uint_added >= uint_offset && uint_added >= uint_length);
3894
3895 DUK_ASSERT_DISABLE(flags >= 0); /* flags is unsigned */
3896 lookupidx = flags & 0x0f; /* 4 low bits */
3897 if (lookupidx >= sizeof(duk__bufobj_flags_lookup) / sizeof(duk_uint32_t)) {
3898 goto arg_error;
3899 }
3900 tmp = duk__bufobj_flags_lookup[lookupidx];
3901 classnum = tmp >> 24;
3902 protobidx = (tmp >> 16) & 0xff;
3903
3904 h_val = duk_require_hbuffer(ctx, idx_buffer);
3905 DUK_ASSERT(h_val != NULL);
3906
3907 h_bufobj = duk_push_bufferobject_raw(ctx,
3908 DUK_HOBJECT_FLAG_EXTENSIBLE |
3909 DUK_HOBJECT_FLAG_BUFFEROBJECT |
3910 DUK_HOBJECT_CLASS_AS_FLAGS(classnum),
3911 protobidx);
3912 DUK_ASSERT(h_bufobj != NULL);
3913
3914 h_bufobj->buf = h_val;
3915 DUK_HBUFFER_INCREF(thr, h_val);
3916 h_bufobj->offset = uint_offset;
3917 h_bufobj->length = uint_length;
3918 h_bufobj->shift = (tmp >> 4) & 0x0f;
3919 h_bufobj->elem_type = (tmp >> 8) & 0xff;
3920 h_bufobj->is_view = tmp & 0x0f;
3921 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
3922
3923#if defined(DUK_USE_BUFFEROBJECT_SUPPORT)
3924 /* TypedArray views need an automatic ArrayBuffer which must be
3925 * provided as .buffer property of the view. Just create a new
3926 * ArrayBuffer sharing the same underlying buffer.
3927 */
3928 if (flags & DUK_BUFOBJ_CREATE_ARRBUF) {
3929 h_bufobj = duk_push_bufferobject_raw(ctx,
3930 DUK_HOBJECT_FLAG_EXTENSIBLE |
3931 DUK_HOBJECT_FLAG_BUFFEROBJECT |
3932 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ARRAYBUFFER),
3933 DUK_BIDX_ARRAYBUFFER_PROTOTYPE);
3934
3935 DUK_ASSERT(h_bufobj != NULL);
3936
3937 h_bufobj->buf = h_val;
3938 DUK_HBUFFER_INCREF(thr, h_val);
3939 h_bufobj->offset = uint_offset;
3940 h_bufobj->length = uint_length;
3941 DUK_ASSERT(h_bufobj->shift == 0);
3942 h_bufobj->elem_type = DUK_HBUFFEROBJECT_ELEM_UINT8;
3943 DUK_ASSERT(h_bufobj->is_view == 0);
3944 DUK_ASSERT_HBUFFEROBJECT_VALID(h_bufobj);
3945
3946 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_LC_BUFFER, DUK_PROPDESC_FLAGS_NONE);
3947 duk_compact(ctx, -1);
3948 }
3949#endif /* DUK_USE_BUFFEROBJECT_SUPPORT */
3950
3951 return;
3952
3953 range_error:
3954 DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_INVALID_CALL_ARGS);
3955 return; /* not reached */
3956
3957 arg_error:
3958 DUK_ERROR(thr, DUK_ERR_TYPE_ERROR, DUK_STR_INVALID_CALL_ARGS);
3959 return; /* not reached */
3960}
3961
3962DUK_EXTERNAL duk_idx_t duk_push_error_object_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
3963 duk_hthread *thr = (duk_hthread *) ctx;
3964 duk_idx_t ret;
3965 duk_hobject *proto;
3966#ifdef DUK_USE_AUGMENT_ERROR_CREATE
3967 duk_bool_t noblame_fileline;
3968#endif
3969
3970 DUK_ASSERT_CTX_VALID(ctx);
3971 DUK_ASSERT(thr != NULL);
3972 DUK_UNREF(filename);
3973 DUK_UNREF(line);
3974
3975 /* Error code also packs a tracedata related flag. */
3976#ifdef DUK_USE_AUGMENT_ERROR_CREATE
3977 noblame_fileline = err_code & DUK_ERRCODE_FLAG_NOBLAME_FILELINE;
3978#endif
3979 err_code = err_code & (~DUK_ERRCODE_FLAG_NOBLAME_FILELINE);
3980
3981 /* error gets its 'name' from the prototype */
3982 proto = duk_error_prototype_from_code(thr, err_code);
3983 ret = duk_push_object_helper_proto(ctx,
3984 DUK_HOBJECT_FLAG_EXTENSIBLE |
3985 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_ERROR),
3986 proto);
3987
3988 /* ... and its 'message' from an instance property */
3989 if (fmt) {
3990 duk_push_vsprintf(ctx, fmt, ap);
3991 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
3992 } else {
3993 /* If no explicit message given, put error code into message field
3994 * (as a number). This is not fully in keeping with the Ecmascript
3995 * error model because messages are supposed to be strings (Error
3996 * constructors use ToString() on their argument). However, it's
3997 * probably more useful than having a separate 'code' property.
3998 */
3999 duk_push_int(ctx, err_code);
4000 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_MESSAGE, DUK_PROPDESC_FLAGS_WC);
4001 }
4002
4003#if 0
4004 /* Disabled for now, not sure this is a useful property */
4005 duk_push_int(ctx, err_code);
4006 duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_CODE, DUK_PROPDESC_FLAGS_WC);
4007#endif
4008
4009 /* Creation time error augmentation */
4010#ifdef DUK_USE_AUGMENT_ERROR_CREATE
4011 /* filename may be NULL in which case file/line is not recorded */
4012 duk_err_augment_error_create(thr, thr, filename, line, noblame_fileline); /* may throw an error */
4013#endif
4014
4015 return ret;
4016}
4017
4018DUK_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, ...) {
4019 va_list ap;
4020 duk_idx_t ret;
4021
4022 DUK_ASSERT_CTX_VALID(ctx);
4023
4024 va_start(ap, fmt);
4025 ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
4026 va_end(ap);
4027 return ret;
4028}
4029
4030#if !defined(DUK_USE_VARIADIC_MACROS)
4031DUK_EXTERNAL duk_idx_t duk_push_error_object_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) {
4032 const char *filename = duk_api_global_filename;
4033 duk_int_t line = duk_api_global_line;
4034 va_list ap;
4035 duk_idx_t ret;
4036
4037 DUK_ASSERT_CTX_VALID(ctx);
4038
4039 duk_api_global_filename = NULL;
4040 duk_api_global_line = 0;
4041 va_start(ap, fmt);
4042 ret = duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
4043 va_end(ap);
4044 return ret;
4045}
4046#endif /* DUK_USE_VARIADIC_MACROS */
4047
4048DUK_EXTERNAL void *duk_push_buffer_raw(duk_context *ctx, duk_size_t size, duk_small_uint_t flags) {
4049 duk_hthread *thr = (duk_hthread *) ctx;
4050 duk_tval *tv_slot;
4051 duk_hbuffer *h;
4052 void *buf_data;
4053
4054 DUK_ASSERT_CTX_VALID(ctx);
4055
4056 /* check stack first */
4057 if (thr->valstack_top >= thr->valstack_end) {
4058 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_PUSH_BEYOND_ALLOC_STACK);
4059 }
4060
4061 /* Check for maximum buffer length. */
4062 if (size > DUK_HBUFFER_MAX_BYTELEN) {
4063 DUK_ERROR(thr, DUK_ERR_RANGE_ERROR, DUK_STR_BUFFER_TOO_LONG);
4064 }
4065
4066 h = duk_hbuffer_alloc(thr->heap, size, flags, &buf_data);
4067 if (!h) {
4068 DUK_ERROR(thr, DUK_ERR_ALLOC_ERROR, DUK_STR_ALLOC_FAILED);
4069 }
4070
4071 tv_slot = thr->valstack_top;
4072 DUK_TVAL_SET_BUFFER(tv_slot, h);
4073 DUK_HBUFFER_INCREF(thr, h);
4074 thr->valstack_top++;
4075
4076 return (void *) buf_data;
4077}
4078
4079DUK_EXTERNAL duk_idx_t duk_push_heapptr(duk_context *ctx, void *ptr) {
4080 duk_hthread *thr = (duk_hthread *) ctx;
4081 duk_idx_t ret;
4082
4083 DUK_ASSERT_CTX_VALID(ctx);
4084
4085 ret = (duk_idx_t) (thr->valstack_top - thr->valstack_bottom);
4086
4087 if (ptr == NULL) {
4088 goto push_undefined;
4089 }
4090
4091 switch (DUK_HEAPHDR_GET_TYPE((duk_heaphdr *) ptr)) {
4092 case DUK_HTYPE_STRING:
4093 duk_push_hstring(ctx, (duk_hstring *) ptr);
4094 break;
4095 case DUK_HTYPE_OBJECT:
4096 duk_push_hobject(ctx, (duk_hobject *) ptr);
4097 break;
4098 case DUK_HTYPE_BUFFER:
4099 duk_push_hbuffer(ctx, (duk_hbuffer *) ptr);
4100 break;
4101 default:
4102 goto push_undefined;
4103 }
4104 return ret;
4105
4106 push_undefined:
4107 duk_push_undefined(ctx);
4108 return ret;
4109}
4110
4111DUK_INTERNAL duk_idx_t duk_push_object_internal(duk_context *ctx) {
4112 return duk_push_object_helper(ctx,
4113 DUK_HOBJECT_FLAG_EXTENSIBLE |
4114 DUK_HOBJECT_CLASS_AS_FLAGS(DUK_HOBJECT_CLASS_OBJECT),
4115 -1); /* no prototype */
4116}
4117
4118DUK_INTERNAL void duk_push_hstring(duk_context *ctx, duk_hstring *h) {
4119 duk_tval tv;
4120 DUK_ASSERT_CTX_VALID(ctx);
4121 DUK_ASSERT(h != NULL);
4122 DUK_TVAL_SET_STRING(&tv, h);
4123 duk_push_tval(ctx, &tv);
4124}
4125
4126DUK_INTERNAL void duk_push_hstring_stridx(duk_context *ctx, duk_small_int_t stridx) {
4127 duk_hthread *thr = (duk_hthread *) ctx;
4128 DUK_ASSERT(stridx >= 0 && stridx < DUK_HEAP_NUM_STRINGS);
4129 duk_push_hstring(ctx, DUK_HTHREAD_GET_STRING(thr, stridx));
4130}
4131
4132DUK_INTERNAL void duk_push_hobject(duk_context *ctx, duk_hobject *h) {
4133 duk_tval tv;
4134 DUK_ASSERT_CTX_VALID(ctx);
4135 DUK_ASSERT(h != NULL);
4136 DUK_TVAL_SET_OBJECT(&tv, h);
4137 duk_push_tval(ctx, &tv);
4138}
4139
4140DUK_INTERNAL void duk_push_hbuffer(duk_context *ctx, duk_hbuffer *h) {
4141 duk_tval tv;
4142 DUK_ASSERT_CTX_VALID(ctx);
4143 DUK_ASSERT(h != NULL);
4144 DUK_TVAL_SET_BUFFER(&tv, h);
4145 duk_push_tval(ctx, &tv);
4146}
4147
4148DUK_INTERNAL void duk_push_hobject_bidx(duk_context *ctx, duk_small_int_t builtin_idx) {
4149 duk_hthread *thr = (duk_hthread *) ctx;
4150 DUK_ASSERT_CTX_VALID(ctx);
4151 DUK_ASSERT(thr != NULL);
4152 DUK_ASSERT(builtin_idx >= 0 && builtin_idx < DUK_NUM_BUILTINS);
4153 DUK_ASSERT(thr->builtins[builtin_idx] != NULL);
4154 duk_push_hobject(ctx, thr->builtins[builtin_idx]);
4155}
4156
4157/*
4158 * Poppers
4159 */
4160
4161DUK_EXTERNAL void duk_pop_n(duk_context *ctx, duk_idx_t count) {
4162 duk_hthread *thr = (duk_hthread *) ctx;
4163
4164 DUK_ASSERT_CTX_VALID(ctx);
4165
4166 if (count < 0) {
4167 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_COUNT);
4168 return;
4169 }
4170
4171 DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
4172 if ((duk_size_t) (thr->valstack_top - thr->valstack_bottom) < (duk_size_t) count) {
4173 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_POP_TOO_MANY);
4174 }
4175
4176 /*
4177 * Must be very careful here, every DECREF may cause reallocation
4178 * of our valstack.
4179 */
4180
4181 /* XXX: inlined DECREF macro would be nice here: no NULL check,
4182 * refzero queueing but no refzero algorithm run (= no pointer
4183 * instability), inline code.
4184 */
4185
4186#ifdef DUK_USE_REFERENCE_COUNTING
4187 while (count > 0) {
4188 duk_tval tv_tmp;
4189 duk_tval *tv;
4190
4191 tv = --thr->valstack_top; /* tv points to element just below prev top */
4192 DUK_ASSERT(tv >= thr->valstack_bottom);
4193 DUK_TVAL_SET_TVAL(&tv_tmp, tv);
4194 DUK_TVAL_SET_UNDEFINED_UNUSED(tv);
4195 DUK_TVAL_DECREF(thr, &tv_tmp); /* side effects */
4196 count--;
4197 }
4198#else
4199 while (count > 0) {
4200 duk_tval *tv;
4201
4202 tv = --thr->valstack_top;
4203 DUK_ASSERT(tv >= thr->valstack_bottom);
4204 DUK_TVAL_SET_UNDEFINED_UNUSED(tv);
4205 count--;
4206 }
4207#endif
4208
4209 DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
4210}
4211
4212DUK_EXTERNAL void duk_pop(duk_context *ctx) {
4213 DUK_ASSERT_CTX_VALID(ctx);
4214 duk_pop_n(ctx, 1);
4215}
4216
4217DUK_EXTERNAL void duk_pop_2(duk_context *ctx) {
4218 DUK_ASSERT_CTX_VALID(ctx);
4219 duk_pop_n(ctx, 2);
4220}
4221
4222DUK_EXTERNAL void duk_pop_3(duk_context *ctx) {
4223 DUK_ASSERT_CTX_VALID(ctx);
4224 duk_pop_n(ctx, 3);
4225}
4226
4227/*
4228 * Error throwing
4229 */
4230
4231DUK_EXTERNAL void duk_throw(duk_context *ctx) {
4232 duk_hthread *thr = (duk_hthread *) ctx;
4233
4234 DUK_ASSERT(thr->valstack_bottom >= thr->valstack);
4235 DUK_ASSERT(thr->valstack_top >= thr->valstack_bottom);
4236 DUK_ASSERT(thr->valstack_end >= thr->valstack_top);
4237
4238 if (thr->valstack_top == thr->valstack_bottom) {
4239 DUK_ERROR(thr, DUK_ERR_API_ERROR, DUK_STR_INVALID_CALL_ARGS);
4240 }
4241
4242 /* Errors are augmented when they are created, not when they are
4243 * thrown or re-thrown. The current error handler, however, runs
4244 * just before an error is thrown.
4245 */
4246
4247 /* Sync so that augmentation sees up-to-date activations, NULL
4248 * thr->ptr_curr_pc so that it's not used if side effects occur
4249 * in augmentation or longjmp handling.
4250 */
4251 duk_hthread_sync_and_null_currpc(thr);
4252
4253#if defined(DUK_USE_AUGMENT_ERROR_THROW)
4254 DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (before throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
4255 duk_err_augment_error_throw(thr);
4256#endif
4257 DUK_DDD(DUK_DDDPRINT("THROW ERROR (API): %!dT (after throw augment)", (duk_tval *) duk_get_tval(ctx, -1)));
4258
4259 duk_err_setup_heap_ljstate(thr, DUK_LJ_TYPE_THROW);
4260
4261 /* thr->heap->lj.jmpbuf_ptr is checked by duk_err_longjmp() so we don't
4262 * need to check that here. If the value is NULL, a panic occurs because
4263 * we can't return.
4264 */
4265
4266 duk_err_longjmp(thr);
4267 DUK_UNREACHABLE();
4268}
4269
4270DUK_EXTERNAL void duk_fatal(duk_context *ctx, duk_errcode_t err_code, const char *err_msg) {
4271 duk_hthread *thr = (duk_hthread *) ctx;
4272
4273 DUK_ASSERT_CTX_VALID(ctx);
4274 DUK_ASSERT(thr != NULL);
4275 DUK_ASSERT(thr->heap != NULL);
4276 DUK_ASSERT(thr->heap->fatal_func != NULL);
4277
4278 DUK_D(DUK_DPRINT("fatal error occurred, code %ld, message %s",
4279 (long) err_code, (const char *) err_msg));
4280
4281 /* fatal_func should be noreturn, but noreturn declarations on function
4282 * pointers has a very spotty support apparently so it's not currently
4283 * done.
4284 */
4285 thr->heap->fatal_func(ctx, err_code, err_msg);
4286
4287 DUK_PANIC(DUK_ERR_API_ERROR, "fatal handler returned");
4288}
4289
4290DUK_EXTERNAL void duk_error_va_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, va_list ap) {
4291 DUK_ASSERT_CTX_VALID(ctx);
4292
4293 duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
4294 duk_throw(ctx);
4295}
4296
4297DUK_EXTERNAL void duk_error_raw(duk_context *ctx, duk_errcode_t err_code, const char *filename, duk_int_t line, const char *fmt, ...) {
4298 va_list ap;
4299
4300 DUK_ASSERT_CTX_VALID(ctx);
4301
4302 va_start(ap, fmt);
4303 duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
4304 va_end(ap);
4305 duk_throw(ctx);
4306}
4307
4308#if !defined(DUK_USE_VARIADIC_MACROS)
4309DUK_EXTERNAL void duk_error_stash(duk_context *ctx, duk_errcode_t err_code, const char *fmt, ...) {
4310 const char *filename;
4311 duk_int_t line;
4312 va_list ap;
4313
4314 DUK_ASSERT_CTX_VALID(ctx);
4315
4316 filename = duk_api_global_filename;
4317 line = duk_api_global_line;
4318 duk_api_global_filename = NULL;
4319 duk_api_global_line = 0;
4320
4321 va_start(ap, fmt);
4322 duk_push_error_object_va_raw(ctx, err_code, filename, line, fmt, ap);
4323 va_end(ap);
4324 duk_throw(ctx);
4325}
4326#endif /* DUK_USE_VARIADIC_MACROS */
4327
4328/*
4329 * Comparison
4330 */
4331
4332DUK_EXTERNAL duk_bool_t duk_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
4333 duk_hthread *thr = (duk_hthread *) ctx;
4334 duk_tval *tv1, *tv2;
4335
4336 DUK_ASSERT_CTX_VALID(ctx);
4337
4338 tv1 = duk_get_tval(ctx, index1);
4339 tv2 = duk_get_tval(ctx, index2);
4340 if ((tv1 == NULL) || (tv2 == NULL)) {
4341 return 0;
4342 }
4343
4344 /* Coercion may be needed, the helper handles that by pushing the
4345 * tagged values to the stack.
4346 */
4347 return duk_js_equals(thr, tv1, tv2);
4348}
4349
4350DUK_EXTERNAL duk_bool_t duk_strict_equals(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
4351 duk_tval *tv1, *tv2;
4352
4353 DUK_ASSERT_CTX_VALID(ctx);
4354
4355 tv1 = duk_get_tval(ctx, index1);
4356 tv2 = duk_get_tval(ctx, index2);
4357 if ((tv1 == NULL) || (tv2 == NULL)) {
4358 return 0;
4359 }
4360
4361 /* No coercions or other side effects, so safe */
4362 return duk_js_strict_equals(tv1, tv2);
4363}
4364
4365/*
4366 * instanceof
4367 */
4368
4369DUK_EXTERNAL duk_bool_t duk_instanceof(duk_context *ctx, duk_idx_t index1, duk_idx_t index2) {
4370 duk_tval *tv1, *tv2;
4371
4372 DUK_ASSERT_CTX_VALID(ctx);
4373
4374 /* Index validation is strict, which differs from duk_equals().
4375 * The strict behavior mimics how instanceof itself works, e.g.
4376 * it is a TypeError if rval is not a -callable- object. It would
4377 * be somewhat inconsistent if rval would be allowed to be
4378 * non-existent without a TypeError.
4379 */
4380 tv1 = duk_require_tval(ctx, index1);
4381 DUK_ASSERT(tv1 != NULL);
4382 tv2 = duk_require_tval(ctx, index2);
4383 DUK_ASSERT(tv2 != NULL);
4384
4385 return duk_js_instanceof((duk_hthread *) ctx, tv1, tv2);
4386}
4387
4388/*
4389 * Lightfunc
4390 */
4391
4392DUK_INTERNAL void duk_push_lightfunc_name(duk_context *ctx, duk_tval *tv) {
4393 duk_c_function func;
4394
4395 DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
4396
4397 /* Lightfunc name, includes Duktape/C native function pointer, which
4398 * can often be used to locate the function from a symbol table.
4399 * The name also includes the 16-bit duk_tval flags field because it
4400 * includes the magic value. Because a single native function often
4401 * provides different functionality depending on the magic value, it
4402 * seems reasonably to include it in the name.
4403 *
4404 * On the other hand, a complicated name increases string table
4405 * pressure in low memory environments (but only when function name
4406 * is accessed).
4407 */
4408
4409 func = DUK_TVAL_GET_LIGHTFUNC_FUNCPTR(tv);
4410 duk_push_sprintf(ctx, "light_");
4411 duk_push_string_funcptr(ctx, (duk_uint8_t *) &func, sizeof(func));
4412 duk_push_sprintf(ctx, "_%04x", (unsigned int) DUK_TVAL_GET_LIGHTFUNC_FLAGS(tv));
4413 duk_concat(ctx, 3);
4414}
4415
4416DUK_INTERNAL void duk_push_lightfunc_tostring(duk_context *ctx, duk_tval *tv) {
4417 DUK_ASSERT(DUK_TVAL_IS_LIGHTFUNC(tv));
4418
4419 duk_push_string(ctx, "function ");
4420 duk_push_lightfunc_name(ctx, tv);
4421 duk_push_string(ctx, "() {/* light */}");
4422 duk_concat(ctx, 3);
4423}
4424
4425/*
4426 * Function pointers
4427 *
4428 * Printing function pointers is non-portable, so we do that by hex printing
4429 * bytes from memory.
4430 */
4431
4432DUK_INTERNAL void duk_push_string_funcptr(duk_context *ctx, duk_uint8_t *ptr, duk_size_t sz) {
4433 duk_uint8_t buf[32 * 2];
4434 duk_uint8_t *p, *q;
4435 duk_small_uint_t i;
4436 duk_small_uint_t t;
4437
4438 DUK_ASSERT(sz <= 32); /* sanity limit for function pointer size */
4439
4440 p = buf;
4441#if defined(DUK_USE_INTEGER_LE)
4442 q = ptr + sz;
4443#else
4444 q = ptr;
4445#endif
4446 for (i = 0; i < sz; i++) {
4447#if defined(DUK_USE_INTEGER_LE)
4448 t = *(--q);
4449#else
4450 t = *(q++);
4451#endif
4452 *p++ = duk_lc_digits[t >> 4];
4453 *p++ = duk_lc_digits[t & 0x0f];
4454 }
4455
4456 duk_push_lstring(ctx, (const char *) buf, sz * 2);
4457}
4458
4459#undef DUK__CHECK_SPACE