comparison mupdf-source/thirdparty/mujs/jsvalue.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 #include "jsi.h"
2 #include "utf.h"
3
4 #define JSV_ISSTRING(v) (v->t.type==JS_TSHRSTR || v->t.type==JS_TMEMSTR || v->t.type==JS_TLITSTR)
5 #define JSV_TOSTRING(v) (v->t.type==JS_TSHRSTR ? v->u.shrstr : v->t.type==JS_TLITSTR ? v->u.litstr : v->t.type==JS_TMEMSTR ? v->u.memstr->p : "")
6
7 double js_strtol(const char *s, char **p, int base)
8 {
9 /* ascii -> digit value. max base is 36. */
10 static const unsigned char table[256] = {
11 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
12 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
13 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
14 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 80, 80, 80, 80, 80, 80,
15 80, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
16 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 80, 80, 80, 80, 80,
17 80, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
18 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 80, 80, 80, 80, 80,
19 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
20 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
21 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
22 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
23 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
24 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
25 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
26 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80
27 };
28 double x;
29 unsigned char c;
30 if (base == 10)
31 for (x = 0, c = *s++; (0 <= c - '0') && (c - '0' < 10); c = *s++)
32 x = x * 10 + (c - '0');
33 else
34 for (x = 0, c = *s++; table[c] < base; c = *s++)
35 x = x * base + table[c];
36 if (p)
37 *p = (char*)s-1;
38 return x;
39 }
40
41 int jsV_numbertointeger(double n)
42 {
43 if (n == 0) return 0;
44 if (isnan(n)) return 0;
45 n = (n < 0) ? -floor(-n) : floor(n);
46 if (n < INT_MIN) return INT_MIN;
47 if (n > INT_MAX) return INT_MAX;
48 return (int)n;
49 }
50
51 int jsV_numbertoint32(double n)
52 {
53 double two32 = 4294967296.0;
54 double two31 = 2147483648.0;
55
56 if (!isfinite(n) || n == 0)
57 return 0;
58
59 n = fmod(n, two32);
60 n = n >= 0 ? floor(n) : ceil(n) + two32;
61 if (n >= two31)
62 return n - two32;
63 else
64 return n;
65 }
66
67 unsigned int jsV_numbertouint32(double n)
68 {
69 return (unsigned int)jsV_numbertoint32(n);
70 }
71
72 short jsV_numbertoint16(double n)
73 {
74 return jsV_numbertoint32(n);
75 }
76
77 unsigned short jsV_numbertouint16(double n)
78 {
79 return jsV_numbertoint32(n);
80 }
81
82 /* obj.toString() */
83 static int jsV_toString(js_State *J, js_Object *obj)
84 {
85 js_pushobject(J, obj);
86 js_getproperty(J, -1, "toString");
87 if (js_iscallable(J, -1)) {
88 js_rot2(J);
89 js_call(J, 0);
90 if (js_isprimitive(J, -1))
91 return 1;
92 js_pop(J, 1);
93 return 0;
94 }
95 js_pop(J, 2);
96 return 0;
97 }
98
99 /* obj.valueOf() */
100 static int jsV_valueOf(js_State *J, js_Object *obj)
101 {
102 js_pushobject(J, obj);
103 js_getproperty(J, -1, "valueOf");
104 if (js_iscallable(J, -1)) {
105 js_rot2(J);
106 js_call(J, 0);
107 if (js_isprimitive(J, -1))
108 return 1;
109 js_pop(J, 1);
110 return 0;
111 }
112 js_pop(J, 2);
113 return 0;
114 }
115
116 /* ToPrimitive() on a value */
117 void jsV_toprimitive(js_State *J, js_Value *v, int preferred)
118 {
119 js_Object *obj;
120
121 if (v->t.type != JS_TOBJECT)
122 return;
123
124 obj = v->u.object;
125
126 if (preferred == JS_HNONE)
127 preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER;
128
129 if (preferred == JS_HSTRING) {
130 if (jsV_toString(J, obj) || jsV_valueOf(J, obj)) {
131 *v = *js_tovalue(J, -1);
132 js_pop(J, 1);
133 return;
134 }
135 } else {
136 if (jsV_valueOf(J, obj) || jsV_toString(J, obj)) {
137 *v = *js_tovalue(J, -1);
138 js_pop(J, 1);
139 return;
140 }
141 }
142
143 if (J->strict)
144 js_typeerror(J, "cannot convert object to primitive");
145
146 v->t.type = JS_TLITSTR;
147 v->u.litstr = "[object]";
148 return;
149 }
150
151 /* ToBoolean() on a value */
152 int jsV_toboolean(js_State *J, js_Value *v)
153 {
154 switch (v->t.type) {
155 default:
156 case JS_TSHRSTR: return v->u.shrstr[0] != 0;
157 case JS_TUNDEFINED: return 0;
158 case JS_TNULL: return 0;
159 case JS_TBOOLEAN: return v->u.boolean;
160 case JS_TNUMBER: return v->u.number != 0 && !isnan(v->u.number);
161 case JS_TLITSTR: return v->u.litstr[0] != 0;
162 case JS_TMEMSTR: return v->u.memstr->p[0] != 0;
163 case JS_TOBJECT: return 1;
164 }
165 }
166
167 const char *js_itoa(char *out, int v)
168 {
169 char buf[32], *s = out;
170 unsigned int a;
171 int i = 0;
172 if (v < 0) {
173 a = -v;
174 *s++ = '-';
175 } else {
176 a = v;
177 }
178 while (a) {
179 buf[i++] = (a % 10) + '0';
180 a /= 10;
181 }
182 if (i == 0)
183 buf[i++] = '0';
184 while (i > 0)
185 *s++ = buf[--i];
186 *s = 0;
187 return out;
188 }
189
190 double js_stringtofloat(const char *s, char **ep)
191 {
192 char *end;
193 double n;
194 const char *e = s;
195 int isflt = 0;
196 if (*e == '+' || *e == '-') ++e;
197 while (*e >= '0' && *e <= '9') ++e;
198 if (*e == '.') { ++e; isflt = 1; }
199 while (*e >= '0' && *e <= '9') ++e;
200 if (*e == 'e' || *e == 'E') {
201 ++e;
202 if (*e == '+' || *e == '-') ++e;
203 while (*e >= '0' && *e <= '9') ++e;
204 isflt = 1;
205 }
206 if (isflt)
207 n = js_strtod(s, &end);
208 else {
209 /* js_strtol doesn't parse the sign */
210 if (*s == '-')
211 n = -js_strtol(s+1, &end, 10);
212 else if (*s == '+')
213 n = js_strtol(s+1, &end, 10);
214 else
215 n = js_strtol(s, &end, 10);
216 }
217 if (end == e) {
218 *ep = (char*)e;
219 return n;
220 }
221 *ep = (char*)s;
222 return 0;
223 }
224
225 /* ToNumber() on a string */
226 double jsV_stringtonumber(js_State *J, const char *s)
227 {
228 char *e;
229 double n;
230 while (jsY_iswhite(*s) || jsY_isnewline(*s)) ++s;
231 if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X') && s[2] != 0)
232 n = js_strtol(s + 2, &e, 16);
233 else if (!strncmp(s, "Infinity", 8))
234 n = INFINITY, e = (char*)s + 8;
235 else if (!strncmp(s, "+Infinity", 9))
236 n = INFINITY, e = (char*)s + 9;
237 else if (!strncmp(s, "-Infinity", 9))
238 n = -INFINITY, e = (char*)s + 9;
239 else
240 n = js_stringtofloat(s, &e);
241 while (jsY_iswhite(*e) || jsY_isnewline(*e)) ++e;
242 if (*e) return NAN;
243 return n;
244 }
245
246 /* ToNumber() on a value */
247 double jsV_tonumber(js_State *J, js_Value *v)
248 {
249 switch (v->t.type) {
250 default:
251 case JS_TSHRSTR: return jsV_stringtonumber(J, v->u.shrstr);
252 case JS_TUNDEFINED: return NAN;
253 case JS_TNULL: return 0;
254 case JS_TBOOLEAN: return v->u.boolean;
255 case JS_TNUMBER: return v->u.number;
256 case JS_TLITSTR: return jsV_stringtonumber(J, v->u.litstr);
257 case JS_TMEMSTR: return jsV_stringtonumber(J, v->u.memstr->p);
258 case JS_TOBJECT:
259 jsV_toprimitive(J, v, JS_HNUMBER);
260 return jsV_tonumber(J, v);
261 }
262 }
263
264 double jsV_tointeger(js_State *J, js_Value *v)
265 {
266 return jsV_numbertointeger(jsV_tonumber(J, v));
267 }
268
269 /* ToString() on a number */
270 const char *jsV_numbertostring(js_State *J, char buf[32], double f)
271 {
272 char digits[32], *p = buf, *s = digits;
273 int exp, ndigits, point;
274
275 if (f == 0) return "0";
276 if (isnan(f)) return "NaN";
277 if (isinf(f)) return f < 0 ? "-Infinity" : "Infinity";
278
279 /* Fast case for integers. This only works assuming all integers can be
280 * exactly represented by a float. This is true for 32-bit integers and
281 * 64-bit floats. */
282 if (f >= INT_MIN && f <= INT_MAX) {
283 int i = (int)f;
284 if ((double)i == f)
285 return js_itoa(buf, i);
286 }
287
288 ndigits = js_grisu2(f, digits, &exp);
289 point = ndigits + exp;
290
291 if (signbit(f))
292 *p++ = '-';
293
294 if (point < -5 || point > 21) {
295 *p++ = *s++;
296 if (ndigits > 1) {
297 int n = ndigits - 1;
298 *p++ = '.';
299 while (n--)
300 *p++ = *s++;
301 }
302 js_fmtexp(p, point - 1);
303 }
304
305 else if (point <= 0) {
306 *p++ = '0';
307 *p++ = '.';
308 while (point++ < 0)
309 *p++ = '0';
310 while (ndigits-- > 0)
311 *p++ = *s++;
312 *p = 0;
313 }
314
315 else {
316 while (ndigits-- > 0) {
317 *p++ = *s++;
318 if (--point == 0 && ndigits > 0)
319 *p++ = '.';
320 }
321 while (point-- > 0)
322 *p++ = '0';
323 *p = 0;
324 }
325
326 return buf;
327 }
328
329 /* ToString() on a value */
330 const char *jsV_tostring(js_State *J, js_Value *v)
331 {
332 char buf[32];
333 const char *p;
334 switch (v->t.type) {
335 default:
336 case JS_TSHRSTR: return v->u.shrstr;
337 case JS_TUNDEFINED: return "undefined";
338 case JS_TNULL: return "null";
339 case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
340 case JS_TLITSTR: return v->u.litstr;
341 case JS_TMEMSTR: return v->u.memstr->p;
342 case JS_TNUMBER:
343 p = jsV_numbertostring(J, buf, v->u.number);
344 if (p == buf) {
345 int n = strlen(p);
346 if (n <= soffsetof(js_Value, t.type)) {
347 char *s = v->u.shrstr;
348 while (n--) *s++ = *p++;
349 *s = 0;
350 v->t.type = JS_TSHRSTR;
351 return v->u.shrstr;
352 } else {
353 v->u.memstr = jsV_newmemstring(J, p, n);
354 v->t.type = JS_TMEMSTR;
355 return v->u.memstr->p;
356 }
357 }
358 return p;
359 case JS_TOBJECT:
360 jsV_toprimitive(J, v, JS_HSTRING);
361 return jsV_tostring(J, v);
362 }
363 }
364
365 /* Objects */
366
367 static js_Object *jsV_newboolean(js_State *J, int v)
368 {
369 js_Object *obj = jsV_newobject(J, JS_CBOOLEAN, J->Boolean_prototype);
370 obj->u.boolean = v;
371 return obj;
372 }
373
374 static js_Object *jsV_newnumber(js_State *J, double v)
375 {
376 js_Object *obj = jsV_newobject(J, JS_CNUMBER, J->Number_prototype);
377 obj->u.number = v;
378 return obj;
379 }
380
381 static js_Object *jsV_newstring(js_State *J, const char *v)
382 {
383 js_Object *obj = jsV_newobject(J, JS_CSTRING, J->String_prototype);
384 size_t n = strlen(v);
385 if (n < sizeof(obj->u.s.shrstr)) {
386 obj->u.s.string = obj->u.s.shrstr;
387 memcpy(obj->u.s.shrstr, v, n + 1);
388 } else {
389 obj->u.s.string = js_strdup(J, v);
390 }
391 obj->u.s.length = js_utflen(v);
392 return obj;
393 }
394
395 /* ToObject() on a value */
396 js_Object *jsV_toobject(js_State *J, js_Value *v)
397 {
398 js_Object *o;
399 switch (v->t.type) {
400 default:
401 case JS_TUNDEFINED: js_typeerror(J, "cannot convert undefined to object");
402 case JS_TNULL: js_typeerror(J, "cannot convert null to object");
403 case JS_TOBJECT: return v->u.object;
404 case JS_TSHRSTR: o = jsV_newstring(J, v->u.shrstr); break;
405 case JS_TLITSTR: o = jsV_newstring(J, v->u.litstr); break;
406 case JS_TMEMSTR: o = jsV_newstring(J, v->u.memstr->p); break;
407 case JS_TBOOLEAN: o = jsV_newboolean(J, v->u.boolean); break;
408 case JS_TNUMBER: o = jsV_newnumber(J, v->u.number); break;
409 }
410 v->t.type = JS_TOBJECT;
411 v->u.object = o;
412 return o;
413 }
414
415 void js_newobjectx(js_State *J)
416 {
417 js_Object *prototype = NULL;
418 if (js_isobject(J, -1))
419 prototype = js_toobject(J, -1);
420 js_pop(J, 1);
421 js_pushobject(J, jsV_newobject(J, JS_COBJECT, prototype));
422 }
423
424 void js_newobject(js_State *J)
425 {
426 js_pushobject(J, jsV_newobject(J, JS_COBJECT, J->Object_prototype));
427 }
428
429 void js_newarguments(js_State *J)
430 {
431 js_pushobject(J, jsV_newobject(J, JS_CARGUMENTS, J->Object_prototype));
432 }
433
434 void js_newarray(js_State *J)
435 {
436 js_Object *obj = jsV_newobject(J, JS_CARRAY, J->Array_prototype);
437 obj->u.a.simple = 1;
438 js_pushobject(J, obj);
439 }
440
441 void js_newboolean(js_State *J, int v)
442 {
443 js_pushobject(J, jsV_newboolean(J, v));
444 }
445
446 void js_newnumber(js_State *J, double v)
447 {
448 js_pushobject(J, jsV_newnumber(J, v));
449 }
450
451 void js_newstring(js_State *J, const char *v)
452 {
453 js_pushobject(J, jsV_newstring(J, v));
454 }
455
456 void js_newfunction(js_State *J, js_Function *fun, js_Environment *scope)
457 {
458 js_Object *obj = jsV_newobject(J, JS_CFUNCTION, J->Function_prototype);
459 obj->u.f.function = fun;
460 obj->u.f.scope = scope;
461 js_pushobject(J, obj);
462 {
463 js_pushnumber(J, fun->numparams);
464 js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
465 js_newobject(J);
466 {
467 js_copy(J, -2);
468 js_defproperty(J, -2, "constructor", JS_DONTENUM);
469 }
470 js_defproperty(J, -2, "prototype", JS_DONTENUM | JS_DONTCONF);
471 }
472 }
473
474 void js_newscript(js_State *J, js_Function *fun, js_Environment *scope)
475 {
476 js_Object *obj = jsV_newobject(J, JS_CSCRIPT, NULL);
477 obj->u.f.function = fun;
478 obj->u.f.scope = scope;
479 js_pushobject(J, obj);
480 }
481
482 void js_newcfunctionx(js_State *J, js_CFunction cfun, const char *name, int length, void *data, js_Finalize finalize)
483 {
484 js_Object *obj;
485
486 if (js_try(J)) {
487 if (finalize)
488 finalize(J, data);
489 js_throw(J);
490 }
491
492 obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
493 obj->u.c.name = name;
494 obj->u.c.function = cfun;
495 obj->u.c.constructor = NULL;
496 obj->u.c.length = length;
497 obj->u.c.data = data;
498 obj->u.c.finalize = finalize;
499
500 js_endtry(J);
501
502 js_pushobject(J, obj);
503 {
504 js_pushnumber(J, length);
505 js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
506 js_newobject(J);
507 {
508 js_copy(J, -2);
509 js_defproperty(J, -2, "constructor", JS_DONTENUM);
510 }
511 js_defproperty(J, -2, "prototype", JS_DONTENUM | JS_DONTCONF);
512 }
513 }
514
515 void js_newcfunction(js_State *J, js_CFunction cfun, const char *name, int length)
516 {
517 js_newcfunctionx(J, cfun, name, length, NULL, NULL);
518 }
519
520 /* prototype -- constructor */
521 void js_newcconstructor(js_State *J, js_CFunction cfun, js_CFunction ccon, const char *name, int length)
522 {
523 js_Object *obj = jsV_newobject(J, JS_CCFUNCTION, J->Function_prototype);
524 obj->u.c.name = name;
525 obj->u.c.function = cfun;
526 obj->u.c.constructor = ccon;
527 obj->u.c.length = length;
528 js_pushobject(J, obj); /* proto obj */
529 {
530 js_pushnumber(J, length);
531 js_defproperty(J, -2, "length", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
532 js_rot2(J); /* obj proto */
533 js_copy(J, -2); /* obj proto obj */
534 js_defproperty(J, -2, "constructor", JS_DONTENUM);
535 js_defproperty(J, -2, "prototype", JS_DONTENUM | JS_DONTCONF);
536 }
537 }
538
539 void js_newuserdatax(js_State *J, const char *tag, void *data, js_HasProperty has, js_Put put, js_Delete delete, js_Finalize finalize)
540 {
541 js_Object *prototype = NULL;
542 js_Object *obj;
543
544 if (js_isobject(J, -1))
545 prototype = js_toobject(J, -1);
546 js_pop(J, 1);
547
548 if (js_try(J)) {
549 if (finalize)
550 finalize(J, data);
551 js_throw(J);
552 }
553
554 obj = jsV_newobject(J, JS_CUSERDATA, prototype);
555 obj->u.user.tag = tag;
556 obj->u.user.data = data;
557 obj->u.user.has = has;
558 obj->u.user.put = put;
559 obj->u.user.delete = delete;
560 obj->u.user.finalize = finalize;
561
562 js_endtry(J);
563
564 js_pushobject(J, obj);
565 }
566
567 void js_newuserdata(js_State *J, const char *tag, void *data, js_Finalize finalize)
568 {
569 js_newuserdatax(J, tag, data, NULL, NULL, NULL, finalize);
570 }
571
572 /* Non-trivial operations on values. These are implemented using the stack. */
573
574 int js_instanceof(js_State *J)
575 {
576 js_Object *O, *V;
577
578 if (!js_iscallable(J, -1))
579 js_typeerror(J, "instanceof: invalid operand");
580
581 if (!js_isobject(J, -2))
582 return 0;
583
584 js_getproperty(J, -1, "prototype");
585 if (!js_isobject(J, -1))
586 js_typeerror(J, "instanceof: 'prototype' property is not an object");
587 O = js_toobject(J, -1);
588 js_pop(J, 1);
589
590 V = js_toobject(J, -2);
591 while (V) {
592 V = V->prototype;
593 if (O == V)
594 return 1;
595 }
596
597 return 0;
598 }
599
600 void js_concat(js_State *J)
601 {
602 js_toprimitive(J, -2, JS_HNONE);
603 js_toprimitive(J, -1, JS_HNONE);
604
605 if (js_isstring(J, -2) || js_isstring(J, -1)) {
606 const char *sa = js_tostring(J, -2);
607 const char *sb = js_tostring(J, -1);
608 char * volatile sab = NULL;
609 /* TODO: create js_String directly */
610 if (js_try(J)) {
611 js_free(J, sab);
612 js_throw(J);
613 }
614 sab = js_malloc(J, strlen(sa) + strlen(sb) + 1);
615 strcpy(sab, sa);
616 strcat(sab, sb);
617 js_pop(J, 2);
618 js_pushstring(J, sab);
619 js_endtry(J);
620 js_free(J, sab);
621 } else {
622 double x = js_tonumber(J, -2);
623 double y = js_tonumber(J, -1);
624 js_pop(J, 2);
625 js_pushnumber(J, x + y);
626 }
627 }
628
629 int js_compare(js_State *J, int *okay)
630 {
631 js_toprimitive(J, -2, JS_HNUMBER);
632 js_toprimitive(J, -1, JS_HNUMBER);
633
634 *okay = 1;
635 if (js_isstring(J, -2) && js_isstring(J, -1)) {
636 return strcmp(js_tostring(J, -2), js_tostring(J, -1));
637 } else {
638 double x = js_tonumber(J, -2);
639 double y = js_tonumber(J, -1);
640 if (isnan(x) || isnan(y))
641 *okay = 0;
642 return x < y ? -1 : x > y ? 1 : 0;
643 }
644 }
645
646 int js_equal(js_State *J)
647 {
648 js_Value *x = js_tovalue(J, -2);
649 js_Value *y = js_tovalue(J, -1);
650
651 retry:
652 if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
653 return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
654 if (x->t.type == y->t.type) {
655 if (x->t.type == JS_TUNDEFINED) return 1;
656 if (x->t.type == JS_TNULL) return 1;
657 if (x->t.type == JS_TNUMBER) return x->u.number == y->u.number;
658 if (x->t.type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
659 if (x->t.type == JS_TOBJECT) return x->u.object == y->u.object;
660 return 0;
661 }
662
663 if (x->t.type == JS_TNULL && y->t.type == JS_TUNDEFINED) return 1;
664 if (x->t.type == JS_TUNDEFINED && y->t.type == JS_TNULL) return 1;
665
666 if (x->t.type == JS_TNUMBER && JSV_ISSTRING(y))
667 return x->u.number == jsV_tonumber(J, y);
668 if (JSV_ISSTRING(x) && y->t.type == JS_TNUMBER)
669 return jsV_tonumber(J, x) == y->u.number;
670
671 if (x->t.type == JS_TBOOLEAN) {
672 x->t.type = JS_TNUMBER;
673 x->u.number = x->u.boolean ? 1 : 0;
674 goto retry;
675 }
676 if (y->t.type == JS_TBOOLEAN) {
677 y->t.type = JS_TNUMBER;
678 y->u.number = y->u.boolean ? 1 : 0;
679 goto retry;
680 }
681 if ((JSV_ISSTRING(x) || x->t.type == JS_TNUMBER) && y->t.type == JS_TOBJECT) {
682 jsV_toprimitive(J, y, JS_HNONE);
683 goto retry;
684 }
685 if (x->t.type == JS_TOBJECT && (JSV_ISSTRING(y) || y->t.type == JS_TNUMBER)) {
686 jsV_toprimitive(J, x, JS_HNONE);
687 goto retry;
688 }
689
690 return 0;
691 }
692
693 int js_strictequal(js_State *J)
694 {
695 js_Value *x = js_tovalue(J, -2);
696 js_Value *y = js_tovalue(J, -1);
697
698 if (JSV_ISSTRING(x) && JSV_ISSTRING(y))
699 return !strcmp(JSV_TOSTRING(x), JSV_TOSTRING(y));
700
701 if (x->t.type != y->t.type) return 0;
702 if (x->t.type == JS_TUNDEFINED) return 1;
703 if (x->t.type == JS_TNULL) return 1;
704 if (x->t.type == JS_TNUMBER) return x->u.number == y->u.number;
705 if (x->t.type == JS_TBOOLEAN) return x->u.boolean == y->u.boolean;
706 if (x->t.type == JS_TOBJECT) return x->u.object == y->u.object;
707 return 0;
708 }