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