2 * Ecmascript specification algorithm and conversion helpers.
4 * These helpers encapsulate the primitive Ecmascript operation
5 * semantics, and are used by the bytecode executor and the API
6 * (among other places). Note that some primitives are only
7 * implemented as part of the API and have no "internal" helper.
8 * (This is the case when an internal helper would not really be
9 * useful; e.g. the operation is rare, uses value stack heavily,
12 * The operation arguments depend on what is required to implement
15 * - If an operation is simple and stateless, and has no side
16 * effects, it won't take an duk_hthread argument and its
17 * arguments may be duk_tval pointers (which are safe as long
18 * as no side effects take place).
20 * - If complex coercions are required (e.g. a "ToNumber" coercion)
21 * or errors may be thrown, the operation takes an duk_hthread
22 * argument. This also implies that the operation may have
23 * arbitrary side effects, invalidating any duk_tval pointers.
25 * - For operations with potential side effects, arguments can be
26 * taken in several ways:
28 * a) as duk_tval pointers, which makes sense if the "common case"
29 * can be resolved without side effects (e.g. coercion); the
30 * arguments are pushed to the valstack for coercion if
33 * b) as duk_tval values
35 * c) implicitly on value stack top
37 * d) as indices to the value stack
41 * - Argument styles may not be the most sensible in every case now.
43 * - In-place coercions might be useful for several operations, if
44 * in-place coercion is OK for the bytecode executor and the API.
47 #include "duk_internal.h"
50 * [[DefaultValue]] (E5 Section 8.12.8)
52 * ==> implemented in the API.
56 * ToPrimitive() (E5 Section 9.1)
58 * ==> implemented in the API.
62 * ToBoolean() (E5 Section 9.2)
65 DUK_INTERNAL duk_bool_t
duk_js_toboolean(duk_tval
*tv
) {
66 switch (DUK_TVAL_GET_TAG(tv
)) {
67 case DUK_TAG_UNDEFINED
:
71 return DUK_TVAL_GET_BOOLEAN(tv
);
72 case DUK_TAG_STRING
: {
73 duk_hstring
*h
= DUK_TVAL_GET_STRING(tv
);
74 DUK_ASSERT(h
!= NULL
);
75 return (DUK_HSTRING_GET_BYTELEN(h
) > 0 ? 1 : 0);
77 case DUK_TAG_OBJECT
: {
80 case DUK_TAG_BUFFER
: {
81 /* mimic semantics for strings */
82 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
83 DUK_ASSERT(h
!= NULL
);
84 return (DUK_HBUFFER_GET_SIZE(h
) > 0 ? 1 : 0);
86 case DUK_TAG_POINTER
: {
87 void *p
= DUK_TVAL_GET_POINTER(tv
);
88 return (p
!= NULL
? 1 : 0);
90 case DUK_TAG_LIGHTFUNC
: {
93 #if defined(DUK_USE_FASTINT)
95 if (DUK_TVAL_GET_FASTINT(tv
) != 0) {
105 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
106 DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv
));
107 d
= DUK_TVAL_GET_DOUBLE(tv
);
108 c
= DUK_FPCLASSIFY((double) d
);
109 if (c
== DUK_FP_ZERO
|| c
== DUK_FP_NAN
) {
120 * ToNumber() (E5 Section 9.3)
122 * Value to convert must be on stack top, and is popped before exit.
124 * See: http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
125 * http://www.cs.indiana.edu/~burger/fp/index.html
127 * Notes on the conversion:
129 * - There are specific requirements on the accuracy of the conversion
130 * through a "Mathematical Value" (MV), so this conversion is not
133 * - Quick rejects (e.g. based on first char) are difficult because
134 * the grammar allows leading and trailing white space.
136 * - Quick reject based on string length is difficult even after
137 * accounting for white space; there may be arbitrarily many
140 * - Standard grammar allows decimal values ("123"), hex values
141 * ("0x123") and infinities
143 * - Unlike source code literals, ToNumber() coerces empty strings
144 * and strings with only whitespace to zero (not NaN).
147 /* E5 Section 9.3.1 */
148 DUK_LOCAL duk_double_t
duk__tonumber_string_raw(duk_hthread
*thr
) {
149 duk_context
*ctx
= (duk_context
*) thr
;
150 duk_small_uint_t s2n_flags
;
153 /* Quite lenient, e.g. allow empty as zero, but don't allow trailing
156 s2n_flags
= DUK_S2N_FLAG_TRIM_WHITE
|
157 DUK_S2N_FLAG_ALLOW_EXP
|
158 DUK_S2N_FLAG_ALLOW_PLUS
|
159 DUK_S2N_FLAG_ALLOW_MINUS
|
160 DUK_S2N_FLAG_ALLOW_INF
|
161 DUK_S2N_FLAG_ALLOW_FRAC
|
162 DUK_S2N_FLAG_ALLOW_NAKED_FRAC
|
163 DUK_S2N_FLAG_ALLOW_EMPTY_FRAC
|
164 DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO
|
165 DUK_S2N_FLAG_ALLOW_LEADING_ZERO
|
166 DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT
;
168 duk_numconv_parse(ctx
, 10 /*radix*/, s2n_flags
);
169 d
= duk_get_number(ctx
, -1);
175 DUK_INTERNAL duk_double_t
duk_js_tonumber(duk_hthread
*thr
, duk_tval
*tv
) {
176 duk_context
*ctx
= (duk_hthread
*) thr
;
178 DUK_ASSERT(thr
!= NULL
);
179 DUK_ASSERT(tv
!= NULL
);
181 switch (DUK_TVAL_GET_TAG(tv
)) {
182 case DUK_TAG_UNDEFINED
: {
183 /* return a specific NaN (although not strictly necessary) */
185 DUK_DBLUNION_SET_NAN(&du
);
186 DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du
));
193 case DUK_TAG_BOOLEAN
: {
194 if (DUK_TVAL_IS_BOOLEAN_TRUE(tv
)) {
199 case DUK_TAG_STRING
: {
200 duk_hstring
*h
= DUK_TVAL_GET_STRING(tv
);
201 duk_push_hstring(ctx
, h
);
202 return duk__tonumber_string_raw(thr
);
204 case DUK_TAG_OBJECT
: {
205 /* Note: ToPrimitive(object,hint) == [[DefaultValue]](object,hint),
206 * so use [[DefaultValue]] directly.
209 duk_push_tval(ctx
, tv
);
210 duk_to_defaultvalue(ctx
, -1, DUK_HINT_NUMBER
); /* 'tv' becomes invalid */
212 /* recursive call for a primitive value (guaranteed not to cause second
215 d
= duk_js_tonumber(thr
, duk_require_tval(ctx
, -1));
220 case DUK_TAG_BUFFER
: {
221 /* Coerce like a string. This makes sense because addition also treats
222 * buffers like strings.
224 duk_hbuffer
*h
= DUK_TVAL_GET_BUFFER(tv
);
225 duk_push_hbuffer(ctx
, h
);
226 duk_to_string(ctx
, -1); /* XXX: expensive, but numconv now expects to see a string */
227 return duk__tonumber_string_raw(thr
);
229 case DUK_TAG_POINTER
: {
230 /* Coerce like boolean */
231 void *p
= DUK_TVAL_GET_POINTER(tv
);
232 return (p
!= NULL
? 1.0 : 0.0);
234 case DUK_TAG_LIGHTFUNC
: {
235 /* +(function(){}) -> NaN */
236 return DUK_DOUBLE_NAN
;
238 #if defined(DUK_USE_FASTINT)
239 case DUK_TAG_FASTINT
:
240 return (duk_double_t
) DUK_TVAL_GET_FASTINT(tv
);
244 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv
));
245 DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv
));
246 return DUK_TVAL_GET_DOUBLE(tv
);
254 * ToInteger() (E5 Section 9.4)
257 /* exposed, used by e.g. duk_bi_date.c */
258 DUK_INTERNAL duk_double_t
duk_js_tointeger_number(duk_double_t x
) {
259 duk_small_int_t c
= (duk_small_int_t
) DUK_FPCLASSIFY(x
);
261 if (c
== DUK_FP_NAN
) {
263 } else if (c
== DUK_FP_ZERO
|| c
== DUK_FP_INFINITE
) {
264 /* XXX: FP_ZERO check can be removed, the else clause handles it
265 * correctly (preserving sign).
269 duk_small_int_t s
= (duk_small_int_t
) DUK_SIGNBIT(x
);
270 x
= DUK_FLOOR(DUK_FABS(x
)); /* truncate towards zero */
278 DUK_INTERNAL duk_double_t
duk_js_tointeger(duk_hthread
*thr
, duk_tval
*tv
) {
280 duk_double_t d
= duk_js_tonumber(thr
, tv
); /* invalidates tv */
281 return duk_js_tointeger_number(d
);
285 * ToInt32(), ToUint32(), ToUint16() (E5 Sections 9.5, 9.6, 9.7)
288 /* combined algorithm matching E5 Sections 9.5 and 9.6 */
289 DUK_LOCAL duk_double_t
duk__toint32_touint32_helper(duk_double_t x
, duk_bool_t is_toint32
) {
290 duk_small_int_t c
= (duk_small_int_t
) DUK_FPCLASSIFY(x
);
293 if (c
== DUK_FP_NAN
|| c
== DUK_FP_ZERO
|| c
== DUK_FP_INFINITE
) {
298 /* x = sign(x) * floor(abs(x)), i.e. truncate towards zero, keep sign */
299 s
= (duk_small_int_t
) DUK_SIGNBIT(x
);
300 x
= DUK_FLOOR(DUK_FABS(x
));
305 /* NOTE: fmod(x) result sign is same as sign of x, which
306 * differs from what Javascript wants (see Section 9.6).
309 x
= DUK_FMOD(x
, DUK_DOUBLE_2TO32
); /* -> x in ]-2**32, 2**32[ */
312 x
+= DUK_DOUBLE_2TO32
;
314 /* -> x in [0, 2**32[ */
317 if (x
>= DUK_DOUBLE_2TO31
) {
318 /* x in [2**31, 2**32[ */
320 x
-= DUK_DOUBLE_2TO32
; /* -> x in [-2**31,2**31[ */
327 DUK_INTERNAL duk_int32_t
duk_js_toint32(duk_hthread
*thr
, duk_tval
*tv
) {
330 #if defined(DUK_USE_FASTINT)
331 if (DUK_TVAL_IS_FASTINT(tv
)) {
332 return DUK_TVAL_GET_FASTINT_I32(tv
);
336 d
= duk_js_tonumber(thr
, tv
); /* invalidates tv */
337 d
= duk__toint32_touint32_helper(d
, 1);
338 DUK_ASSERT(DUK_FPCLASSIFY(d
) == DUK_FP_ZERO
|| DUK_FPCLASSIFY(d
) == DUK_FP_NORMAL
);
339 DUK_ASSERT(d
>= -2147483648.0 && d
<= 2147483647.0); /* [-0x80000000,0x7fffffff] */
340 DUK_ASSERT(d
== ((duk_double_t
) ((duk_int32_t
) d
))); /* whole, won't clip */
341 return (duk_int32_t
) d
;
345 DUK_INTERNAL duk_uint32_t
duk_js_touint32(duk_hthread
*thr
, duk_tval
*tv
) {
348 #if defined(DUK_USE_FASTINT)
349 if (DUK_TVAL_IS_FASTINT(tv
)) {
350 return DUK_TVAL_GET_FASTINT_U32(tv
);
354 d
= duk_js_tonumber(thr
, tv
); /* invalidates tv */
355 d
= duk__toint32_touint32_helper(d
, 0);
356 DUK_ASSERT(DUK_FPCLASSIFY(d
) == DUK_FP_ZERO
|| DUK_FPCLASSIFY(d
) == DUK_FP_NORMAL
);
357 DUK_ASSERT(d
>= 0.0 && d
<= 4294967295.0); /* [0x00000000, 0xffffffff] */
358 DUK_ASSERT(d
== ((duk_double_t
) ((duk_uint32_t
) d
))); /* whole, won't clip */
359 return (duk_uint32_t
) d
;
363 DUK_INTERNAL duk_uint16_t
duk_js_touint16(duk_hthread
*thr
, duk_tval
*tv
) {
364 /* should be a safe way to compute this */
365 return (duk_uint16_t
) (duk_js_touint32(thr
, tv
) & 0x0000ffffU
);
369 * ToString() (E5 Section 9.8)
371 * ==> implemented in the API.
375 * ToObject() (E5 Section 9.9)
377 * ==> implemented in the API.
381 * CheckObjectCoercible() (E5 Section 9.10)
383 * Note: no API equivalent now.
387 DUK_INTERNAL
void duk_js_checkobjectcoercible(duk_hthread
*thr
, duk_tval
*tv_x
) {
388 duk_small_uint_t tag
= DUK_TVAL_GET_TAG(tv_x
);
390 /* Note: this must match ToObject() behavior */
392 if (tag
== DUK_TAG_UNDEFINED
||
393 tag
== DUK_TAG_NULL
||
394 tag
== DUK_TAG_POINTER
||
395 tag
== DUK_TAG_BUFFER
) {
396 DUK_ERROR_TYPE(thr
, "not object coercible");
402 * IsCallable() (E5 Section 9.11)
404 * XXX: API equivalent is a separate implementation now, and this has
405 * currently no callers.
409 DUK_INTERNAL duk_bool_t
duk_js_iscallable(duk_tval
*tv_x
) {
412 if (!DUK_TVAL_IS_OBJECT(tv_x
)) {
415 obj
= DUK_TVAL_GET_OBJECT(tv_x
);
416 DUK_ASSERT(obj
!= NULL
);
418 return DUK_HOBJECT_IS_CALLABLE(obj
);
423 * Loose equality, strict equality, and SameValue (E5 Sections 11.9.1, 11.9.4,
424 * 9.12). These have much in common so they can share some helpers.
428 * - Current implementation (and spec definition) has recursion; this should
429 * be fixed if possible.
431 * - String-to-number coercion should be possible without going through the
432 * value stack (and be more compact) if a shared helper is invoked.
435 /* Note that this is the same operation for strict and loose equality:
436 * - E5 Section 11.9.3, step 1.c (loose)
437 * - E5 Section 11.9.6, step 4 (strict)
440 DUK_LOCAL duk_bool_t
duk__js_equals_number(duk_double_t x
, duk_double_t y
) {
441 #if defined(DUK_USE_PARANOID_MATH)
442 /* Straightforward algorithm, makes fewer compiler assumptions. */
443 duk_small_int_t cx
= (duk_small_int_t
) DUK_FPCLASSIFY(x
);
444 duk_small_int_t cy
= (duk_small_int_t
) DUK_FPCLASSIFY(y
);
445 if (cx
== DUK_FP_NAN
|| cy
== DUK_FP_NAN
) {
448 if (cx
== DUK_FP_ZERO
&& cy
== DUK_FP_ZERO
) {
455 #else /* DUK_USE_PARANOID_MATH */
456 /* Better equivalent algorithm. If the compiler is compliant, C and
457 * Ecmascript semantics are identical for this particular comparison.
458 * In particular, NaNs must never compare equal and zeroes must compare
459 * equal regardless of sign. Could also use a macro, but this inlines
460 * already nicely (no difference on gcc, for instance).
463 /* IEEE requires that NaNs compare false */
464 DUK_ASSERT(DUK_FPCLASSIFY(x
) != DUK_FP_NAN
);
465 DUK_ASSERT(DUK_FPCLASSIFY(y
) != DUK_FP_NAN
);
468 /* IEEE requires that zeros compare the same regardless
469 * of their signed, so if both x and y are zeroes, they
472 DUK_ASSERT(!(DUK_FPCLASSIFY(x
) == DUK_FP_ZERO
&& DUK_FPCLASSIFY(y
) == DUK_FP_ZERO
));
475 #endif /* DUK_USE_PARANOID_MATH */
478 DUK_LOCAL duk_bool_t
duk__js_samevalue_number(duk_double_t x
, duk_double_t y
) {
479 #if defined(DUK_USE_PARANOID_MATH)
480 duk_small_int_t cx
= (duk_small_int_t
) DUK_FPCLASSIFY(x
);
481 duk_small_int_t cy
= (duk_small_int_t
) DUK_FPCLASSIFY(y
);
483 if (cx
== DUK_FP_NAN
&& cy
== DUK_FP_NAN
) {
484 /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
487 if (cx
== DUK_FP_ZERO
&& cy
== DUK_FP_ZERO
) {
488 /* Note: cannot assume that a non-zero return value of signbit() would
489 * always be the same -- hence cannot (portably) use something like:
491 * signbit(x) == signbit(y)
493 duk_small_int_t sx
= (DUK_SIGNBIT(x
) ? 1 : 0);
494 duk_small_int_t sy
= (DUK_SIGNBIT(y
) ? 1 : 0);
498 /* normal comparison; known:
499 * - both x and y are not NaNs (but one of them can be)
500 * - both x and y are not zero (but one of them can be)
501 * - x and y may be denormal or infinite
505 #else /* DUK_USE_PARANOID_MATH */
506 duk_small_int_t cx
= (duk_small_int_t
) DUK_FPCLASSIFY(x
);
507 duk_small_int_t cy
= (duk_small_int_t
) DUK_FPCLASSIFY(y
);
510 /* IEEE requires that NaNs compare false */
511 DUK_ASSERT(DUK_FPCLASSIFY(x
) != DUK_FP_NAN
);
512 DUK_ASSERT(DUK_FPCLASSIFY(y
) != DUK_FP_NAN
);
514 /* Using classification has smaller footprint than direct comparison. */
515 if (DUK_UNLIKELY(cx
== DUK_FP_ZERO
&& cy
== DUK_FP_ZERO
)) {
516 /* Note: cannot assume that a non-zero return value of signbit() would
517 * always be the same -- hence cannot (portably) use something like:
519 * signbit(x) == signbit(y)
521 duk_small_int_t sx
= (DUK_SIGNBIT(x
) ? 1 : 0);
522 duk_small_int_t sy
= (DUK_SIGNBIT(y
) ? 1 : 0);
527 /* IEEE requires that zeros compare the same regardless
528 * of their signed, so if both x and y are zeroes, they
531 DUK_ASSERT(!(DUK_FPCLASSIFY(x
) == DUK_FP_ZERO
&& DUK_FPCLASSIFY(y
) == DUK_FP_ZERO
));
533 /* Difference to non-strict/strict comparison is that NaNs compare
534 * equal and signed zero signs matter.
536 if (DUK_UNLIKELY(cx
== DUK_FP_NAN
&& cy
== DUK_FP_NAN
)) {
537 /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
542 #endif /* DUK_USE_PARANOID_MATH */
545 DUK_INTERNAL duk_bool_t
duk_js_equals_helper(duk_hthread
*thr
, duk_tval
*tv_x
, duk_tval
*tv_y
, duk_small_int_t flags
) {
546 duk_context
*ctx
= (duk_context
*) thr
;
549 /* If flags != 0 (strict or SameValue), thr can be NULL. For loose
550 * equals comparison it must be != NULL.
552 DUK_ASSERT(flags
!= 0 || thr
!= NULL
);
557 * Note: since number values have no explicit tag in the 8-byte
558 * representation, need the awkward if + switch.
561 #if defined(DUK_USE_FASTINT)
562 if (DUK_TVAL_IS_FASTINT(tv_x
) && DUK_TVAL_IS_FASTINT(tv_y
)) {
563 if (DUK_TVAL_GET_FASTINT(tv_x
) == DUK_TVAL_GET_FASTINT(tv_y
)) {
571 if (DUK_TVAL_IS_NUMBER(tv_x
) && DUK_TVAL_IS_NUMBER(tv_y
)) {
572 /* Catches both doubles and cases where only one argument is a fastint */
573 if (DUK_UNLIKELY((flags
& DUK_EQUALS_FLAG_SAMEVALUE
) != 0)) {
575 return duk__js_samevalue_number(DUK_TVAL_GET_NUMBER(tv_x
),
576 DUK_TVAL_GET_NUMBER(tv_y
));
578 /* equals and strict equals */
579 return duk__js_equals_number(DUK_TVAL_GET_NUMBER(tv_x
),
580 DUK_TVAL_GET_NUMBER(tv_y
));
582 } else if (DUK_TVAL_GET_TAG(tv_x
) == DUK_TVAL_GET_TAG(tv_y
)) {
583 switch (DUK_TVAL_GET_TAG(tv_x
)) {
584 case DUK_TAG_UNDEFINED
:
588 case DUK_TAG_BOOLEAN
: {
589 return DUK_TVAL_GET_BOOLEAN(tv_x
) == DUK_TVAL_GET_BOOLEAN(tv_y
);
591 case DUK_TAG_POINTER
: {
592 return DUK_TVAL_GET_POINTER(tv_x
) == DUK_TVAL_GET_POINTER(tv_y
);
595 case DUK_TAG_OBJECT
: {
596 /* heap pointer comparison suffices */
597 return DUK_TVAL_GET_HEAPHDR(tv_x
) == DUK_TVAL_GET_HEAPHDR(tv_y
);
599 case DUK_TAG_BUFFER
: {
600 if ((flags
& (DUK_EQUALS_FLAG_STRICT
| DUK_EQUALS_FLAG_SAMEVALUE
)) != 0) {
601 /* heap pointer comparison suffices */
602 return DUK_TVAL_GET_HEAPHDR(tv_x
) == DUK_TVAL_GET_HEAPHDR(tv_y
);
604 /* non-strict equality for buffers compares contents */
605 duk_hbuffer
*h_x
= DUK_TVAL_GET_BUFFER(tv_x
);
606 duk_hbuffer
*h_y
= DUK_TVAL_GET_BUFFER(tv_y
);
607 duk_size_t len_x
= DUK_HBUFFER_GET_SIZE(h_x
);
608 duk_size_t len_y
= DUK_HBUFFER_GET_SIZE(h_y
);
611 if (len_x
!= len_y
) {
614 buf_x
= (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_x
);
615 buf_y
= (void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_y
);
616 /* if len_x == len_y == 0, buf_x and/or buf_y may
617 * be NULL, but that's OK.
619 DUK_ASSERT(len_x
== len_y
);
620 DUK_ASSERT(len_x
== 0 || buf_x
!= NULL
);
621 DUK_ASSERT(len_y
== 0 || buf_y
!= NULL
);
622 return (DUK_MEMCMP((const void *) buf_x
, (const void *) buf_y
, (size_t) len_x
) == 0) ? 1 : 0;
625 case DUK_TAG_LIGHTFUNC
: {
626 /* At least 'magic' has a significant impact on function
629 duk_small_uint_t lf_flags_x
;
630 duk_small_uint_t lf_flags_y
;
631 duk_c_function func_x
;
632 duk_c_function func_y
;
634 DUK_TVAL_GET_LIGHTFUNC(tv_x
, func_x
, lf_flags_x
);
635 DUK_TVAL_GET_LIGHTFUNC(tv_y
, func_y
, lf_flags_y
);
636 return ((func_x
== func_y
) && (lf_flags_x
== lf_flags_y
)) ? 1 : 0;
638 #if defined(DUK_USE_FASTINT)
639 case DUK_TAG_FASTINT
:
642 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x
));
643 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_y
));
644 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x
));
645 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_y
));
652 if ((flags
& (DUK_EQUALS_FLAG_STRICT
| DUK_EQUALS_FLAG_SAMEVALUE
)) != 0) {
656 DUK_ASSERT(flags
== 0); /* non-strict equality from here on */
659 * Types are different; various cases for non-strict comparison
661 * Since comparison is symmetric, we use a "swap trick" to reduce
665 /* Undefined/null are considered equal (e.g. "null == undefined" -> true). */
666 if ((DUK_TVAL_IS_UNDEFINED(tv_x
) && DUK_TVAL_IS_NULL(tv_y
)) ||
667 (DUK_TVAL_IS_NULL(tv_x
) && DUK_TVAL_IS_UNDEFINED(tv_y
))) {
671 /* Number/string-or-buffer -> coerce string to number (e.g. "'1.5' == 1.5" -> true). */
672 if (DUK_TVAL_IS_NUMBER(tv_x
) && (DUK_TVAL_IS_STRING(tv_y
) || DUK_TVAL_IS_BUFFER(tv_y
))) {
673 /* the next 'if' is guaranteed to match after swap */
678 if ((DUK_TVAL_IS_STRING(tv_x
) || DUK_TVAL_IS_BUFFER(tv_x
)) && DUK_TVAL_IS_NUMBER(tv_y
)) {
679 /* XXX: this is possible without resorting to the value stack */
681 d2
= DUK_TVAL_GET_NUMBER(tv_y
);
682 duk_push_tval(ctx
, tv_x
);
683 duk_to_string(ctx
, -1); /* buffer values are coerced first to string here */
684 duk_to_number(ctx
, -1);
685 d1
= duk_require_number(ctx
, -1);
687 return duk__js_equals_number(d1
, d2
);
690 /* Buffer/string -> compare contents. */
691 if (DUK_TVAL_IS_BUFFER(tv_x
) && DUK_TVAL_IS_STRING(tv_y
)) {
696 if (DUK_TVAL_IS_STRING(tv_x
) && DUK_TVAL_IS_BUFFER(tv_y
)) {
697 duk_hstring
*h_x
= DUK_TVAL_GET_STRING(tv_x
);
698 duk_hbuffer
*h_y
= DUK_TVAL_GET_BUFFER(tv_y
);
699 duk_size_t len_x
= DUK_HSTRING_GET_BYTELEN(h_x
);
700 duk_size_t len_y
= DUK_HBUFFER_GET_SIZE(h_y
);
703 if (len_x
!= len_y
) {
706 buf_x
= (const void *) DUK_HSTRING_GET_DATA(h_x
);
707 buf_y
= (const void *) DUK_HBUFFER_GET_DATA_PTR(thr
->heap
, h_y
);
708 /* if len_x == len_y == 0, buf_x and/or buf_y may
709 * be NULL, but that's OK.
711 DUK_ASSERT(len_x
== len_y
);
712 DUK_ASSERT(len_x
== 0 || buf_x
!= NULL
);
713 DUK_ASSERT(len_y
== 0 || buf_y
!= NULL
);
714 return (DUK_MEMCMP((const void *) buf_x
, (const void *) buf_y
, (size_t) len_x
) == 0) ? 1 : 0;
717 /* Boolean/any -> coerce boolean to number and try again. If boolean is
718 * compared to a pointer, the final comparison after coercion now always
719 * yields false (as pointer vs. number compares to false), but this is
722 if (DUK_TVAL_IS_BOOLEAN(tv_x
)) {
727 if (DUK_TVAL_IS_BOOLEAN(tv_y
)) {
728 /* ToNumber(bool) is +1.0 or 0.0. Tagged boolean value is always 0 or 1. */
730 DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y
) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y
) == 1);
731 duk_push_tval(ctx
, tv_x
);
732 duk_push_int(ctx
, DUK_TVAL_GET_BOOLEAN(tv_y
));
733 rc
= duk_js_equals_helper(thr
,
734 DUK_GET_TVAL_NEGIDX(ctx
, -2),
735 DUK_GET_TVAL_NEGIDX(ctx
, -1),
736 0 /*flags:nonstrict*/);
741 /* String-number-buffer/object -> coerce object to primitive (apparently without hint), then try again. */
742 if ((DUK_TVAL_IS_STRING(tv_x
) || DUK_TVAL_IS_NUMBER(tv_x
) || DUK_TVAL_IS_BUFFER(tv_x
)) &&
743 DUK_TVAL_IS_OBJECT(tv_y
)) {
748 if (DUK_TVAL_IS_OBJECT(tv_x
) &&
749 (DUK_TVAL_IS_STRING(tv_y
) || DUK_TVAL_IS_NUMBER(tv_y
) || DUK_TVAL_IS_BUFFER(tv_y
))) {
751 duk_push_tval(ctx
, tv_x
);
752 duk_push_tval(ctx
, tv_y
);
753 duk_to_primitive(ctx
, -2, DUK_HINT_NONE
); /* apparently no hint? */
754 rc
= duk_js_equals_helper(thr
,
755 DUK_GET_TVAL_NEGIDX(ctx
, -2),
756 DUK_GET_TVAL_NEGIDX(ctx
, -1),
757 0 /*flags:nonstrict*/);
762 /* Nothing worked -> not equal. */
767 * Comparisons (x >= y, x > y, x <= y, x < y)
769 * E5 Section 11.8.5: implement 'x < y' and then use negate and eval_left_first
770 * flags to get the rest.
773 /* XXX: this should probably just operate on the stack top, because it
774 * needs to push stuff on the stack anyway...
777 DUK_INTERNAL duk_small_int_t
duk_js_data_compare(const duk_uint8_t
*buf1
, const duk_uint8_t
*buf2
, duk_size_t len1
, duk_size_t len2
) {
778 duk_size_t prefix_len
;
781 prefix_len
= (len1
<= len2
? len1
: len2
);
783 /* DUK_MEMCMP() is guaranteed to return zero (equal) for zero length
784 * inputs so no zero length check is needed.
786 rc
= DUK_MEMCMP((const void *) buf1
,
788 (size_t) prefix_len
);
796 /* prefix matches, lengths matter now */
798 /* e.g. "x" < "xx" */
800 } else if (len1
> len2
) {
807 DUK_INTERNAL duk_small_int_t
duk_js_string_compare(duk_hstring
*h1
, duk_hstring
*h2
) {
809 * String comparison (E5 Section 11.8.5, step 4), which
810 * needs to compare codepoint by codepoint.
812 * However, UTF-8 allows us to use strcmp directly: the shared
813 * prefix will be encoded identically (UTF-8 has unique encoding)
814 * and the first differing character can be compared with a simple
815 * unsigned byte comparison (which strcmp does).
817 * This will not work properly for non-xutf-8 strings, but this
818 * is not an issue for compliance.
821 DUK_ASSERT(h1
!= NULL
);
822 DUK_ASSERT(h2
!= NULL
);
824 return duk_js_data_compare((const duk_uint8_t
*) DUK_HSTRING_GET_DATA(h1
),
825 (const duk_uint8_t
*) DUK_HSTRING_GET_DATA(h2
),
826 (duk_size_t
) DUK_HSTRING_GET_BYTELEN(h1
),
827 (duk_size_t
) DUK_HSTRING_GET_BYTELEN(h2
));
831 DUK_INTERNAL duk_small_int_t
duk_js_buffer_compare(duk_heap
*heap
, duk_hbuffer
*h1
, duk_hbuffer
*h2
) {
832 /* Similar to String comparison. */
834 DUK_ASSERT(h1
!= NULL
);
835 DUK_ASSERT(h2
!= NULL
);
838 return duk_js_data_compare((const duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(heap
, h1
),
839 (const duk_uint8_t
*) DUK_HBUFFER_GET_DATA_PTR(heap
, h2
),
840 (duk_size_t
) DUK_HBUFFER_GET_SIZE(h1
),
841 (duk_size_t
) DUK_HBUFFER_GET_SIZE(h2
));
845 DUK_INTERNAL duk_bool_t
duk_js_compare_helper(duk_hthread
*thr
, duk_tval
*tv_x
, duk_tval
*tv_y
, duk_small_int_t flags
) {
846 duk_context
*ctx
= (duk_context
*) thr
;
848 duk_small_int_t c1
, c2
;
849 duk_small_int_t s1
, s2
;
853 /* Fast path for fastints */
854 #if defined(DUK_USE_FASTINT)
855 if (DUK_TVAL_IS_FASTINT(tv_x
) && DUK_TVAL_IS_FASTINT(tv_y
)) {
856 duk_int64_t v1
= DUK_TVAL_GET_FASTINT(tv_x
);
857 duk_int64_t v2
= DUK_TVAL_GET_FASTINT(tv_y
);
864 if (flags
& DUK_COMPARE_FLAG_NEGATE
) {
869 #endif /* DUK_USE_FASTINT */
871 /* Fast path for numbers (one of which may be a fastint) */
872 #if 1 /* XXX: make fast paths optional for size minimization? */
873 if (DUK_TVAL_IS_NUMBER(tv_x
) && DUK_TVAL_IS_NUMBER(tv_y
)) {
874 d1
= DUK_TVAL_GET_NUMBER(tv_x
);
875 d2
= DUK_TVAL_GET_NUMBER(tv_y
);
876 c1
= DUK_FPCLASSIFY(d1
);
877 c2
= DUK_FPCLASSIFY(d2
);
879 if (c1
== DUK_FP_NORMAL
&& c2
== DUK_FP_NORMAL
) {
880 /* XXX: this is a very narrow check, and doesn't cover
881 * zeroes, subnormals, infinities, which compare normally.
890 if (flags
& DUK_COMPARE_FLAG_NEGATE
) {
900 duk_push_tval(ctx
, tv_x
);
901 duk_push_tval(ctx
, tv_y
);
903 if (flags
& DUK_COMPARE_FLAG_EVAL_LEFT_FIRST
) {
904 duk_to_primitive(ctx
, -2, DUK_HINT_NUMBER
);
905 duk_to_primitive(ctx
, -1, DUK_HINT_NUMBER
);
907 duk_to_primitive(ctx
, -1, DUK_HINT_NUMBER
);
908 duk_to_primitive(ctx
, -2, DUK_HINT_NUMBER
);
911 /* Note: reuse variables */
912 tv_x
= DUK_GET_TVAL_NEGIDX(ctx
, -2);
913 tv_y
= DUK_GET_TVAL_NEGIDX(ctx
, -1);
915 if (DUK_TVAL_IS_STRING(tv_x
) && DUK_TVAL_IS_STRING(tv_y
)) {
916 duk_hstring
*h1
= DUK_TVAL_GET_STRING(tv_x
);
917 duk_hstring
*h2
= DUK_TVAL_GET_STRING(tv_y
);
918 DUK_ASSERT(h1
!= NULL
);
919 DUK_ASSERT(h2
!= NULL
);
921 rc
= duk_js_string_compare(h1
, h2
);
928 /* Ordering should not matter (E5 Section 11.8.5, step 3.a) but
929 * preserve it just in case.
932 if (flags
& DUK_COMPARE_FLAG_EVAL_LEFT_FIRST
) {
933 d1
= duk_to_number(ctx
, -2);
934 d2
= duk_to_number(ctx
, -1);
936 d2
= duk_to_number(ctx
, -1);
937 d1
= duk_to_number(ctx
, -2);
940 c1
= (duk_small_int_t
) DUK_FPCLASSIFY(d1
);
941 s1
= (duk_small_int_t
) DUK_SIGNBIT(d1
);
942 c2
= (duk_small_int_t
) DUK_FPCLASSIFY(d2
);
943 s2
= (duk_small_int_t
) DUK_SIGNBIT(d2
);
945 if (c1
== DUK_FP_NAN
|| c2
== DUK_FP_NAN
) {
949 if (c1
== DUK_FP_ZERO
&& c2
== DUK_FP_ZERO
) {
950 /* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0,
960 if (c1
== DUK_FP_INFINITE
&& s1
== 0) {
965 if (c2
== DUK_FP_INFINITE
&& s2
== 0) {
970 if (c2
== DUK_FP_INFINITE
&& s2
!= 0) {
975 if (c1
== DUK_FP_INFINITE
&& s1
!= 0) {
988 /* Note: undefined from Section 11.8.5 always results in false
989 * return (see e.g. Section 11.8.3) - hence special treatment here.
995 if (flags
& DUK_COMPARE_FLAG_NEGATE
) {
1005 if (flags
& DUK_COMPARE_FLAG_NEGATE
) {
1024 * E5 Section 11.8.6 describes the main algorithm, which uses
1025 * [[HasInstance]]. [[HasInstance]] is defined for only
1028 * - Normal functions:
1029 * E5 Section 15.3.5.3
1030 * - Functions established with Function.prototype.bind():
1031 * E5 Section 15.3.4.5.3
1033 * For other objects, a TypeError is thrown.
1035 * Limited Proxy support: don't support 'getPrototypeOf' trap but
1036 * continue lookup in Proxy target if the value is a Proxy.
1039 DUK_INTERNAL duk_bool_t
duk_js_instanceof(duk_hthread
*thr
, duk_tval
*tv_x
, duk_tval
*tv_y
) {
1040 duk_context
*ctx
= (duk_context
*) thr
;
1047 * Get the values onto the stack first. It would be possible to cover
1048 * some normal cases without resorting to the value stack.
1050 * The right hand side could be a light function (as they generally
1051 * behave like objects). Light functions never have a 'prototype'
1052 * property so E5.1 Section 15.3.5.3 step 3 always throws a TypeError.
1053 * Using duk_require_hobject() is thus correct (except for error msg).
1056 duk_push_tval(ctx
, tv_x
);
1057 duk_push_tval(ctx
, tv_y
);
1058 func
= duk_require_hobject(ctx
, -1);
1061 * For bound objects, [[HasInstance]] just calls the target function
1062 * [[HasInstance]]. If that is again a bound object, repeat until
1063 * we find a non-bound Function object.
1066 /* XXX: this bound function resolution also happens elsewhere,
1067 * move into a shared helper.
1070 sanity
= DUK_HOBJECT_BOUND_CHAIN_SANITY
;
1072 /* check func supports [[HasInstance]] (this is checked for every function
1073 * in the bound chain, including the final one)
1076 if (!DUK_HOBJECT_IS_CALLABLE(func
)) {
1078 * Note: of native Ecmascript objects, only Function instances
1079 * have a [[HasInstance]] internal property. Custom objects might
1080 * also have it, but not in current implementation.
1082 * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF?
1084 DUK_ERROR_TYPE(thr
, "invalid instanceof rval");
1087 if (!DUK_HOBJECT_HAS_BOUND(func
)) {
1091 /* [ ... lval rval ] */
1093 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_INT_TARGET
); /* -> [ ... lval rval new_rval ] */
1094 duk_replace(ctx
, -1); /* -> [ ... lval new_rval ] */
1095 func
= duk_require_hobject(ctx
, -1);
1097 /* func support for [[HasInstance]] checked in the beginning of the loop */
1098 } while (--sanity
> 0);
1101 DUK_ERROR_RANGE(thr
, DUK_STR_BOUND_CHAIN_LIMIT
);
1105 * 'func' is now a non-bound object which supports [[HasInstance]]
1106 * (which here just means DUK_HOBJECT_FLAG_CALLABLE). Move on
1107 * to execute E5 Section 15.3.5.3.
1110 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func
));
1111 DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func
));
1113 /* [ ... lval rval(func) ] */
1115 /* Handle lightfuncs through object coercion for now. */
1116 /* XXX: direct implementation */
1117 val
= duk_get_hobject_or_lfunc_coerce(ctx
, -2);
1122 duk_get_prop_stridx(ctx
, -1, DUK_STRIDX_PROTOTYPE
); /* -> [ ... lval rval rval.prototype ] */
1123 proto
= duk_require_hobject(ctx
, -1);
1124 duk_pop(ctx
); /* -> [ ... lval rval ] */
1126 DUK_ASSERT(val
!= NULL
);
1128 #if defined(DUK_USE_ES6_PROXY)
1129 val
= duk_hobject_resolve_proxy_target(thr
, val
);
1130 DUK_ASSERT(val
!= NULL
);
1133 sanity
= DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY
;
1136 * Note: prototype chain is followed BEFORE first comparison. This
1137 * means that the instanceof lval is never itself compared to the
1138 * rval.prototype property. This is apparently intentional, see E5
1139 * Section 15.3.5.3, step 4.a.
1143 * js> (function() {}) instanceof Function
1145 * js> Function instanceof Function
1148 * For the latter, h_proto will be Function.prototype, which is the
1149 * built-in Function prototype. Because Function.[[Prototype]] is
1150 * also the built-in Function prototype, the result is true.
1153 DUK_ASSERT(val
!= NULL
);
1154 val
= DUK_HOBJECT_GET_PROTOTYPE(thr
->heap
, val
);
1160 DUK_ASSERT(val
!= NULL
);
1161 #if defined(DUK_USE_ES6_PROXY)
1162 val
= duk_hobject_resolve_proxy_target(thr
, val
);
1169 /* follow prototype chain */
1170 } while (--sanity
> 0);
1173 DUK_ERROR_RANGE(thr
, DUK_STR_PROTOTYPE_CHAIN_LIMIT
);
1191 * E5 Sections 11.8.7, 8.12.6.
1193 * Basically just a property existence check using [[HasProperty]].
1196 DUK_INTERNAL duk_bool_t
duk_js_in(duk_hthread
*thr
, duk_tval
*tv_x
, duk_tval
*tv_y
) {
1197 duk_context
*ctx
= (duk_context
*) thr
;
1201 * Get the values onto the stack first. It would be possible to cover
1202 * some normal cases without resorting to the value stack (e.g. if
1203 * lval is already a string).
1206 /* XXX: The ES5/5.1/6 specifications require that the key in 'key in obj'
1207 * must be string coerced before the internal HasProperty() algorithm is
1208 * invoked. A fast path skipping coercion could be safely implemented for
1209 * numbers (as number-to-string coercion has no side effects). For ES6
1210 * proxy behavior, the trap 'key' argument must be in a string coerced
1211 * form (which is a shame).
1214 /* TypeError if rval is not an object (or lightfunc which should behave
1215 * like a Function instance).
1217 duk_push_tval(ctx
, tv_x
);
1218 duk_push_tval(ctx
, tv_y
);
1219 duk_require_type_mask(ctx
, -1, DUK_TYPE_MASK_OBJECT
| DUK_TYPE_MASK_LIGHTFUNC
);
1220 duk_to_string(ctx
, -2); /* coerce lval with ToString() */
1222 retval
= duk_hobject_hasprop(thr
,
1223 DUK_GET_TVAL_NEGIDX(ctx
, -1),
1224 DUK_GET_TVAL_NEGIDX(ctx
, -2));
1233 * E5 Section 11.4.3.
1235 * Very straightforward. The only question is what to return for our
1236 * non-standard tag / object types.
1238 * There is an unfortunate string constant define naming problem with
1239 * typeof return values for e.g. "Object" and "object"; careful with
1240 * the built-in string defines. The LC_XXX defines are used for the
1241 * lowercase variants now.
1244 DUK_INTERNAL duk_hstring
*duk_js_typeof(duk_hthread
*thr
, duk_tval
*tv_x
) {
1245 duk_small_int_t stridx
= 0;
1249 switch (DUK_TVAL_GET_TAG(tv_x
)) {
1250 case DUK_TAG_UNDEFINED
: {
1251 stridx
= DUK_STRIDX_LC_UNDEFINED
;
1254 case DUK_TAG_NULL
: {
1255 /* Note: not a typo, "object" is returned for a null value */
1256 stridx
= DUK_STRIDX_LC_OBJECT
;
1259 case DUK_TAG_BOOLEAN
: {
1260 stridx
= DUK_STRIDX_LC_BOOLEAN
;
1263 case DUK_TAG_POINTER
: {
1264 /* implementation specific */
1265 stridx
= DUK_STRIDX_LC_POINTER
;
1268 case DUK_TAG_STRING
: {
1269 stridx
= DUK_STRIDX_LC_STRING
;
1272 case DUK_TAG_OBJECT
: {
1273 duk_hobject
*obj
= DUK_TVAL_GET_OBJECT(tv_x
);
1274 DUK_ASSERT(obj
!= NULL
);
1275 if (DUK_HOBJECT_IS_CALLABLE(obj
)) {
1276 stridx
= DUK_STRIDX_LC_FUNCTION
;
1278 stridx
= DUK_STRIDX_LC_OBJECT
;
1282 case DUK_TAG_BUFFER
: {
1283 /* implementation specific */
1284 stridx
= DUK_STRIDX_LC_BUFFER
;
1287 case DUK_TAG_LIGHTFUNC
: {
1288 stridx
= DUK_STRIDX_LC_FUNCTION
;
1291 #if defined(DUK_USE_FASTINT)
1292 case DUK_TAG_FASTINT
:
1296 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x
));
1297 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x
));
1298 stridx
= DUK_STRIDX_LC_NUMBER
;
1303 DUK_ASSERT(stridx
>= 0 && stridx
< DUK_HEAP_NUM_STRINGS
);
1304 return DUK_HTHREAD_GET_STRING(thr
, stridx
);
1308 * Array index and length
1310 * Array index: E5 Section 15.4
1311 * Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write)
1313 * The DUK_HSTRING_GET_ARRIDX_SLOW() and DUK_HSTRING_GET_ARRIDX_FAST() macros
1314 * call duk_js_to_arrayindex_string_helper().
1317 DUK_INTERNAL duk_small_int_t
duk_js_to_arrayindex_raw_string(const duk_uint8_t
*str
, duk_uint32_t blen
, duk_uarridx_t
*out_idx
) {
1320 if (blen
== 0 || blen
> 10) {
1323 if (str
[0] == DUK_ASC_0
&& blen
> 1) {
1327 /* Accept 32-bit decimal integers, no leading zeroes, signs, etc.
1328 * Leading zeroes are not accepted (zero index "0" is an exception
1333 while (blen
-- > 0) {
1334 duk_uint8_t c
= *str
++;
1335 if (c
>= DUK_ASC_0
&& c
<= DUK_ASC_9
) {
1336 /* Careful overflow handling. When multiplying by 10:
1337 * - 0x19999998 x 10 = 0xfffffff0: no overflow, and adding
1339 * - 0x19999999 x 10 = 0xfffffffa: no overflow, adding
1340 * 0...5 is safe, 6...9 overflows.
1341 * - 0x1999999a x 10 = 0x100000004: always overflow.
1343 if (DUK_UNLIKELY(res
>= 0x19999999UL
)) {
1344 if (res
>= 0x1999999aUL
) {
1345 /* Always overflow. */
1348 DUK_ASSERT(res
== 0x19999999UL
);
1353 res
= 0xfffffffaUL
+ c
;
1354 DUK_ASSERT(res
>= 0xfffffffaUL
);
1355 DUK_ASSERT_DISABLE(res
<= 0xffffffffUL
); /* range */
1357 res
= res
* 10U + (duk_uint32_t
) (c
- DUK_ASC_0
);
1368 *out_idx
= DUK_HSTRING_NO_ARRAY_INDEX
;
1372 /* Called by duk_hstring.h macros */
1373 DUK_INTERNAL duk_uarridx_t
duk_js_to_arrayindex_string_helper(duk_hstring
*h
) {
1377 if (!DUK_HSTRING_HAS_ARRIDX(h
)) {
1378 return DUK_HSTRING_NO_ARRAY_INDEX
;
1381 rc
= duk_js_to_arrayindex_raw_string(DUK_HSTRING_GET_DATA(h
),
1382 DUK_HSTRING_GET_BYTELEN(h
),
1385 DUK_ASSERT(rc
!= 0);