1 /*
2 * ECMAScript specification algorithm and conversion helpers.
3 *
4 * These helpers encapsulate the primitive ECMAScript operation semantics,
5 * and are used by the bytecode executor and the API (among other places).
6 * Some primitives are only implemented as part of the API and have no
7 * "internal" helper. This is the case when an internal helper would not
8 * really be useful; e.g. the operation is rare, uses value stack heavily,
9 * etc.
10 *
11 * The operation arguments depend on what is required to implement
12 * the operation:
13 *
14 * - If an operation is simple and stateless, and has no side
15 * effects, it won't take an duk_hthread argument and its
16 * arguments may be duk_tval pointers (which are safe as long
17 * as no side effects take place).
18 *
19 * - If complex coercions are required (e.g. a "ToNumber" coercion)
20 * or errors may be thrown, the operation takes an duk_hthread
21 * argument. This also implies that the operation may have
22 * arbitrary side effects, invalidating any duk_tval pointers.
23 *
24 * - For operations with potential side effects, arguments can be
25 * taken in several ways:
26 *
27 * a) as duk_tval pointers, which makes sense if the "common case"
28 * can be resolved without side effects (e.g. coercion); the
29 * arguments are pushed to the valstack for coercion if
30 * necessary
31 *
32 * b) as duk_tval values
33 *
34 * c) implicitly on value stack top
35 *
36 * d) as indices to the value stack
37 *
38 * Future work:
39 *
40 * - Argument styles may not be the most sensible in every case now.
41 *
42 * - In-place coercions might be useful for several operations, if
43 * in-place coercion is OK for the bytecode executor and the API.
44 */
45
46 #include "duk_internal.h"
47
48 /*
49 * ToPrimitive() (E5 Section 9.1)
50 *
51 * ==> implemented in the API.
52 */
53
54 /*
55 * ToBoolean() (E5 Section 9.2)
56 */
57
duk_js_toboolean(duk_tval * tv)58 DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) {
59 switch (DUK_TVAL_GET_TAG(tv)) {
60 case DUK_TAG_UNDEFINED:
61 case DUK_TAG_NULL:
62 return 0;
63 case DUK_TAG_BOOLEAN:
64 DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv) == 0 || DUK_TVAL_GET_BOOLEAN(tv) == 1);
65 return DUK_TVAL_GET_BOOLEAN(tv);
66 case DUK_TAG_STRING: {
67 /* Symbols ToBoolean() coerce to true, regardless of their
68 * description. This happens with no explicit check because
69 * of the symbol representation byte prefix.
70 */
71 duk_hstring *h = DUK_TVAL_GET_STRING(tv);
72 DUK_ASSERT(h != NULL);
73 return (DUK_HSTRING_GET_BYTELEN(h) > 0 ? 1 : 0);
74 }
75 case DUK_TAG_OBJECT: {
76 return 1;
77 }
78 case DUK_TAG_BUFFER: {
79 /* Mimic Uint8Array semantics: objects coerce true, regardless
80 * of buffer length (zero or not) or context.
81 */
82 return 1;
83 }
84 case DUK_TAG_POINTER: {
85 void *p = DUK_TVAL_GET_POINTER(tv);
86 return (p != NULL ? 1 : 0);
87 }
88 case DUK_TAG_LIGHTFUNC: {
89 return 1;
90 }
91 #if defined(DUK_USE_FASTINT)
92 case DUK_TAG_FASTINT:
93 if (DUK_TVAL_GET_FASTINT(tv) != 0) {
94 return 1;
95 } else {
96 return 0;
97 }
98 #endif
99 default: {
100 /* number */
101 duk_double_t d;
102 #if defined(DUK_USE_PREFER_SIZE)
103 int c;
104 #endif
105 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
106 DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
107 d = DUK_TVAL_GET_DOUBLE(tv);
108 #if defined(DUK_USE_PREFER_SIZE)
109 c = DUK_FPCLASSIFY((double) d);
110 if (c == DUK_FP_ZERO || c == DUK_FP_NAN) {
111 return 0;
112 } else {
113 return 1;
114 }
115 #else
116 DUK_ASSERT(duk_double_is_nan_or_zero(d) == 0 || duk_double_is_nan_or_zero(d) == 1);
117 return duk_double_is_nan_or_zero(d) ^ 1;
118 #endif
119 }
120 }
121 DUK_UNREACHABLE();
122 DUK_WO_UNREACHABLE(return 0;);
123 }
124
125 /*
126 * ToNumber() (E5 Section 9.3)
127 *
128 * Value to convert must be on stack top, and is popped before exit.
129 *
130 * See: http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
131 * http://www.cs.indiana.edu/~burger/fp/index.html
132 *
133 * Notes on the conversion:
134 *
135 * - There are specific requirements on the accuracy of the conversion
136 * through a "Mathematical Value" (MV), so this conversion is not
137 * trivial.
138 *
139 * - Quick rejects (e.g. based on first char) are difficult because
140 * the grammar allows leading and trailing white space.
141 *
142 * - Quick reject based on string length is difficult even after
143 * accounting for white space; there may be arbitrarily many
144 * decimal digits.
145 *
146 * - Standard grammar allows decimal values ("123"), hex values
147 * ("0x123") and infinities
148 *
149 * - Unlike source code literals, ToNumber() coerces empty strings
150 * and strings with only whitespace to zero (not NaN). However,
151 * while '' coerces to 0, '+' and '-' coerce to NaN.
152 */
153
154 /* E5 Section 9.3.1 */
duk__tonumber_string_raw(duk_hthread * thr)155 DUK_LOCAL duk_double_t duk__tonumber_string_raw(duk_hthread *thr) {
156 duk_small_uint_t s2n_flags;
157 duk_double_t d;
158
159 DUK_ASSERT(duk_is_string(thr, -1));
160
161 /* Quite lenient, e.g. allow empty as zero, but don't allow trailing
162 * garbage.
163 */
164 s2n_flags = DUK_S2N_FLAG_TRIM_WHITE |
165 DUK_S2N_FLAG_ALLOW_EXP |
166 DUK_S2N_FLAG_ALLOW_PLUS |
167 DUK_S2N_FLAG_ALLOW_MINUS |
168 DUK_S2N_FLAG_ALLOW_INF |
169 DUK_S2N_FLAG_ALLOW_FRAC |
170 DUK_S2N_FLAG_ALLOW_NAKED_FRAC |
171 DUK_S2N_FLAG_ALLOW_EMPTY_FRAC |
172 DUK_S2N_FLAG_ALLOW_EMPTY_AS_ZERO |
173 DUK_S2N_FLAG_ALLOW_LEADING_ZERO |
174 DUK_S2N_FLAG_ALLOW_AUTO_HEX_INT |
175 DUK_S2N_FLAG_ALLOW_AUTO_OCT_INT |
176 DUK_S2N_FLAG_ALLOW_AUTO_BIN_INT;
177
178 duk_numconv_parse(thr, 10 /*radix*/, s2n_flags);
179
180 #if defined(DUK_USE_PREFER_SIZE)
181 d = duk_get_number(thr, -1);
182 duk_pop_unsafe(thr);
183 #else
184 thr->valstack_top--;
185 DUK_ASSERT(DUK_TVAL_IS_NUMBER(thr->valstack_top));
186 DUK_ASSERT(DUK_TVAL_IS_DOUBLE(thr->valstack_top)); /* no fastint conversion in numconv now */
187 DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(thr->valstack_top));
188 d = DUK_TVAL_GET_DOUBLE(thr->valstack_top); /* assumes not a fastint */
189 DUK_TVAL_SET_UNDEFINED(thr->valstack_top);
190 #endif
191
192 return d;
193 }
194
duk_js_tonumber(duk_hthread * thr,duk_tval * tv)195 DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
196 DUK_ASSERT(thr != NULL);
197 DUK_ASSERT(tv != NULL);
198
199 switch (DUK_TVAL_GET_TAG(tv)) {
200 case DUK_TAG_UNDEFINED: {
201 /* return a specific NaN (although not strictly necessary) */
202 duk_double_union du;
203 DUK_DBLUNION_SET_NAN(&du);
204 DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
205 return du.d;
206 }
207 case DUK_TAG_NULL: {
208 /* +0.0 */
209 return 0.0;
210 }
211 case DUK_TAG_BOOLEAN: {
212 if (DUK_TVAL_IS_BOOLEAN_TRUE(tv)) {
213 return 1.0;
214 }
215 return 0.0;
216 }
217 case DUK_TAG_STRING: {
218 /* For Symbols ToNumber() is always a TypeError. */
219 duk_hstring *h = DUK_TVAL_GET_STRING(tv);
220 if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(h))) {
221 DUK_ERROR_TYPE(thr, DUK_STR_CANNOT_NUMBER_COERCE_SYMBOL);
222 DUK_WO_NORETURN(return 0.0;);
223 }
224 duk_push_hstring(thr, h);
225 return duk__tonumber_string_raw(thr);
226 }
227 case DUK_TAG_BUFFER: /* plain buffer treated like object */
228 case DUK_TAG_OBJECT: {
229 duk_double_t d;
230 duk_push_tval(thr, tv);
231 duk_to_primitive(thr, -1, DUK_HINT_NUMBER); /* 'tv' becomes invalid */
232
233 /* recursive call for a primitive value (guaranteed not to cause second
234 * recursion).
235 */
236 DUK_ASSERT(duk_get_tval(thr, -1) != NULL);
237 d = duk_js_tonumber(thr, duk_get_tval(thr, -1));
238
239 duk_pop_unsafe(thr);
240 return d;
241 }
242 case DUK_TAG_POINTER: {
243 /* Coerce like boolean */
244 void *p = DUK_TVAL_GET_POINTER(tv);
245 return (p != NULL ? 1.0 : 0.0);
246 }
247 case DUK_TAG_LIGHTFUNC: {
248 /* +(function(){}) -> NaN */
249 return DUK_DOUBLE_NAN;
250 }
251 #if defined(DUK_USE_FASTINT)
252 case DUK_TAG_FASTINT:
253 return (duk_double_t) DUK_TVAL_GET_FASTINT(tv);
254 #endif
255 default: {
256 /* number */
257 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
258 DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
259 return DUK_TVAL_GET_DOUBLE(tv);
260 }
261 }
262
263 DUK_UNREACHABLE();
264 DUK_WO_UNREACHABLE(return 0.0;);
265 }
266
267 /*
268 * ToInteger() (E5 Section 9.4)
269 */
270
271 /* exposed, used by e.g. duk_bi_date.c */
duk_js_tointeger_number(duk_double_t x)272 DUK_INTERNAL duk_double_t duk_js_tointeger_number(duk_double_t x) {
273 #if defined(DUK_USE_PREFER_SIZE)
274 duk_small_int_t c = (duk_small_int_t) DUK_FPCLASSIFY(x);
275
276 if (DUK_UNLIKELY(c == DUK_FP_NAN)) {
277 return 0.0;
278 } else if (DUK_UNLIKELY(c == DUK_FP_INFINITE)) {
279 return x;
280 } else {
281 /* Finite, including neg/pos zero. Neg zero sign must be
282 * preserved.
283 */
284 return duk_double_trunc_towards_zero(x);
285 }
286 #else /* DUK_USE_PREFER_SIZE */
287 /* NaN and Infinity have the same exponent so it's a cheap
288 * initial check for the rare path.
289 */
290 if (DUK_UNLIKELY(duk_double_is_nan_or_inf(x) != 0U)) {
291 if (duk_double_is_nan(x)) {
292 return 0.0;
293 } else {
294 return x;
295 }
296 } else {
297 return duk_double_trunc_towards_zero(x);
298 }
299 #endif /* DUK_USE_PREFER_SIZE */
300 }
301
duk_js_tointeger(duk_hthread * thr,duk_tval * tv)302 DUK_INTERNAL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv) {
303 /* XXX: fastint */
304 duk_double_t d = duk_js_tonumber(thr, tv); /* invalidates tv */
305 return duk_js_tointeger_number(d);
306 }
307
308 /*
309 * ToInt32(), ToUint32(), ToUint16() (E5 Sections 9.5, 9.6, 9.7)
310 */
311
312 /* combined algorithm matching E5 Sections 9.5 and 9.6 */
duk__toint32_touint32_helper(duk_double_t x,duk_bool_t is_toint32)313 DUK_LOCAL duk_double_t duk__toint32_touint32_helper(duk_double_t x, duk_bool_t is_toint32) {
314 #if defined (DUK_USE_PREFER_SIZE)
315 duk_small_int_t c;
316 #endif
317
318 #if defined (DUK_USE_PREFER_SIZE)
319 c = (duk_small_int_t) DUK_FPCLASSIFY(x);
320 if (c == DUK_FP_NAN || c == DUK_FP_ZERO || c == DUK_FP_INFINITE) {
321 return 0.0;
322 }
323 #else
324 if (duk_double_is_nan_zero_inf(x)) {
325 return 0.0;
326 }
327 #endif
328
329 /* x = sign(x) * floor(abs(x)), i.e. truncate towards zero, keep sign */
330 x = duk_double_trunc_towards_zero(x);
331
332 /* NOTE: fmod(x) result sign is same as sign of x, which
333 * differs from what Javascript wants (see Section 9.6).
334 */
335
336 x = DUK_FMOD(x, DUK_DOUBLE_2TO32); /* -> x in ]-2**32, 2**32[ */
337
338 if (x < 0.0) {
339 x += DUK_DOUBLE_2TO32;
340 }
341 DUK_ASSERT(x >= 0 && x < DUK_DOUBLE_2TO32); /* -> x in [0, 2**32[ */
342
343 if (is_toint32) {
344 if (x >= DUK_DOUBLE_2TO31) {
345 /* x in [2**31, 2**32[ */
346
347 x -= DUK_DOUBLE_2TO32; /* -> x in [-2**31,2**31[ */
348 }
349 }
350
351 return x;
352 }
353
duk_js_toint32(duk_hthread * thr,duk_tval * tv)354 DUK_INTERNAL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv) {
355 duk_double_t d;
356
357 #if defined(DUK_USE_FASTINT)
358 if (DUK_TVAL_IS_FASTINT(tv)) {
359 return DUK_TVAL_GET_FASTINT_I32(tv);
360 }
361 #endif
362
363 d = duk_js_tonumber(thr, tv); /* invalidates tv */
364 d = duk__toint32_touint32_helper(d, 1);
365 DUK_ASSERT(DUK_FPCLASSIFY(d) == DUK_FP_ZERO || DUK_FPCLASSIFY(d) == DUK_FP_NORMAL);
366 DUK_ASSERT(d >= -2147483648.0 && d <= 2147483647.0); /* [-0x80000000,0x7fffffff] */
367 DUK_ASSERT(duk_double_equals(d, (duk_double_t) ((duk_int32_t) d))); /* whole, won't clip */
368 return (duk_int32_t) d;
369 }
370
371
duk_js_touint32(duk_hthread * thr,duk_tval * tv)372 DUK_INTERNAL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv) {
373 duk_double_t d;
374
375 #if defined(DUK_USE_FASTINT)
376 if (DUK_TVAL_IS_FASTINT(tv)) {
377 return DUK_TVAL_GET_FASTINT_U32(tv);
378 }
379 #endif
380
381 d = duk_js_tonumber(thr, tv); /* invalidates tv */
382 d = duk__toint32_touint32_helper(d, 0);
383 DUK_ASSERT(DUK_FPCLASSIFY(d) == DUK_FP_ZERO || DUK_FPCLASSIFY(d) == DUK_FP_NORMAL);
384 DUK_ASSERT(d >= 0.0 && d <= 4294967295.0); /* [0x00000000, 0xffffffff] */
385 DUK_ASSERT(duk_double_equals(d, (duk_double_t) ((duk_uint32_t) d))); /* whole, won't clip */
386 return (duk_uint32_t) d;
387
388 }
389
duk_js_touint16(duk_hthread * thr,duk_tval * tv)390 DUK_INTERNAL duk_uint16_t duk_js_touint16(duk_hthread *thr, duk_tval *tv) {
391 /* should be a safe way to compute this */
392 return (duk_uint16_t) (duk_js_touint32(thr, tv) & 0x0000ffffU);
393 }
394
395 /*
396 * ToString() (E5 Section 9.8)
397 * ToObject() (E5 Section 9.9)
398 * CheckObjectCoercible() (E5 Section 9.10)
399 * IsCallable() (E5 Section 9.11)
400 *
401 * ==> implemented in the API.
402 */
403
404 /*
405 * Loose equality, strict equality, and SameValue (E5 Sections 11.9.1, 11.9.4,
406 * 9.12). These have much in common so they can share some helpers.
407 *
408 * Future work notes:
409 *
410 * - Current implementation (and spec definition) has recursion; this should
411 * be fixed if possible.
412 *
413 * - String-to-number coercion should be possible without going through the
414 * value stack (and be more compact) if a shared helper is invoked.
415 */
416
417 /* Note that this is the same operation for strict and loose equality:
418 * - E5 Section 11.9.3, step 1.c (loose)
419 * - E5 Section 11.9.6, step 4 (strict)
420 */
421
duk__js_equals_number(duk_double_t x,duk_double_t y)422 DUK_LOCAL duk_bool_t duk__js_equals_number(duk_double_t x, duk_double_t y) {
423 #if defined(DUK_USE_PARANOID_MATH)
424 /* Straightforward algorithm, makes fewer compiler assumptions. */
425 duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
426 duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
427 if (cx == DUK_FP_NAN || cy == DUK_FP_NAN) {
428 return 0;
429 }
430 if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) {
431 return 1;
432 }
433 if (x == y) {
434 return 1;
435 }
436 return 0;
437 #else /* DUK_USE_PARANOID_MATH */
438 /* Better equivalent algorithm. If the compiler is compliant, C and
439 * ECMAScript semantics are identical for this particular comparison.
440 * In particular, NaNs must never compare equal and zeroes must compare
441 * equal regardless of sign. Could also use a macro, but this inlines
442 * already nicely (no difference on gcc, for instance).
443 */
444 if (duk_double_equals(x, y)) {
445 /* IEEE requires that NaNs compare false */
446 DUK_ASSERT(DUK_FPCLASSIFY(x) != DUK_FP_NAN);
447 DUK_ASSERT(DUK_FPCLASSIFY(y) != DUK_FP_NAN);
448 return 1;
449 } else {
450 /* IEEE requires that zeros compare the same regardless
451 * of their signed, so if both x and y are zeroes, they
452 * are caught above.
453 */
454 DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));
455 return 0;
456 }
457 #endif /* DUK_USE_PARANOID_MATH */
458 }
459
duk__js_samevalue_number(duk_double_t x,duk_double_t y)460 DUK_LOCAL duk_bool_t duk__js_samevalue_number(duk_double_t x, duk_double_t y) {
461 #if defined(DUK_USE_PARANOID_MATH)
462 duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
463 duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
464
465 if (cx == DUK_FP_NAN && cy == DUK_FP_NAN) {
466 /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
467 return 1;
468 }
469 if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) {
470 /* Note: cannot assume that a non-zero return value of signbit() would
471 * always be the same -- hence cannot (portably) use something like:
472 *
473 * signbit(x) == signbit(y)
474 */
475 duk_small_int_t sx = DUK_SIGNBIT(x) ? 1 : 0;
476 duk_small_int_t sy = DUK_SIGNBIT(y) ? 1 : 0;
477 return (sx == sy);
478 }
479
480 /* normal comparison; known:
481 * - both x and y are not NaNs (but one of them can be)
482 * - both x and y are not zero (but one of them can be)
483 * - x and y may be denormal or infinite
484 */
485
486 return (x == y);
487 #else /* DUK_USE_PARANOID_MATH */
488 duk_small_int_t cx = (duk_small_int_t) DUK_FPCLASSIFY(x);
489 duk_small_int_t cy = (duk_small_int_t) DUK_FPCLASSIFY(y);
490
491 if (duk_double_equals(x, y)) {
492 /* IEEE requires that NaNs compare false */
493 DUK_ASSERT(DUK_FPCLASSIFY(x) != DUK_FP_NAN);
494 DUK_ASSERT(DUK_FPCLASSIFY(y) != DUK_FP_NAN);
495
496 /* Using classification has smaller footprint than direct comparison. */
497 if (DUK_UNLIKELY(cx == DUK_FP_ZERO && cy == DUK_FP_ZERO)) {
498 /* Note: cannot assume that a non-zero return value of signbit() would
499 * always be the same -- hence cannot (portably) use something like:
500 *
501 * signbit(x) == signbit(y)
502 */
503 return duk_double_same_sign(x, y);
504 }
505 return 1;
506 } else {
507 /* IEEE requires that zeros compare the same regardless
508 * of their sign, so if both x and y are zeroes, they
509 * are caught above.
510 */
511 DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));
512
513 /* Difference to non-strict/strict comparison is that NaNs compare
514 * equal and signed zero signs matter.
515 */
516 if (DUK_UNLIKELY(cx == DUK_FP_NAN && cy == DUK_FP_NAN)) {
517 /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
518 return 1;
519 }
520 return 0;
521 }
522 #endif /* DUK_USE_PARANOID_MATH */
523 }
524
duk_js_equals_helper(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y,duk_small_uint_t flags)525 DUK_INTERNAL duk_bool_t duk_js_equals_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) {
526 duk_uint_t type_mask_x;
527 duk_uint_t type_mask_y;
528
529 /* If flags != 0 (strict or SameValue), thr can be NULL. For loose
530 * equals comparison it must be != NULL.
531 */
532 DUK_ASSERT(flags != 0 || thr != NULL);
533
534 /*
535 * Same type?
536 *
537 * Note: since number values have no explicit tag in the 8-byte
538 * representation, need the awkward if + switch.
539 */
540
541 #if defined(DUK_USE_FASTINT)
542 if (DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y)) {
543 if (DUK_TVAL_GET_FASTINT(tv_x) == DUK_TVAL_GET_FASTINT(tv_y)) {
544 return 1;
545 } else {
546 return 0;
547 }
548 }
549 else
550 #endif
551 if (DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y)) {
552 duk_double_t d1, d2;
553
554 /* Catches both doubles and cases where only one argument is
555 * a fastint so can't assume a double.
556 */
557 d1 = DUK_TVAL_GET_NUMBER(tv_x);
558 d2 = DUK_TVAL_GET_NUMBER(tv_y);
559 if (DUK_UNLIKELY((flags & DUK_EQUALS_FLAG_SAMEVALUE) != 0)) {
560 /* SameValue */
561 return duk__js_samevalue_number(d1, d2);
562 } else {
563 /* equals and strict equals */
564 return duk__js_equals_number(d1, d2);
565 }
566 } else if (DUK_TVAL_GET_TAG(tv_x) == DUK_TVAL_GET_TAG(tv_y)) {
567 switch (DUK_TVAL_GET_TAG(tv_x)) {
568 case DUK_TAG_UNDEFINED:
569 case DUK_TAG_NULL: {
570 return 1;
571 }
572 case DUK_TAG_BOOLEAN: {
573 return DUK_TVAL_GET_BOOLEAN(tv_x) == DUK_TVAL_GET_BOOLEAN(tv_y);
574 }
575 case DUK_TAG_POINTER: {
576 return DUK_TVAL_GET_POINTER(tv_x) == DUK_TVAL_GET_POINTER(tv_y);
577 }
578 case DUK_TAG_STRING:
579 case DUK_TAG_OBJECT: {
580 /* Heap pointer comparison suffices for strings and objects.
581 * Symbols compare equal if they have the same internal
582 * representation; again heap pointer comparison suffices.
583 */
584 return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y);
585 }
586 case DUK_TAG_BUFFER: {
587 /* In Duktape 2.x plain buffers mimic Uint8Array objects
588 * so always compare by heap pointer. In Duktape 1.x
589 * strict comparison would compare heap pointers and
590 * non-strict would compare contents.
591 */
592 return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y);
593 }
594 case DUK_TAG_LIGHTFUNC: {
595 /* At least 'magic' has a significant impact on function
596 * identity.
597 */
598 duk_small_uint_t lf_flags_x;
599 duk_small_uint_t lf_flags_y;
600 duk_c_function func_x;
601 duk_c_function func_y;
602
603 DUK_TVAL_GET_LIGHTFUNC(tv_x, func_x, lf_flags_x);
604 DUK_TVAL_GET_LIGHTFUNC(tv_y, func_y, lf_flags_y);
605 return ((func_x == func_y) && (lf_flags_x == lf_flags_y)) ? 1 : 0;
606 }
607 #if defined(DUK_USE_FASTINT)
608 case DUK_TAG_FASTINT:
609 #endif
610 default: {
611 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x));
612 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_y));
613 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));
614 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_y));
615 DUK_UNREACHABLE();
616 DUK_WO_UNREACHABLE(return 0;);
617 }
618 }
619 }
620
621 if ((flags & (DUK_EQUALS_FLAG_STRICT | DUK_EQUALS_FLAG_SAMEVALUE)) != 0) {
622 return 0;
623 }
624
625 DUK_ASSERT(flags == 0); /* non-strict equality from here on */
626
627 /*
628 * Types are different; various cases for non-strict comparison
629 *
630 * Since comparison is symmetric, we use a "swap trick" to reduce
631 * code size.
632 */
633
634 type_mask_x = duk_get_type_mask_tval(tv_x);
635 type_mask_y = duk_get_type_mask_tval(tv_y);
636
637 /* Undefined/null are considered equal (e.g. "null == undefined" -> true). */
638 if ((type_mask_x & (DUK_TYPE_MASK_UNDEFINED | DUK_TYPE_MASK_NULL)) &&
639 (type_mask_y & (DUK_TYPE_MASK_NULL | DUK_TYPE_MASK_UNDEFINED))) {
640 return 1;
641 }
642
643 /* Number/string -> coerce string to number (e.g. "'1.5' == 1.5" -> true). */
644 if ((type_mask_x & DUK_TYPE_MASK_NUMBER) && (type_mask_y & DUK_TYPE_MASK_STRING)) {
645 if (!DUK_TVAL_STRING_IS_SYMBOL(tv_y)) {
646 duk_double_t d1, d2;
647 d1 = DUK_TVAL_GET_NUMBER(tv_x);
648 d2 = duk_to_number_tval(thr, tv_y);
649 return duk__js_equals_number(d1, d2);
650 }
651 }
652 if ((type_mask_x & DUK_TYPE_MASK_STRING) && (type_mask_y & DUK_TYPE_MASK_NUMBER)) {
653 if (!DUK_TVAL_STRING_IS_SYMBOL(tv_x)) {
654 duk_double_t d1, d2;
655 d1 = DUK_TVAL_GET_NUMBER(tv_y);
656 d2 = duk_to_number_tval(thr, tv_x);
657 return duk__js_equals_number(d1, d2);
658 }
659 }
660
661 /* Boolean/any -> coerce boolean to number and try again. If boolean is
662 * compared to a pointer, the final comparison after coercion now always
663 * yields false (as pointer vs. number compares to false), but this is
664 * not special cased.
665 *
666 * ToNumber(bool) is +1.0 or 0.0. Tagged boolean value is always 0 or 1.
667 */
668 if (type_mask_x & DUK_TYPE_MASK_BOOLEAN) {
669 DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_x) == 0 || DUK_TVAL_GET_BOOLEAN(tv_x) == 1);
670 duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_x));
671 duk_push_tval(thr, tv_y);
672 goto recursive_call;
673 }
674 if (type_mask_y & DUK_TYPE_MASK_BOOLEAN) {
675 DUK_ASSERT(DUK_TVAL_GET_BOOLEAN(tv_y) == 0 || DUK_TVAL_GET_BOOLEAN(tv_y) == 1);
676 duk_push_tval(thr, tv_x);
677 duk_push_uint(thr, DUK_TVAL_GET_BOOLEAN(tv_y));
678 goto recursive_call;
679 }
680
681 /* String-number-symbol/object -> coerce object to primitive (apparently without hint), then try again. */
682 if ((type_mask_x & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER)) &&
683 (type_mask_y & DUK_TYPE_MASK_OBJECT)) {
684 /* No symbol check needed because symbols and strings are accepted. */
685 duk_push_tval(thr, tv_x);
686 duk_push_tval(thr, tv_y);
687 duk_to_primitive(thr, -1, DUK_HINT_NONE); /* apparently no hint? */
688 goto recursive_call;
689 }
690 if ((type_mask_x & DUK_TYPE_MASK_OBJECT) &&
691 (type_mask_y & (DUK_TYPE_MASK_STRING | DUK_TYPE_MASK_NUMBER))) {
692 /* No symbol check needed because symbols and strings are accepted. */
693 duk_push_tval(thr, tv_x);
694 duk_push_tval(thr, tv_y);
695 duk_to_primitive(thr, -2, DUK_HINT_NONE); /* apparently no hint? */
696 goto recursive_call;
697 }
698
699 /* Nothing worked -> not equal. */
700 return 0;
701
702 recursive_call:
703 /* Shared code path to call the helper again with arguments on stack top. */
704 {
705 duk_bool_t rc;
706 rc = duk_js_equals_helper(thr,
707 DUK_GET_TVAL_NEGIDX(thr, -2),
708 DUK_GET_TVAL_NEGIDX(thr, -1),
709 0 /*flags:nonstrict*/);
710 duk_pop_2_unsafe(thr);
711 return rc;
712 }
713 }
714
715 /*
716 * Comparisons (x >= y, x > y, x <= y, x < y)
717 *
718 * E5 Section 11.8.5: implement 'x < y' and then use negate and eval_left_first
719 * flags to get the rest.
720 */
721
722 /* XXX: this should probably just operate on the stack top, because it
723 * needs to push stuff on the stack anyway...
724 */
725
duk_js_data_compare(const duk_uint8_t * buf1,const duk_uint8_t * buf2,duk_size_t len1,duk_size_t len2)726 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) {
727 duk_size_t prefix_len;
728 duk_small_int_t rc;
729
730 prefix_len = (len1 <= len2 ? len1 : len2);
731
732 /* duk_memcmp() is guaranteed to return zero (equal) for zero length
733 * inputs.
734 */
735 rc = duk_memcmp_unsafe((const void *) buf1,
736 (const void *) buf2,
737 (size_t) prefix_len);
738
739 if (rc < 0) {
740 return -1;
741 } else if (rc > 0) {
742 return 1;
743 }
744
745 /* prefix matches, lengths matter now */
746 if (len1 < len2) {
747 /* e.g. "x" < "xx" */
748 return -1;
749 } else if (len1 > len2) {
750 return 1;
751 }
752
753 return 0;
754 }
755
duk_js_string_compare(duk_hstring * h1,duk_hstring * h2)756 DUK_INTERNAL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2) {
757 /*
758 * String comparison (E5 Section 11.8.5, step 4), which
759 * needs to compare codepoint by codepoint.
760 *
761 * However, UTF-8 allows us to use strcmp directly: the shared
762 * prefix will be encoded identically (UTF-8 has unique encoding)
763 * and the first differing character can be compared with a simple
764 * unsigned byte comparison (which strcmp does).
765 *
766 * This will not work properly for non-xutf-8 strings, but this
767 * is not an issue for compliance.
768 */
769
770 DUK_ASSERT(h1 != NULL);
771 DUK_ASSERT(h2 != NULL);
772
773 return duk_js_data_compare((const duk_uint8_t *) DUK_HSTRING_GET_DATA(h1),
774 (const duk_uint8_t *) DUK_HSTRING_GET_DATA(h2),
775 (duk_size_t) DUK_HSTRING_GET_BYTELEN(h1),
776 (duk_size_t) DUK_HSTRING_GET_BYTELEN(h2));
777 }
778
779 #if 0 /* unused */
780 DUK_INTERNAL duk_small_int_t duk_js_buffer_compare(duk_heap *heap, duk_hbuffer *h1, duk_hbuffer *h2) {
781 /* Similar to String comparison. */
782
783 DUK_ASSERT(h1 != NULL);
784 DUK_ASSERT(h2 != NULL);
785 DUK_UNREF(heap);
786
787 return duk_js_data_compare((const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(heap, h1),
788 (const duk_uint8_t *) DUK_HBUFFER_GET_DATA_PTR(heap, h2),
789 (duk_size_t) DUK_HBUFFER_GET_SIZE(h1),
790 (duk_size_t) DUK_HBUFFER_GET_SIZE(h2));
791 }
792 #endif
793
794 #if defined(DUK_USE_FASTINT)
duk__compare_fastint(duk_bool_t retval,duk_int64_t v1,duk_int64_t v2)795 DUK_LOCAL duk_bool_t duk__compare_fastint(duk_bool_t retval, duk_int64_t v1, duk_int64_t v2) {
796 DUK_ASSERT(retval == 0 || retval == 1);
797 if (v1 < v2) {
798 return retval ^ 1;
799 } else {
800 return retval;
801 }
802 }
803 #endif
804
805 #if defined(DUK_USE_PARANOID_MATH)
duk__compare_number(duk_bool_t retval,duk_double_t d1,duk_double_t d2)806 DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) {
807 duk_small_int_t c1, s1, c2, s2;
808
809 DUK_ASSERT(retval == 0 || retval == 1);
810 c1 = (duk_small_int_t) DUK_FPCLASSIFY(d1);
811 s1 = (duk_small_int_t) DUK_SIGNBIT(d1);
812 c2 = (duk_small_int_t) DUK_FPCLASSIFY(d2);
813 s2 = (duk_small_int_t) DUK_SIGNBIT(d2);
814
815 if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) {
816 return 0; /* Always false, regardless of negation. */
817 }
818
819 if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) {
820 /* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0,
821 * steps e, f, and g.
822 */
823 return retval; /* false */
824 }
825
826 if (d1 == d2) {
827 return retval; /* false */
828 }
829
830 if (c1 == DUK_FP_INFINITE && s1 == 0) {
831 /* x == +Infinity */
832 return retval; /* false */
833 }
834
835 if (c2 == DUK_FP_INFINITE && s2 == 0) {
836 /* y == +Infinity */
837 return retval ^ 1; /* true */
838 }
839
840 if (c2 == DUK_FP_INFINITE && s2 != 0) {
841 /* y == -Infinity */
842 return retval; /* false */
843 }
844
845 if (c1 == DUK_FP_INFINITE && s1 != 0) {
846 /* x == -Infinity */
847 return retval ^ 1; /* true */
848 }
849
850 if (d1 < d2) {
851 return retval ^ 1; /* true */
852 }
853
854 return retval; /* false */
855 }
856 #else /* DUK_USE_PARANOID_MATH */
duk__compare_number(duk_bool_t retval,duk_double_t d1,duk_double_t d2)857 DUK_LOCAL duk_bool_t duk__compare_number(duk_bool_t retval, duk_double_t d1, duk_double_t d2) {
858 /* This comparison tree relies doesn't match the exact steps in
859 * E5 Section 11.8.5 but should produce the same results. The
860 * steps rely on exact IEEE semantics for NaNs, etc.
861 */
862
863 DUK_ASSERT(retval == 0 || retval == 1);
864 if (d1 < d2) {
865 /* In no case should both (d1 < d2) and (d2 < d1) be true.
866 * It's possible that neither is true though, and that's
867 * handled below.
868 */
869 DUK_ASSERT(!(d2 < d1));
870
871 /* - d1 < d2, both d1/d2 are normals (not Infinity, not NaN)
872 * - d2 is +Infinity, d1 != +Infinity and NaN
873 * - d1 is -Infinity, d2 != -Infinity and NaN
874 */
875 return retval ^ 1;
876 } else {
877 if (d2 < d1) {
878 /* - !(d1 < d2), both d1/d2 are normals (not Infinity, not NaN)
879 * - d1 is +Infinity, d2 != +Infinity and NaN
880 * - d2 is -Infinity, d1 != -Infinity and NaN
881 */
882 return retval;
883 } else {
884 /* - d1 and/or d2 is NaN
885 * - d1 and d2 are both +/- 0
886 * - d1 == d2 (including infinities)
887 */
888 if (duk_double_is_nan(d1) || duk_double_is_nan(d2)) {
889 /* Note: undefined from Section 11.8.5 always
890 * results in false return (see e.g. Section
891 * 11.8.3) - hence special treatment here.
892 */
893 return 0; /* zero regardless of negation */
894 } else {
895 return retval;
896 }
897 }
898 }
899 }
900 #endif /* DUK_USE_PARANOID_MATH */
901
duk_js_compare_helper(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y,duk_small_uint_t flags)902 DUK_INTERNAL duk_bool_t duk_js_compare_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_small_uint_t flags) {
903 duk_double_t d1, d2;
904 duk_small_int_t rc;
905 duk_bool_t retval;
906
907 DUK_ASSERT(DUK_COMPARE_FLAG_NEGATE == 1); /* Rely on this flag being lowest. */
908 retval = flags & DUK_COMPARE_FLAG_NEGATE;
909 DUK_ASSERT(retval == 0 || retval == 1);
910
911 /* Fast path for fastints */
912 #if defined(DUK_USE_FASTINT)
913 if (DUK_LIKELY(DUK_TVAL_IS_FASTINT(tv_x) && DUK_TVAL_IS_FASTINT(tv_y))) {
914 return duk__compare_fastint(retval,
915 DUK_TVAL_GET_FASTINT(tv_x),
916 DUK_TVAL_GET_FASTINT(tv_y));
917 }
918 #endif /* DUK_USE_FASTINT */
919
920 /* Fast path for numbers (one of which may be a fastint) */
921 #if !defined(DUK_USE_PREFER_SIZE)
922 if (DUK_LIKELY(DUK_TVAL_IS_NUMBER(tv_x) && DUK_TVAL_IS_NUMBER(tv_y))) {
923 return duk__compare_number(retval,
924 DUK_TVAL_GET_NUMBER(tv_x),
925 DUK_TVAL_GET_NUMBER(tv_y));
926 }
927 #endif
928
929 /* Slow path */
930
931 duk_push_tval(thr, tv_x);
932 duk_push_tval(thr, tv_y);
933
934 if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
935 duk_to_primitive(thr, -2, DUK_HINT_NUMBER);
936 duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
937 } else {
938 duk_to_primitive(thr, -1, DUK_HINT_NUMBER);
939 duk_to_primitive(thr, -2, DUK_HINT_NUMBER);
940 }
941
942 /* Note: reuse variables */
943 tv_x = DUK_GET_TVAL_NEGIDX(thr, -2);
944 tv_y = DUK_GET_TVAL_NEGIDX(thr, -1);
945
946 if (DUK_TVAL_IS_STRING(tv_x) && DUK_TVAL_IS_STRING(tv_y)) {
947 duk_hstring *h1 = DUK_TVAL_GET_STRING(tv_x);
948 duk_hstring *h2 = DUK_TVAL_GET_STRING(tv_y);
949 DUK_ASSERT(h1 != NULL);
950 DUK_ASSERT(h2 != NULL);
951
952 if (DUK_LIKELY(!DUK_HSTRING_HAS_SYMBOL(h1) && !DUK_HSTRING_HAS_SYMBOL(h2))) {
953 rc = duk_js_string_compare(h1, h2);
954 duk_pop_2_unsafe(thr);
955 if (rc < 0) {
956 return retval ^ 1;
957 } else {
958 return retval;
959 }
960 }
961
962 /* One or both are Symbols: fall through to handle in the
963 * generic path. Concretely, ToNumber() will fail.
964 */
965 }
966
967 /* Ordering should not matter (E5 Section 11.8.5, step 3.a). */
968 #if 0
969 if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
970 d1 = duk_to_number_m2(thr);
971 d2 = duk_to_number_m1(thr);
972 } else {
973 d2 = duk_to_number_m1(thr);
974 d1 = duk_to_number_m2(thr);
975 }
976 #endif
977 d1 = duk_to_number_m2(thr);
978 d2 = duk_to_number_m1(thr);
979
980 /* We want to duk_pop_2_unsafe(thr); because the values are numbers
981 * no decref check is needed.
982 */
983 #if defined(DUK_USE_PREFER_SIZE)
984 duk_pop_2_nodecref_unsafe(thr);
985 #else
986 DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -2)));
987 DUK_ASSERT(!DUK_TVAL_NEEDS_REFCOUNT_UPDATE(duk_get_tval(thr, -1)));
988 DUK_ASSERT(duk_get_top(thr) >= 2);
989 thr->valstack_top -= 2;
990 tv_x = thr->valstack_top;
991 tv_y = tv_x + 1;
992 DUK_TVAL_SET_UNDEFINED(tv_x); /* Value stack policy */
993 DUK_TVAL_SET_UNDEFINED(tv_y);
994 #endif
995
996 return duk__compare_number(retval, d1, d2);
997 }
998
999 /*
1000 * instanceof
1001 */
1002
1003 /*
1004 * ES2015 Section 7.3.19 describes the OrdinaryHasInstance() algorithm
1005 * which covers both bound and non-bound functions; in effect the algorithm
1006 * includes E5 Sections 11.8.6, 15.3.5.3, and 15.3.4.5.3.
1007 *
1008 * ES2015 Section 12.9.4 describes the instanceof operator which first
1009 * checks @@hasInstance well-known symbol and falls back to
1010 * OrdinaryHasInstance().
1011 *
1012 * Limited Proxy support: don't support 'getPrototypeOf' trap but
1013 * continue lookup in Proxy target if the value is a Proxy.
1014 */
1015
duk__js_instanceof_helper(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y,duk_bool_t skip_sym_check)1016 DUK_LOCAL duk_bool_t duk__js_instanceof_helper(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y, duk_bool_t skip_sym_check) {
1017 duk_hobject *func;
1018 duk_hobject *val;
1019 duk_hobject *proto;
1020 duk_tval *tv;
1021 duk_bool_t skip_first;
1022 duk_uint_t sanity;
1023
1024 /*
1025 * Get the values onto the stack first. It would be possible to cover
1026 * some normal cases without resorting to the value stack.
1027 *
1028 * The right hand side could be a light function (as they generally
1029 * behave like objects). Light functions never have a 'prototype'
1030 * property so E5.1 Section 15.3.5.3 step 3 always throws a TypeError.
1031 * Using duk_require_hobject() is thus correct (except for error msg).
1032 */
1033
1034 duk_push_tval(thr, tv_x);
1035 duk_push_tval(thr, tv_y);
1036 func = duk_require_hobject(thr, -1);
1037 DUK_ASSERT(func != NULL);
1038
1039 #if defined(DUK_USE_SYMBOL_BUILTIN)
1040 /*
1041 * @@hasInstance check, ES2015 Section 12.9.4, Steps 2-4.
1042 */
1043 if (!skip_sym_check) {
1044 if (duk_get_method_stridx(thr, -1, DUK_STRIDX_WELLKNOWN_SYMBOL_HAS_INSTANCE)) {
1045 /* [ ... lhs rhs func ] */
1046 duk_insert(thr, -3); /* -> [ ... func lhs rhs ] */
1047 duk_swap_top(thr, -2); /* -> [ ... func rhs(this) lhs ] */
1048 duk_call_method(thr, 1);
1049 return duk_to_boolean_top_pop(thr);
1050 }
1051 }
1052 #else
1053 DUK_UNREF(skip_sym_check);
1054 #endif
1055
1056 /*
1057 * For bound objects, [[HasInstance]] just calls the target function
1058 * [[HasInstance]]. If that is again a bound object, repeat until
1059 * we find a non-bound Function object.
1060 *
1061 * The bound function chain is now "collapsed" so there can be only
1062 * one bound function in the chain.
1063 */
1064
1065 if (!DUK_HOBJECT_IS_CALLABLE(func)) {
1066 /*
1067 * Note: of native ECMAScript objects, only Function instances
1068 * have a [[HasInstance]] internal property. Custom objects might
1069 * also have it, but not in current implementation.
1070 *
1071 * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF?
1072 */
1073 goto error_invalid_rval;
1074 }
1075
1076 if (DUK_HOBJECT_HAS_BOUNDFUNC(func)) {
1077 duk_push_tval(thr, &((duk_hboundfunc *) (void *) func)->target);
1078 duk_replace(thr, -2);
1079 func = duk_require_hobject(thr, -1); /* lightfunc throws */
1080
1081 /* Rely on Function.prototype.bind() never creating bound
1082 * functions whose target is not proper.
1083 */
1084 DUK_ASSERT(func != NULL);
1085 DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
1086 }
1087
1088 /*
1089 * 'func' is now a non-bound object which supports [[HasInstance]]
1090 * (which here just means DUK_HOBJECT_FLAG_CALLABLE). Move on
1091 * to execute E5 Section 15.3.5.3.
1092 */
1093
1094 DUK_ASSERT(func != NULL);
1095 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUNDFUNC(func));
1096 DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
1097
1098 /* [ ... lval rval(func) ] */
1099
1100 /* For lightfuncs, buffers, and pointers start the comparison directly
1101 * from the virtual prototype object.
1102 */
1103 skip_first = 0;
1104 tv = DUK_GET_TVAL_NEGIDX(thr, -2);
1105 switch (DUK_TVAL_GET_TAG(tv)) {
1106 case DUK_TAG_LIGHTFUNC:
1107 val = thr->builtins[DUK_BIDX_FUNCTION_PROTOTYPE];
1108 DUK_ASSERT(val != NULL);
1109 break;
1110 case DUK_TAG_BUFFER:
1111 val = thr->builtins[DUK_BIDX_UINT8ARRAY_PROTOTYPE];
1112 DUK_ASSERT(val != NULL);
1113 break;
1114 case DUK_TAG_POINTER:
1115 val = thr->builtins[DUK_BIDX_POINTER_PROTOTYPE];
1116 DUK_ASSERT(val != NULL);
1117 break;
1118 case DUK_TAG_OBJECT:
1119 skip_first = 1; /* Ignore object itself on first round. */
1120 val = DUK_TVAL_GET_OBJECT(tv);
1121 DUK_ASSERT(val != NULL);
1122 break;
1123 default:
1124 goto pop2_and_false;
1125 }
1126 DUK_ASSERT(val != NULL); /* Loop doesn't actually rely on this. */
1127
1128 /* Look up .prototype of rval. Leave it on the value stack in case it
1129 * has been virtualized (e.g. getter, Proxy trap).
1130 */
1131 duk_get_prop_stridx_short(thr, -1, DUK_STRIDX_PROTOTYPE); /* -> [ ... lval rval rval.prototype ] */
1132 #if defined(DUK_USE_VERBOSE_ERRORS)
1133 proto = duk_get_hobject(thr, -1);
1134 if (proto == NULL) {
1135 goto error_invalid_rval_noproto;
1136 }
1137 #else
1138 proto = duk_require_hobject(thr, -1);
1139 #endif
1140
1141 sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
1142 do {
1143 /*
1144 * Note: prototype chain is followed BEFORE first comparison. This
1145 * means that the instanceof lval is never itself compared to the
1146 * rval.prototype property. This is apparently intentional, see E5
1147 * Section 15.3.5.3, step 4.a.
1148 *
1149 * Also note:
1150 *
1151 * js> (function() {}) instanceof Function
1152 * true
1153 * js> Function instanceof Function
1154 * true
1155 *
1156 * For the latter, h_proto will be Function.prototype, which is the
1157 * built-in Function prototype. Because Function.[[Prototype]] is
1158 * also the built-in Function prototype, the result is true.
1159 */
1160
1161 if (!val) {
1162 goto pop3_and_false;
1163 }
1164
1165 DUK_ASSERT(val != NULL);
1166 #if defined(DUK_USE_ES6_PROXY)
1167 val = duk_hobject_resolve_proxy_target(val);
1168 #endif
1169
1170 if (skip_first) {
1171 skip_first = 0;
1172 } else if (val == proto) {
1173 goto pop3_and_true;
1174 }
1175
1176 DUK_ASSERT(val != NULL);
1177 val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val);
1178 } while (--sanity > 0);
1179
1180 DUK_ASSERT(sanity == 0);
1181 DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
1182 DUK_WO_NORETURN(return 0;);
1183
1184 pop2_and_false:
1185 duk_pop_2_unsafe(thr);
1186 return 0;
1187
1188 pop3_and_false:
1189 duk_pop_3_unsafe(thr);
1190 return 0;
1191
1192 pop3_and_true:
1193 duk_pop_3_unsafe(thr);
1194 return 1;
1195
1196 error_invalid_rval:
1197 DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL);
1198 DUK_WO_NORETURN(return 0;);
1199
1200 #if defined(DUK_USE_VERBOSE_ERRORS)
1201 error_invalid_rval_noproto:
1202 DUK_ERROR_TYPE(thr, DUK_STR_INVALID_INSTANCEOF_RVAL_NOPROTO);
1203 DUK_WO_NORETURN(return 0;);
1204 #endif
1205 }
1206
1207 #if defined(DUK_USE_SYMBOL_BUILTIN)
duk_js_instanceof_ordinary(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y)1208 DUK_INTERNAL duk_bool_t duk_js_instanceof_ordinary(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
1209 return duk__js_instanceof_helper(thr, tv_x, tv_y, 1 /*skip_sym_check*/);
1210 }
1211 #endif
1212
duk_js_instanceof(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y)1213 DUK_INTERNAL duk_bool_t duk_js_instanceof(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
1214 return duk__js_instanceof_helper(thr, tv_x, tv_y, 0 /*skip_sym_check*/);
1215 }
1216
1217 /*
1218 * in
1219 */
1220
1221 /*
1222 * E5 Sections 11.8.7, 8.12.6.
1223 *
1224 * Basically just a property existence check using [[HasProperty]].
1225 */
1226
duk_js_in(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y)1227 DUK_INTERNAL duk_bool_t duk_js_in(duk_hthread *thr, duk_tval *tv_x, duk_tval *tv_y) {
1228 duk_bool_t retval;
1229
1230 /*
1231 * Get the values onto the stack first. It would be possible to cover
1232 * some normal cases without resorting to the value stack (e.g. if
1233 * lval is already a string).
1234 */
1235
1236 /* XXX: The ES5/5.1/6 specifications require that the key in 'key in obj'
1237 * must be string coerced before the internal HasProperty() algorithm is
1238 * invoked. A fast path skipping coercion could be safely implemented for
1239 * numbers (as number-to-string coercion has no side effects). For ES2015
1240 * proxy behavior, the trap 'key' argument must be in a string coerced
1241 * form (which is a shame).
1242 */
1243
1244 /* TypeError if rval is not an object or object like (e.g. lightfunc
1245 * or plain buffer).
1246 */
1247 duk_push_tval(thr, tv_x);
1248 duk_push_tval(thr, tv_y);
1249 duk_require_type_mask(thr, -1, DUK_TYPE_MASK_OBJECT | DUK_TYPE_MASK_LIGHTFUNC | DUK_TYPE_MASK_BUFFER);
1250
1251 (void) duk_to_property_key_hstring(thr, -2);
1252
1253 retval = duk_hobject_hasprop(thr,
1254 DUK_GET_TVAL_NEGIDX(thr, -1),
1255 DUK_GET_TVAL_NEGIDX(thr, -2));
1256
1257 duk_pop_2_unsafe(thr);
1258 return retval;
1259 }
1260
1261 /*
1262 * typeof
1263 *
1264 * E5 Section 11.4.3.
1265 *
1266 * Very straightforward. The only question is what to return for our
1267 * non-standard tag / object types.
1268 *
1269 * There is an unfortunate string constant define naming problem with
1270 * typeof return values for e.g. "Object" and "object"; careful with
1271 * the built-in string defines. The LC_XXX defines are used for the
1272 * lowercase variants now.
1273 */
1274
duk_js_typeof_stridx(duk_tval * tv_x)1275 DUK_INTERNAL duk_small_uint_t duk_js_typeof_stridx(duk_tval *tv_x) {
1276 duk_small_uint_t stridx = 0;
1277
1278 switch (DUK_TVAL_GET_TAG(tv_x)) {
1279 case DUK_TAG_UNDEFINED: {
1280 stridx = DUK_STRIDX_LC_UNDEFINED;
1281 break;
1282 }
1283 case DUK_TAG_NULL: {
1284 /* Note: not a typo, "object" is returned for a null value. */
1285 stridx = DUK_STRIDX_LC_OBJECT;
1286 break;
1287 }
1288 case DUK_TAG_BOOLEAN: {
1289 stridx = DUK_STRIDX_LC_BOOLEAN;
1290 break;
1291 }
1292 case DUK_TAG_POINTER: {
1293 /* Implementation specific. */
1294 stridx = DUK_STRIDX_LC_POINTER;
1295 break;
1296 }
1297 case DUK_TAG_STRING: {
1298 duk_hstring *str;
1299
1300 /* All internal keys are identified as Symbols. */
1301 str = DUK_TVAL_GET_STRING(tv_x);
1302 DUK_ASSERT(str != NULL);
1303 if (DUK_UNLIKELY(DUK_HSTRING_HAS_SYMBOL(str))) {
1304 stridx = DUK_STRIDX_LC_SYMBOL;
1305 } else {
1306 stridx = DUK_STRIDX_LC_STRING;
1307 }
1308 break;
1309 }
1310 case DUK_TAG_OBJECT: {
1311 duk_hobject *obj = DUK_TVAL_GET_OBJECT(tv_x);
1312 DUK_ASSERT(obj != NULL);
1313 if (DUK_HOBJECT_IS_CALLABLE(obj)) {
1314 stridx = DUK_STRIDX_LC_FUNCTION;
1315 } else {
1316 stridx = DUK_STRIDX_LC_OBJECT;
1317 }
1318 break;
1319 }
1320 case DUK_TAG_BUFFER: {
1321 /* Implementation specific. In Duktape 1.x this would be
1322 * 'buffer', in Duktape 2.x changed to 'object' because plain
1323 * buffers now mimic Uint8Array objects.
1324 */
1325 stridx = DUK_STRIDX_LC_OBJECT;
1326 break;
1327 }
1328 case DUK_TAG_LIGHTFUNC: {
1329 stridx = DUK_STRIDX_LC_FUNCTION;
1330 break;
1331 }
1332 #if defined(DUK_USE_FASTINT)
1333 case DUK_TAG_FASTINT:
1334 #endif
1335 default: {
1336 /* number */
1337 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x));
1338 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));
1339 stridx = DUK_STRIDX_LC_NUMBER;
1340 break;
1341 }
1342 }
1343
1344 DUK_ASSERT_STRIDX_VALID(stridx);
1345 return stridx;
1346 }
1347
1348 /*
1349 * IsArray()
1350 */
1351
duk_js_isarray_hobject(duk_hobject * h)1352 DUK_INTERNAL duk_bool_t duk_js_isarray_hobject(duk_hobject *h) {
1353 DUK_ASSERT(h != NULL);
1354 #if defined(DUK_USE_ES6_PROXY)
1355 h = duk_hobject_resolve_proxy_target(h);
1356 #endif
1357 return (DUK_HOBJECT_GET_CLASS_NUMBER(h) == DUK_HOBJECT_CLASS_ARRAY ? 1 : 0);
1358 }
1359
duk_js_isarray(duk_tval * tv)1360 DUK_INTERNAL duk_bool_t duk_js_isarray(duk_tval *tv) {
1361 DUK_ASSERT(tv != NULL);
1362 if (DUK_TVAL_IS_OBJECT(tv)) {
1363 return duk_js_isarray_hobject(DUK_TVAL_GET_OBJECT(tv));
1364 }
1365 return 0;
1366 }
1367
1368 /*
1369 * Array index and length
1370 *
1371 * Array index: E5 Section 15.4
1372 * Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write)
1373 */
1374
1375 /* Compure array index from string context, or return a "not array index"
1376 * indicator.
1377 */
duk_js_to_arrayindex_string(const duk_uint8_t * str,duk_uint32_t blen)1378 DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string(const duk_uint8_t *str, duk_uint32_t blen) {
1379 duk_uarridx_t res;
1380
1381 /* Only strings with byte length 1-10 can be 32-bit array indices.
1382 * Leading zeroes (except '0' alone), plus/minus signs are not allowed.
1383 * We could do a lot of prechecks here, but since most strings won't
1384 * start with any digits, it's simpler to just parse the number and
1385 * fail quickly.
1386 */
1387
1388 res = 0;
1389 if (blen == 0) {
1390 goto parse_fail;
1391 }
1392 do {
1393 duk_uarridx_t dig;
1394 dig = (duk_uarridx_t) (*str++) - DUK_ASC_0;
1395
1396 if (dig <= 9U) {
1397 /* Careful overflow handling. When multiplying by 10:
1398 * - 0x19999998 x 10 = 0xfffffff0: no overflow, and adding
1399 * 0...9 is safe.
1400 * - 0x19999999 x 10 = 0xfffffffa: no overflow, adding
1401 * 0...5 is safe, 6...9 overflows.
1402 * - 0x1999999a x 10 = 0x100000004: always overflow.
1403 */
1404 if (DUK_UNLIKELY(res >= 0x19999999UL)) {
1405 if (res >= 0x1999999aUL) {
1406 /* Always overflow. */
1407 goto parse_fail;
1408 }
1409 DUK_ASSERT(res == 0x19999999UL);
1410 if (dig >= 6U) {
1411 goto parse_fail;
1412 }
1413 res = 0xfffffffaUL + dig;
1414 DUK_ASSERT(res >= 0xfffffffaUL);
1415 DUK_ASSERT_DISABLE(res <= 0xffffffffUL); /* range */
1416 } else {
1417 res = res * 10U + dig;
1418 if (DUK_UNLIKELY(res == 0)) {
1419 /* If 'res' is 0, previous 'res' must
1420 * have been 0 and we scanned in a zero.
1421 * This is only allowed if blen == 1,
1422 * i.e. the exact string '0'.
1423 */
1424 if (blen == (duk_uint32_t) 1) {
1425 return 0;
1426 }
1427 goto parse_fail;
1428 }
1429 }
1430 } else {
1431 /* Because 'dig' is unsigned, catches both values
1432 * above '9' and below '0'.
1433 */
1434 goto parse_fail;
1435 }
1436 } while (--blen > 0);
1437
1438 return res;
1439
1440 parse_fail:
1441 return DUK_HSTRING_NO_ARRAY_INDEX;
1442 }
1443
1444 #if !defined(DUK_USE_HSTRING_ARRIDX)
1445 /* Get array index for a string which is known to be an array index. This helper
1446 * is needed when duk_hstring doesn't concretely store the array index, but strings
1447 * are flagged as array indices at intern time.
1448 */
duk_js_to_arrayindex_hstring_fast_known(duk_hstring * h)1449 DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast_known(duk_hstring *h) {
1450 const duk_uint8_t *p;
1451 duk_uarridx_t res;
1452 duk_uint8_t t;
1453
1454 DUK_ASSERT(h != NULL);
1455 DUK_ASSERT(DUK_HSTRING_HAS_ARRIDX(h));
1456
1457 p = DUK_HSTRING_GET_DATA(h);
1458 res = 0;
1459 for (;;) {
1460 t = *p++;
1461 if (DUK_UNLIKELY(t == 0)) {
1462 /* Scanning to NUL is always safe for interned strings. */
1463 break;
1464 }
1465 DUK_ASSERT(t >= (duk_uint8_t) DUK_ASC_0 && t <= (duk_uint8_t) DUK_ASC_9);
1466 res = res * 10U + (duk_uarridx_t) t - (duk_uarridx_t) DUK_ASC_0;
1467 }
1468 return res;
1469 }
1470
duk_js_to_arrayindex_hstring_fast(duk_hstring * h)1471 DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_hstring_fast(duk_hstring *h) {
1472 DUK_ASSERT(h != NULL);
1473 if (!DUK_HSTRING_HAS_ARRIDX(h)) {
1474 return DUK_HSTRING_NO_ARRAY_INDEX;
1475 }
1476 return duk_js_to_arrayindex_hstring_fast_known(h);
1477 }
1478 #endif /* DUK_USE_HSTRING_ARRIDX */
1479