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