1 /*
2 * Ecmascript specification algorithm and conversion helpers.
3 *
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,
10 * etc.)
11 *
12 * The operation arguments depend on what is required to implement
13 * the operation:
14 *
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).
19 *
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.
24 *
25 * - For operations with potential side effects, arguments can be
26 * taken in several ways:
27 *
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
31 * necessary
32 *
33 * b) as duk_tval values
34 *
35 * c) implicitly on value stack top
36 *
37 * d) as indices to the value stack
38 *
39 * Future work:
40 *
41 * - Argument styles may not be the most sensible in every case now.
42 *
43 * - In-place coercions might be useful for several operations, if
44 * in-place coercion is OK for the bytecode executor and the API.
45 */
46
47 #include "duk_internal.h"
48
49 /*
50 * [[DefaultValue]] (E5 Section 8.12.8)
51 *
52 * ==> implemented in the API.
53 */
54
55 /*
56 * ToPrimitive() (E5 Section 9.1)
57 *
58 * ==> implemented in the API.
59 */
60
61 /*
62 * ToBoolean() (E5 Section 9.2)
63 */
64
duk_js_toboolean(duk_tval * tv)65 DUK_INTERNAL duk_bool_t duk_js_toboolean(duk_tval *tv) {
66 switch (DUK_TVAL_GET_TAG(tv)) {
67 case DUK_TAG_UNDEFINED:
68 case DUK_TAG_NULL:
69 return 0;
70 case DUK_TAG_BOOLEAN:
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);
76 }
77 case DUK_TAG_OBJECT: {
78 return 1;
79 }
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);
85 }
86 case DUK_TAG_POINTER: {
87 void *p = DUK_TVAL_GET_POINTER(tv);
88 return (p != NULL ? 1 : 0);
89 }
90 case DUK_TAG_LIGHTFUNC: {
91 return 1;
92 }
93 #if defined(DUK_USE_FASTINT)
94 case DUK_TAG_FASTINT:
95 if (DUK_TVAL_GET_FASTINT(tv) != 0) {
96 return 1;
97 } else {
98 return 0;
99 }
100 #endif
101 default: {
102 /* number */
103 duk_double_t d;
104 int c;
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) {
110 return 0;
111 } else {
112 return 1;
113 }
114 }
115 }
116 DUK_UNREACHABLE();
117 }
118
119 /*
120 * ToNumber() (E5 Section 9.3)
121 *
122 * Value to convert must be on stack top, and is popped before exit.
123 *
124 * See: http://www.cs.indiana.edu/~burger/FP-Printing-PLDI96.pdf
125 * http://www.cs.indiana.edu/~burger/fp/index.html
126 *
127 * Notes on the conversion:
128 *
129 * - There are specific requirements on the accuracy of the conversion
130 * through a "Mathematical Value" (MV), so this conversion is not
131 * trivial.
132 *
133 * - Quick rejects (e.g. based on first char) are difficult because
134 * the grammar allows leading and trailing white space.
135 *
136 * - Quick reject based on string length is difficult even after
137 * accounting for white space; there may be arbitrarily many
138 * decimal digits.
139 *
140 * - Standard grammar allows decimal values ("123"), hex values
141 * ("0x123") and infinities
142 *
143 * - Unlike source code literals, ToNumber() coerces empty strings
144 * and strings with only whitespace to zero (not NaN).
145 */
146
147 /* E5 Section 9.3.1 */
duk__tonumber_string_raw(duk_hthread * thr)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;
151 duk_double_t d;
152
153 /* Quite lenient, e.g. allow empty as zero, but don't allow trailing
154 * garbage.
155 */
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;
167
168 duk_numconv_parse(ctx, 10 /*radix*/, s2n_flags);
169 d = duk_get_number(ctx, -1);
170 duk_pop(ctx);
171
172 return d;
173 }
174
duk_js_tonumber(duk_hthread * thr,duk_tval * tv)175 DUK_INTERNAL duk_double_t duk_js_tonumber(duk_hthread *thr, duk_tval *tv) {
176 duk_context *ctx = (duk_hthread *) thr;
177
178 DUK_ASSERT(thr != NULL);
179 DUK_ASSERT(tv != NULL);
180
181 switch (DUK_TVAL_GET_TAG(tv)) {
182 case DUK_TAG_UNDEFINED: {
183 /* return a specific NaN (although not strictly necessary) */
184 duk_double_union du;
185 DUK_DBLUNION_SET_NAN(&du);
186 DUK_ASSERT(DUK_DBLUNION_IS_NORMALIZED(&du));
187 return du.d;
188 }
189 case DUK_TAG_NULL: {
190 /* +0.0 */
191 return 0.0;
192 }
193 case DUK_TAG_BOOLEAN: {
194 if (DUK_TVAL_IS_BOOLEAN_TRUE(tv)) {
195 return 1.0;
196 }
197 return 0.0;
198 }
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);
203 }
204 case DUK_TAG_OBJECT: {
205 /* Note: ToPrimitive(object,hint) == [[DefaultValue]](object,hint),
206 * so use [[DefaultValue]] directly.
207 */
208 duk_double_t d;
209 duk_push_tval(ctx, tv);
210 duk_to_defaultvalue(ctx, -1, DUK_HINT_NUMBER); /* 'tv' becomes invalid */
211
212 /* recursive call for a primitive value (guaranteed not to cause second
213 * recursion).
214 */
215 d = duk_js_tonumber(thr, duk_require_tval(ctx, -1));
216
217 duk_pop(ctx);
218 return d;
219 }
220 case DUK_TAG_BUFFER: {
221 /* Coerce like a string. This makes sense because addition also treats
222 * buffers like strings.
223 */
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);
228 }
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);
233 }
234 case DUK_TAG_LIGHTFUNC: {
235 /* +(function(){}) -> NaN */
236 return DUK_DOUBLE_NAN;
237 }
238 #if defined(DUK_USE_FASTINT)
239 case DUK_TAG_FASTINT:
240 return (duk_double_t) DUK_TVAL_GET_FASTINT(tv);
241 #endif
242 default: {
243 /* number */
244 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv));
245 DUK_ASSERT(DUK_TVAL_IS_DOUBLE(tv));
246 return DUK_TVAL_GET_DOUBLE(tv);
247 }
248 }
249
250 DUK_UNREACHABLE();
251 }
252
253 /*
254 * ToInteger() (E5 Section 9.4)
255 */
256
257 /* exposed, used by e.g. duk_bi_date.c */
duk_js_tointeger_number(duk_double_t x)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);
260
261 if (c == DUK_FP_NAN) {
262 return 0.0;
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).
266 */
267 return x;
268 } else {
269 duk_small_int_t s = (duk_small_int_t) DUK_SIGNBIT(x);
270 x = DUK_FLOOR(DUK_FABS(x)); /* truncate towards zero */
271 if (s) {
272 x = -x;
273 }
274 return x;
275 }
276 }
277
duk_js_tointeger(duk_hthread * thr,duk_tval * tv)278 DUK_INTERNAL duk_double_t duk_js_tointeger(duk_hthread *thr, duk_tval *tv) {
279 /* XXX: fastint */
280 duk_double_t d = duk_js_tonumber(thr, tv); /* invalidates tv */
281 return duk_js_tointeger_number(d);
282 }
283
284 /*
285 * ToInt32(), ToUint32(), ToUint16() (E5 Sections 9.5, 9.6, 9.7)
286 */
287
288 /* combined algorithm matching E5 Sections 9.5 and 9.6 */
duk__toint32_touint32_helper(duk_double_t x,duk_bool_t is_toint32)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);
291 duk_small_int_t s;
292
293 if (c == DUK_FP_NAN || c == DUK_FP_ZERO || c == DUK_FP_INFINITE) {
294 return 0.0;
295 }
296
297
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));
301 if (s) {
302 x = -x;
303 }
304
305 /* NOTE: fmod(x) result sign is same as sign of x, which
306 * differs from what Javascript wants (see Section 9.6).
307 */
308
309 x = DUK_FMOD(x, DUK_DOUBLE_2TO32); /* -> x in ]-2**32, 2**32[ */
310
311 if (x < 0.0) {
312 x += DUK_DOUBLE_2TO32;
313 }
314 /* -> x in [0, 2**32[ */
315
316 if (is_toint32) {
317 if (x >= DUK_DOUBLE_2TO31) {
318 /* x in [2**31, 2**32[ */
319
320 x -= DUK_DOUBLE_2TO32; /* -> x in [-2**31,2**31[ */
321 }
322 }
323
324 return x;
325 }
326
duk_js_toint32(duk_hthread * thr,duk_tval * tv)327 DUK_INTERNAL duk_int32_t duk_js_toint32(duk_hthread *thr, duk_tval *tv) {
328 duk_double_t d;
329
330 #if defined(DUK_USE_FASTINT)
331 if (DUK_TVAL_IS_FASTINT(tv)) {
332 return DUK_TVAL_GET_FASTINT_I32(tv);
333 }
334 #endif
335
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;
342 }
343
344
duk_js_touint32(duk_hthread * thr,duk_tval * tv)345 DUK_INTERNAL duk_uint32_t duk_js_touint32(duk_hthread *thr, duk_tval *tv) {
346 duk_double_t d;
347
348 #if defined(DUK_USE_FASTINT)
349 if (DUK_TVAL_IS_FASTINT(tv)) {
350 return DUK_TVAL_GET_FASTINT_U32(tv);
351 }
352 #endif
353
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;
360
361 }
362
duk_js_touint16(duk_hthread * thr,duk_tval * tv)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);
366 }
367
368 /*
369 * ToString() (E5 Section 9.8)
370 *
371 * ==> implemented in the API.
372 */
373
374 /*
375 * ToObject() (E5 Section 9.9)
376 *
377 * ==> implemented in the API.
378 */
379
380 /*
381 * CheckObjectCoercible() (E5 Section 9.10)
382 *
383 * Note: no API equivalent now.
384 */
385
386 #if 0 /* unused */
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);
389
390 /* Note: this must match ToObject() behavior */
391
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");
397 }
398 }
399 #endif
400
401 /*
402 * IsCallable() (E5 Section 9.11)
403 *
404 * XXX: API equivalent is a separate implementation now, and this has
405 * currently no callers.
406 */
407
408 #if 0 /* unused */
409 DUK_INTERNAL duk_bool_t duk_js_iscallable(duk_tval *tv_x) {
410 duk_hobject *obj;
411
412 if (!DUK_TVAL_IS_OBJECT(tv_x)) {
413 return 0;
414 }
415 obj = DUK_TVAL_GET_OBJECT(tv_x);
416 DUK_ASSERT(obj != NULL);
417
418 return DUK_HOBJECT_IS_CALLABLE(obj);
419 }
420 #endif
421
422 /*
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.
425 *
426 * Future work notes:
427 *
428 * - Current implementation (and spec definition) has recursion; this should
429 * be fixed if possible.
430 *
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.
433 */
434
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)
438 */
439
duk__js_equals_number(duk_double_t x,duk_double_t y)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) {
446 return 0;
447 }
448 if (cx == DUK_FP_ZERO && cy == DUK_FP_ZERO) {
449 return 1;
450 }
451 if (x == y) {
452 return 1;
453 }
454 return 0;
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).
461 */
462 if (x == y) {
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);
466 return 1;
467 } else {
468 /* IEEE requires that zeros compare the same regardless
469 * of their signed, so if both x and y are zeroes, they
470 * are caught above.
471 */
472 DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));
473 return 0;
474 }
475 #endif /* DUK_USE_PARANOID_MATH */
476 }
477
duk__js_samevalue_number(duk_double_t x,duk_double_t y)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);
482
483 if (cx == DUK_FP_NAN && cy == DUK_FP_NAN) {
484 /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
485 return 1;
486 }
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:
490 *
491 * signbit(x) == signbit(y)
492 */
493 duk_small_int_t sx = (DUK_SIGNBIT(x) ? 1 : 0);
494 duk_small_int_t sy = (DUK_SIGNBIT(y) ? 1 : 0);
495 return (sx == sy);
496 }
497
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
502 */
503
504 return (x == y);
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);
508
509 if (x == 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);
513
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:
518 *
519 * signbit(x) == signbit(y)
520 */
521 duk_small_int_t sx = (DUK_SIGNBIT(x) ? 1 : 0);
522 duk_small_int_t sy = (DUK_SIGNBIT(y) ? 1 : 0);
523 return (sx == sy);
524 }
525 return 1;
526 } else {
527 /* IEEE requires that zeros compare the same regardless
528 * of their signed, so if both x and y are zeroes, they
529 * are caught above.
530 */
531 DUK_ASSERT(!(DUK_FPCLASSIFY(x) == DUK_FP_ZERO && DUK_FPCLASSIFY(y) == DUK_FP_ZERO));
532
533 /* Difference to non-strict/strict comparison is that NaNs compare
534 * equal and signed zero signs matter.
535 */
536 if (DUK_UNLIKELY(cx == DUK_FP_NAN && cy == DUK_FP_NAN)) {
537 /* SameValue(NaN, NaN) = true, regardless of NaN sign or extra bits */
538 return 1;
539 }
540 return 0;
541 }
542 #endif /* DUK_USE_PARANOID_MATH */
543 }
544
duk_js_equals_helper(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y,duk_small_int_t flags)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;
547 duk_tval *tv_tmp;
548
549 /* If flags != 0 (strict or SameValue), thr can be NULL. For loose
550 * equals comparison it must be != NULL.
551 */
552 DUK_ASSERT(flags != 0 || thr != NULL);
553
554 /*
555 * Same type?
556 *
557 * Note: since number values have no explicit tag in the 8-byte
558 * representation, need the awkward if + switch.
559 */
560
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)) {
564 return 1;
565 } else {
566 return 0;
567 }
568 }
569 else
570 #endif
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)) {
574 /* SameValue */
575 return duk__js_samevalue_number(DUK_TVAL_GET_NUMBER(tv_x),
576 DUK_TVAL_GET_NUMBER(tv_y));
577 } else {
578 /* equals and strict equals */
579 return duk__js_equals_number(DUK_TVAL_GET_NUMBER(tv_x),
580 DUK_TVAL_GET_NUMBER(tv_y));
581 }
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:
585 case DUK_TAG_NULL: {
586 return 1;
587 }
588 case DUK_TAG_BOOLEAN: {
589 return DUK_TVAL_GET_BOOLEAN(tv_x) == DUK_TVAL_GET_BOOLEAN(tv_y);
590 }
591 case DUK_TAG_POINTER: {
592 return DUK_TVAL_GET_POINTER(tv_x) == DUK_TVAL_GET_POINTER(tv_y);
593 }
594 case DUK_TAG_STRING:
595 case DUK_TAG_OBJECT: {
596 /* heap pointer comparison suffices */
597 return DUK_TVAL_GET_HEAPHDR(tv_x) == DUK_TVAL_GET_HEAPHDR(tv_y);
598 }
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);
603 } else {
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);
609 void *buf_x;
610 void *buf_y;
611 if (len_x != len_y) {
612 return 0;
613 }
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.
618 */
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;
623 }
624 }
625 case DUK_TAG_LIGHTFUNC: {
626 /* At least 'magic' has a significant impact on function
627 * identity.
628 */
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;
633
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;
637 }
638 #if defined(DUK_USE_FASTINT)
639 case DUK_TAG_FASTINT:
640 #endif
641 default: {
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));
646 DUK_UNREACHABLE();
647 return 0;
648 }
649 }
650 }
651
652 if ((flags & (DUK_EQUALS_FLAG_STRICT | DUK_EQUALS_FLAG_SAMEVALUE)) != 0) {
653 return 0;
654 }
655
656 DUK_ASSERT(flags == 0); /* non-strict equality from here on */
657
658 /*
659 * Types are different; various cases for non-strict comparison
660 *
661 * Since comparison is symmetric, we use a "swap trick" to reduce
662 * code size.
663 */
664
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))) {
668 return 1;
669 }
670
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 */
674 tv_tmp = tv_x;
675 tv_x = tv_y;
676 tv_y = tv_tmp;
677 }
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 */
680 duk_double_t d1, d2;
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);
686 duk_pop(ctx);
687 return duk__js_equals_number(d1, d2);
688 }
689
690 /* Buffer/string -> compare contents. */
691 if (DUK_TVAL_IS_BUFFER(tv_x) && DUK_TVAL_IS_STRING(tv_y)) {
692 tv_tmp = tv_x;
693 tv_x = tv_y;
694 tv_y = tv_tmp;
695 }
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);
701 const void *buf_x;
702 const void *buf_y;
703 if (len_x != len_y) {
704 return 0;
705 }
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.
710 */
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;
715 }
716
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
720 * not special cased.
721 */
722 if (DUK_TVAL_IS_BOOLEAN(tv_x)) {
723 tv_tmp = tv_x;
724 tv_x = tv_y;
725 tv_y = tv_tmp;
726 }
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. */
729 duk_bool_t rc;
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*/);
737 duk_pop_2(ctx);
738 return rc;
739 }
740
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)) {
744 tv_tmp = tv_x;
745 tv_x = tv_y;
746 tv_y = tv_tmp;
747 }
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))) {
750 duk_bool_t rc;
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*/);
758 duk_pop_2(ctx);
759 return rc;
760 }
761
762 /* Nothing worked -> not equal. */
763 return 0;
764 }
765
766 /*
767 * Comparisons (x >= y, x > y, x <= y, x < y)
768 *
769 * E5 Section 11.8.5: implement 'x < y' and then use negate and eval_left_first
770 * flags to get the rest.
771 */
772
773 /* XXX: this should probably just operate on the stack top, because it
774 * needs to push stuff on the stack anyway...
775 */
776
duk_js_data_compare(const duk_uint8_t * buf1,const duk_uint8_t * buf2,duk_size_t len1,duk_size_t len2)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;
779 duk_small_int_t rc;
780
781 prefix_len = (len1 <= len2 ? len1 : len2);
782
783 /* DUK_MEMCMP() is guaranteed to return zero (equal) for zero length
784 * inputs so no zero length check is needed.
785 */
786 rc = DUK_MEMCMP((const void *) buf1,
787 (const void *) buf2,
788 (size_t) prefix_len);
789
790 if (rc < 0) {
791 return -1;
792 } else if (rc > 0) {
793 return 1;
794 }
795
796 /* prefix matches, lengths matter now */
797 if (len1 < len2) {
798 /* e.g. "x" < "xx" */
799 return -1;
800 } else if (len1 > len2) {
801 return 1;
802 }
803
804 return 0;
805 }
806
duk_js_string_compare(duk_hstring * h1,duk_hstring * h2)807 DUK_INTERNAL duk_small_int_t duk_js_string_compare(duk_hstring *h1, duk_hstring *h2) {
808 /*
809 * String comparison (E5 Section 11.8.5, step 4), which
810 * needs to compare codepoint by codepoint.
811 *
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).
816 *
817 * This will not work properly for non-xutf-8 strings, but this
818 * is not an issue for compliance.
819 */
820
821 DUK_ASSERT(h1 != NULL);
822 DUK_ASSERT(h2 != NULL);
823
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));
828 }
829
830 #if 0 /* unused */
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. */
833
834 DUK_ASSERT(h1 != NULL);
835 DUK_ASSERT(h2 != NULL);
836 DUK_UNREF(heap);
837
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));
842 }
843 #endif
844
duk_js_compare_helper(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y,duk_small_int_t flags)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;
847 duk_double_t d1, d2;
848 duk_small_int_t c1, c2;
849 duk_small_int_t s1, s2;
850 duk_small_int_t rc;
851 duk_bool_t retval;
852
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);
858 if (v1 < v2) {
859 /* 'lt is true' */
860 retval = 1;
861 } else {
862 retval = 0;
863 }
864 if (flags & DUK_COMPARE_FLAG_NEGATE) {
865 retval ^= 1;
866 }
867 return retval;
868 }
869 #endif /* DUK_USE_FASTINT */
870
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);
878
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.
882 */
883
884 if (d1 < d2) {
885 /* 'lt is true' */
886 retval = 1;
887 } else {
888 retval = 0;
889 }
890 if (flags & DUK_COMPARE_FLAG_NEGATE) {
891 retval ^= 1;
892 }
893 return retval;
894 }
895 }
896 #endif
897
898 /* Slow path */
899
900 duk_push_tval(ctx, tv_x);
901 duk_push_tval(ctx, tv_y);
902
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);
906 } else {
907 duk_to_primitive(ctx, -1, DUK_HINT_NUMBER);
908 duk_to_primitive(ctx, -2, DUK_HINT_NUMBER);
909 }
910
911 /* Note: reuse variables */
912 tv_x = DUK_GET_TVAL_NEGIDX(ctx, -2);
913 tv_y = DUK_GET_TVAL_NEGIDX(ctx, -1);
914
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);
920
921 rc = duk_js_string_compare(h1, h2);
922 if (rc < 0) {
923 goto lt_true;
924 } else {
925 goto lt_false;
926 }
927 } else {
928 /* Ordering should not matter (E5 Section 11.8.5, step 3.a) but
929 * preserve it just in case.
930 */
931
932 if (flags & DUK_COMPARE_FLAG_EVAL_LEFT_FIRST) {
933 d1 = duk_to_number(ctx, -2);
934 d2 = duk_to_number(ctx, -1);
935 } else {
936 d2 = duk_to_number(ctx, -1);
937 d1 = duk_to_number(ctx, -2);
938 }
939
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);
944
945 if (c1 == DUK_FP_NAN || c2 == DUK_FP_NAN) {
946 goto lt_undefined;
947 }
948
949 if (c1 == DUK_FP_ZERO && c2 == DUK_FP_ZERO) {
950 /* For all combinations: +0 < +0, +0 < -0, -0 < +0, -0 < -0,
951 * steps e, f, and g.
952 */
953 goto lt_false;
954 }
955
956 if (d1 == d2) {
957 goto lt_false;
958 }
959
960 if (c1 == DUK_FP_INFINITE && s1 == 0) {
961 /* x == +Infinity */
962 goto lt_false;
963 }
964
965 if (c2 == DUK_FP_INFINITE && s2 == 0) {
966 /* y == +Infinity */
967 goto lt_true;
968 }
969
970 if (c2 == DUK_FP_INFINITE && s2 != 0) {
971 /* y == -Infinity */
972 goto lt_false;
973 }
974
975 if (c1 == DUK_FP_INFINITE && s1 != 0) {
976 /* x == -Infinity */
977 goto lt_true;
978 }
979
980 if (d1 < d2) {
981 goto lt_true;
982 }
983
984 goto lt_false;
985 }
986
987 lt_undefined:
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.
990 */
991 retval = 0;
992 goto cleanup;
993
994 lt_true:
995 if (flags & DUK_COMPARE_FLAG_NEGATE) {
996 retval = 0;
997 goto cleanup;
998 } else {
999 retval = 1;
1000 goto cleanup;
1001 }
1002 /* never here */
1003
1004 lt_false:
1005 if (flags & DUK_COMPARE_FLAG_NEGATE) {
1006 retval = 1;
1007 goto cleanup;
1008 } else {
1009 retval = 0;
1010 goto cleanup;
1011 }
1012 /* never here */
1013
1014 cleanup:
1015 duk_pop_2(ctx);
1016 return retval;
1017 }
1018
1019 /*
1020 * instanceof
1021 */
1022
1023 /*
1024 * E5 Section 11.8.6 describes the main algorithm, which uses
1025 * [[HasInstance]]. [[HasInstance]] is defined for only
1026 * function objects:
1027 *
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
1032 *
1033 * For other objects, a TypeError is thrown.
1034 *
1035 * Limited Proxy support: don't support 'getPrototypeOf' trap but
1036 * continue lookup in Proxy target if the value is a Proxy.
1037 */
1038
duk_js_instanceof(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y)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;
1041 duk_hobject *func;
1042 duk_hobject *val;
1043 duk_hobject *proto;
1044 duk_uint_t sanity;
1045
1046 /*
1047 * Get the values onto the stack first. It would be possible to cover
1048 * some normal cases without resorting to the value stack.
1049 *
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).
1054 */
1055
1056 duk_push_tval(ctx, tv_x);
1057 duk_push_tval(ctx, tv_y);
1058 func = duk_require_hobject(ctx, -1);
1059
1060 /*
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.
1064 */
1065
1066 /* XXX: this bound function resolution also happens elsewhere,
1067 * move into a shared helper.
1068 */
1069
1070 sanity = DUK_HOBJECT_BOUND_CHAIN_SANITY;
1071 do {
1072 /* check func supports [[HasInstance]] (this is checked for every function
1073 * in the bound chain, including the final one)
1074 */
1075
1076 if (!DUK_HOBJECT_IS_CALLABLE(func)) {
1077 /*
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.
1081 *
1082 * XXX: add a separate flag, DUK_HOBJECT_FLAG_ALLOW_INSTANCEOF?
1083 */
1084 DUK_ERROR_TYPE(thr, "invalid instanceof rval");
1085 }
1086
1087 if (!DUK_HOBJECT_HAS_BOUND(func)) {
1088 break;
1089 }
1090
1091 /* [ ... lval rval ] */
1092
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);
1096
1097 /* func support for [[HasInstance]] checked in the beginning of the loop */
1098 } while (--sanity > 0);
1099
1100 if (sanity == 0) {
1101 DUK_ERROR_RANGE(thr, DUK_STR_BOUND_CHAIN_LIMIT);
1102 }
1103
1104 /*
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.
1108 */
1109
1110 DUK_ASSERT(!DUK_HOBJECT_HAS_BOUND(func));
1111 DUK_ASSERT(DUK_HOBJECT_IS_CALLABLE(func));
1112
1113 /* [ ... lval rval(func) ] */
1114
1115 /* Handle lightfuncs through object coercion for now. */
1116 /* XXX: direct implementation */
1117 val = duk_get_hobject_or_lfunc_coerce(ctx, -2);
1118 if (!val) {
1119 goto pop_and_false;
1120 }
1121
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 ] */
1125
1126 DUK_ASSERT(val != NULL);
1127
1128 #if defined(DUK_USE_ES6_PROXY)
1129 val = duk_hobject_resolve_proxy_target(thr, val);
1130 DUK_ASSERT(val != NULL);
1131 #endif
1132
1133 sanity = DUK_HOBJECT_PROTOTYPE_CHAIN_SANITY;
1134 do {
1135 /*
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.
1140 *
1141 * Also note:
1142 *
1143 * js> (function() {}) instanceof Function
1144 * true
1145 * js> Function instanceof Function
1146 * true
1147 *
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.
1151 */
1152
1153 DUK_ASSERT(val != NULL);
1154 val = DUK_HOBJECT_GET_PROTOTYPE(thr->heap, val);
1155
1156 if (!val) {
1157 goto pop_and_false;
1158 }
1159
1160 DUK_ASSERT(val != NULL);
1161 #if defined(DUK_USE_ES6_PROXY)
1162 val = duk_hobject_resolve_proxy_target(thr, val);
1163 #endif
1164
1165 if (val == proto) {
1166 goto pop_and_true;
1167 }
1168
1169 /* follow prototype chain */
1170 } while (--sanity > 0);
1171
1172 if (sanity == 0) {
1173 DUK_ERROR_RANGE(thr, DUK_STR_PROTOTYPE_CHAIN_LIMIT);
1174 }
1175 DUK_UNREACHABLE();
1176
1177 pop_and_false:
1178 duk_pop_2(ctx);
1179 return 0;
1180
1181 pop_and_true:
1182 duk_pop_2(ctx);
1183 return 1;
1184 }
1185
1186 /*
1187 * in
1188 */
1189
1190 /*
1191 * E5 Sections 11.8.7, 8.12.6.
1192 *
1193 * Basically just a property existence check using [[HasProperty]].
1194 */
1195
duk_js_in(duk_hthread * thr,duk_tval * tv_x,duk_tval * tv_y)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;
1198 duk_bool_t retval;
1199
1200 /*
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).
1204 */
1205
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).
1212 */
1213
1214 /* TypeError if rval is not an object (or lightfunc which should behave
1215 * like a Function instance).
1216 */
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() */
1221
1222 retval = duk_hobject_hasprop(thr,
1223 DUK_GET_TVAL_NEGIDX(ctx, -1),
1224 DUK_GET_TVAL_NEGIDX(ctx, -2));
1225
1226 duk_pop_2(ctx);
1227 return retval;
1228 }
1229
1230 /*
1231 * typeof
1232 *
1233 * E5 Section 11.4.3.
1234 *
1235 * Very straightforward. The only question is what to return for our
1236 * non-standard tag / object types.
1237 *
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.
1242 */
1243
duk_js_typeof(duk_hthread * thr,duk_tval * tv_x)1244 DUK_INTERNAL duk_hstring *duk_js_typeof(duk_hthread *thr, duk_tval *tv_x) {
1245 duk_small_int_t stridx = 0;
1246
1247 DUK_UNREF(thr);
1248
1249 switch (DUK_TVAL_GET_TAG(tv_x)) {
1250 case DUK_TAG_UNDEFINED: {
1251 stridx = DUK_STRIDX_LC_UNDEFINED;
1252 break;
1253 }
1254 case DUK_TAG_NULL: {
1255 /* Note: not a typo, "object" is returned for a null value */
1256 stridx = DUK_STRIDX_LC_OBJECT;
1257 break;
1258 }
1259 case DUK_TAG_BOOLEAN: {
1260 stridx = DUK_STRIDX_LC_BOOLEAN;
1261 break;
1262 }
1263 case DUK_TAG_POINTER: {
1264 /* implementation specific */
1265 stridx = DUK_STRIDX_LC_POINTER;
1266 break;
1267 }
1268 case DUK_TAG_STRING: {
1269 stridx = DUK_STRIDX_LC_STRING;
1270 break;
1271 }
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;
1277 } else {
1278 stridx = DUK_STRIDX_LC_OBJECT;
1279 }
1280 break;
1281 }
1282 case DUK_TAG_BUFFER: {
1283 /* implementation specific */
1284 stridx = DUK_STRIDX_LC_BUFFER;
1285 break;
1286 }
1287 case DUK_TAG_LIGHTFUNC: {
1288 stridx = DUK_STRIDX_LC_FUNCTION;
1289 break;
1290 }
1291 #if defined(DUK_USE_FASTINT)
1292 case DUK_TAG_FASTINT:
1293 #endif
1294 default: {
1295 /* number */
1296 DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv_x));
1297 DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_x));
1298 stridx = DUK_STRIDX_LC_NUMBER;
1299 break;
1300 }
1301 }
1302
1303 DUK_ASSERT(stridx >= 0 && stridx < DUK_HEAP_NUM_STRINGS);
1304 return DUK_HTHREAD_GET_STRING(thr, stridx);
1305 }
1306
1307 /*
1308 * Array index and length
1309 *
1310 * Array index: E5 Section 15.4
1311 * Array length: E5 Section 15.4.5.1 steps 3.c - 3.d (array length write)
1312 *
1313 * The DUK_HSTRING_GET_ARRIDX_SLOW() and DUK_HSTRING_GET_ARRIDX_FAST() macros
1314 * call duk_js_to_arrayindex_string_helper().
1315 */
1316
duk_js_to_arrayindex_raw_string(const duk_uint8_t * str,duk_uint32_t blen,duk_uarridx_t * out_idx)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) {
1318 duk_uarridx_t res, new_res;
1319
1320 if (blen == 0 || blen > 10) {
1321 goto parse_fail;
1322 }
1323 if (str[0] == (duk_uint8_t) '0' && blen > 1) {
1324 goto parse_fail;
1325 }
1326
1327 /* Accept 32-bit decimal integers, no leading zeroes, signs, etc.
1328 * Leading zeroes are not accepted (zero index "0" is an exception
1329 * handled above).
1330 */
1331
1332 res = 0;
1333 while (blen-- > 0) {
1334 duk_uint8_t c = *str++;
1335 if (c >= (duk_uint8_t) '0' && c <= (duk_uint8_t) '9') {
1336 new_res = res * 10 + (duk_uint32_t) (c - (duk_uint8_t) '0');
1337 if (new_res < res) {
1338 /* overflow, more than 32 bits -> not an array index */
1339 goto parse_fail;
1340 }
1341 res = new_res;
1342 } else {
1343 goto parse_fail;
1344 }
1345 }
1346
1347 *out_idx = res;
1348 return 1;
1349
1350 parse_fail:
1351 *out_idx = DUK_HSTRING_NO_ARRAY_INDEX;
1352 return 0;
1353 }
1354
1355 /* Called by duk_hstring.h macros */
duk_js_to_arrayindex_string_helper(duk_hstring * h)1356 DUK_INTERNAL duk_uarridx_t duk_js_to_arrayindex_string_helper(duk_hstring *h) {
1357 duk_uarridx_t res;
1358 duk_small_int_t rc;
1359
1360 if (!DUK_HSTRING_HAS_ARRIDX(h)) {
1361 return DUK_HSTRING_NO_ARRAY_INDEX;
1362 }
1363
1364 rc = duk_js_to_arrayindex_raw_string(DUK_HSTRING_GET_DATA(h),
1365 DUK_HSTRING_GET_BYTELEN(h),
1366 &res);
1367 DUK_UNREF(rc);
1368 DUK_ASSERT(rc != 0);
1369 return res;
1370 }
1371