Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/mujs/jsrun.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 #include <assert.h> | |
| 5 | |
| 6 static void jsR_run(js_State *J, js_Function *F); | |
| 7 | |
| 8 /* Push values on stack */ | |
| 9 | |
| 10 #define STACK (J->stack) | |
| 11 #define TOP (J->top) | |
| 12 #define BOT (J->bot) | |
| 13 | |
| 14 static void js_trystackoverflow(js_State *J) | |
| 15 { | |
| 16 STACK[TOP].t.type = JS_TLITSTR; | |
| 17 STACK[TOP].u.litstr = "exception stack overflow"; | |
| 18 ++TOP; | |
| 19 js_throw(J); | |
| 20 } | |
| 21 | |
| 22 static void js_stackoverflow(js_State *J) | |
| 23 { | |
| 24 STACK[TOP].t.type = JS_TLITSTR; | |
| 25 STACK[TOP].u.litstr = "stack overflow"; | |
| 26 ++TOP; | |
| 27 js_throw(J); | |
| 28 } | |
| 29 | |
| 30 static void js_outofmemory(js_State *J) | |
| 31 { | |
| 32 STACK[TOP].t.type = JS_TLITSTR; | |
| 33 STACK[TOP].u.litstr = "out of memory"; | |
| 34 ++TOP; | |
| 35 js_throw(J); | |
| 36 } | |
| 37 | |
| 38 void *js_malloc(js_State *J, int size) | |
| 39 { | |
| 40 void *ptr = J->alloc(J->actx, NULL, size); | |
| 41 if (!ptr) | |
| 42 js_outofmemory(J); | |
| 43 return ptr; | |
| 44 } | |
| 45 | |
| 46 void *js_realloc(js_State *J, void *ptr, int size) | |
| 47 { | |
| 48 ptr = J->alloc(J->actx, ptr, size); | |
| 49 if (!ptr) | |
| 50 js_outofmemory(J); | |
| 51 return ptr; | |
| 52 } | |
| 53 | |
| 54 char *js_strdup(js_State *J, const char *s) | |
| 55 { | |
| 56 int n = strlen(s) + 1; | |
| 57 char *p = js_malloc(J, n); | |
| 58 memcpy(p, s, n); | |
| 59 return p; | |
| 60 } | |
| 61 | |
| 62 void js_free(js_State *J, void *ptr) | |
| 63 { | |
| 64 J->alloc(J->actx, ptr, 0); | |
| 65 } | |
| 66 | |
| 67 js_String *jsV_newmemstring(js_State *J, const char *s, int n) | |
| 68 { | |
| 69 js_String *v = js_malloc(J, soffsetof(js_String, p) + n + 1); | |
| 70 memcpy(v->p, s, n); | |
| 71 v->p[n] = 0; | |
| 72 v->gcmark = 0; | |
| 73 v->gcnext = J->gcstr; | |
| 74 J->gcstr = v; | |
| 75 ++J->gccounter; | |
| 76 return v; | |
| 77 } | |
| 78 | |
| 79 #define CHECKSTACK(n) if (TOP + n >= JS_STACKSIZE) js_stackoverflow(J) | |
| 80 | |
| 81 void js_pushvalue(js_State *J, js_Value v) | |
| 82 { | |
| 83 CHECKSTACK(1); | |
| 84 STACK[TOP] = v; | |
| 85 ++TOP; | |
| 86 } | |
| 87 | |
| 88 void js_pushundefined(js_State *J) | |
| 89 { | |
| 90 CHECKSTACK(1); | |
| 91 STACK[TOP].t.type = JS_TUNDEFINED; | |
| 92 ++TOP; | |
| 93 } | |
| 94 | |
| 95 void js_pushnull(js_State *J) | |
| 96 { | |
| 97 CHECKSTACK(1); | |
| 98 STACK[TOP].t.type = JS_TNULL; | |
| 99 ++TOP; | |
| 100 } | |
| 101 | |
| 102 void js_pushboolean(js_State *J, int v) | |
| 103 { | |
| 104 CHECKSTACK(1); | |
| 105 STACK[TOP].t.type = JS_TBOOLEAN; | |
| 106 STACK[TOP].u.boolean = !!v; | |
| 107 ++TOP; | |
| 108 } | |
| 109 | |
| 110 void js_pushnumber(js_State *J, double v) | |
| 111 { | |
| 112 CHECKSTACK(1); | |
| 113 STACK[TOP].t.type = JS_TNUMBER; | |
| 114 STACK[TOP].u.number = v; | |
| 115 ++TOP; | |
| 116 } | |
| 117 | |
| 118 void js_pushstring(js_State *J, const char *v) | |
| 119 { | |
| 120 size_t n = strlen(v); | |
| 121 if (n > JS_STRLIMIT) | |
| 122 js_rangeerror(J, "invalid string length"); | |
| 123 CHECKSTACK(1); | |
| 124 if (n <= soffsetof(js_Value, t.type)) { | |
| 125 char *s = STACK[TOP].u.shrstr; | |
| 126 while (n--) *s++ = *v++; | |
| 127 *s = 0; | |
| 128 STACK[TOP].t.type = JS_TSHRSTR; | |
| 129 } else { | |
| 130 STACK[TOP].t.type = JS_TMEMSTR; | |
| 131 STACK[TOP].u.memstr = jsV_newmemstring(J, v, n); | |
| 132 } | |
| 133 ++TOP; | |
| 134 } | |
| 135 | |
| 136 void js_pushlstring(js_State *J, const char *v, int n) | |
| 137 { | |
| 138 if (n > JS_STRLIMIT) | |
| 139 js_rangeerror(J, "invalid string length"); | |
| 140 CHECKSTACK(1); | |
| 141 if (n <= soffsetof(js_Value, t.type)) { | |
| 142 char *s = STACK[TOP].u.shrstr; | |
| 143 while (n--) *s++ = *v++; | |
| 144 *s = 0; | |
| 145 STACK[TOP].t.type = JS_TSHRSTR; | |
| 146 } else { | |
| 147 STACK[TOP].t.type = JS_TMEMSTR; | |
| 148 STACK[TOP].u.memstr = jsV_newmemstring(J, v, n); | |
| 149 } | |
| 150 ++TOP; | |
| 151 } | |
| 152 | |
| 153 void js_pushliteral(js_State *J, const char *v) | |
| 154 { | |
| 155 CHECKSTACK(1); | |
| 156 STACK[TOP].t.type = JS_TLITSTR; | |
| 157 STACK[TOP].u.litstr = v; | |
| 158 ++TOP; | |
| 159 } | |
| 160 | |
| 161 void js_pushobject(js_State *J, js_Object *v) | |
| 162 { | |
| 163 CHECKSTACK(1); | |
| 164 STACK[TOP].t.type = JS_TOBJECT; | |
| 165 STACK[TOP].u.object = v; | |
| 166 ++TOP; | |
| 167 } | |
| 168 | |
| 169 void js_pushglobal(js_State *J) | |
| 170 { | |
| 171 js_pushobject(J, J->G); | |
| 172 } | |
| 173 | |
| 174 void js_currentfunction(js_State *J) | |
| 175 { | |
| 176 CHECKSTACK(1); | |
| 177 if (BOT > 0) | |
| 178 STACK[TOP] = STACK[BOT-1]; | |
| 179 else | |
| 180 STACK[TOP].t.type = JS_TUNDEFINED; | |
| 181 ++TOP; | |
| 182 } | |
| 183 | |
| 184 void *js_currentfunctiondata(js_State *J) | |
| 185 { | |
| 186 if (BOT > 0) | |
| 187 return STACK[BOT-1].u.object->u.c.data; | |
| 188 return NULL; | |
| 189 } | |
| 190 | |
| 191 /* Read values from stack */ | |
| 192 | |
| 193 static js_Value *stackidx(js_State *J, int idx) | |
| 194 { | |
| 195 static js_Value undefined = { { {0}, JS_TUNDEFINED } }; | |
| 196 idx = idx < 0 ? TOP + idx : BOT + idx; | |
| 197 if (idx < 0 || idx >= TOP) | |
| 198 return &undefined; | |
| 199 return STACK + idx; | |
| 200 } | |
| 201 | |
| 202 js_Value *js_tovalue(js_State *J, int idx) | |
| 203 { | |
| 204 return stackidx(J, idx); | |
| 205 } | |
| 206 | |
| 207 int js_isdefined(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TUNDEFINED; } | |
| 208 int js_isundefined(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TUNDEFINED; } | |
| 209 int js_isnull(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNULL; } | |
| 210 int js_isboolean(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TBOOLEAN; } | |
| 211 int js_isnumber(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TNUMBER; } | |
| 212 int js_isstring(js_State *J, int idx) { enum js_Type t = stackidx(J, idx)->t.type; return t == JS_TSHRSTR || t == JS_TLITSTR || t == JS_TMEMSTR; } | |
| 213 int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->t.type != JS_TOBJECT; } | |
| 214 int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->t.type == JS_TOBJECT; } | |
| 215 int js_iscoercible(js_State *J, int idx) { js_Value *v = stackidx(J, idx); return v->t.type != JS_TUNDEFINED && v->t.type != JS_TNULL; } | |
| 216 | |
| 217 int js_iscallable(js_State *J, int idx) | |
| 218 { | |
| 219 js_Value *v = stackidx(J, idx); | |
| 220 if (v->t.type == JS_TOBJECT) | |
| 221 return v->u.object->type == JS_CFUNCTION || | |
| 222 v->u.object->type == JS_CSCRIPT || | |
| 223 v->u.object->type == JS_CCFUNCTION; | |
| 224 return 0; | |
| 225 } | |
| 226 | |
| 227 int js_isarray(js_State *J, int idx) | |
| 228 { | |
| 229 js_Value *v = stackidx(J, idx); | |
| 230 return v->t.type == JS_TOBJECT && v->u.object->type == JS_CARRAY; | |
| 231 } | |
| 232 | |
| 233 int js_isregexp(js_State *J, int idx) | |
| 234 { | |
| 235 js_Value *v = stackidx(J, idx); | |
| 236 return v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP; | |
| 237 } | |
| 238 | |
| 239 int js_isuserdata(js_State *J, int idx, const char *tag) | |
| 240 { | |
| 241 js_Value *v = stackidx(J, idx); | |
| 242 if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA) | |
| 243 return !strcmp(tag, v->u.object->u.user.tag); | |
| 244 return 0; | |
| 245 } | |
| 246 | |
| 247 int js_iserror(js_State *J, int idx) | |
| 248 { | |
| 249 js_Value *v = stackidx(J, idx); | |
| 250 return v->t.type == JS_TOBJECT && v->u.object->type == JS_CERROR; | |
| 251 } | |
| 252 | |
| 253 const char *js_typeof(js_State *J, int idx) | |
| 254 { | |
| 255 js_Value *v = stackidx(J, idx); | |
| 256 switch (v->t.type) { | |
| 257 default: | |
| 258 case JS_TSHRSTR: return "string"; | |
| 259 case JS_TUNDEFINED: return "undefined"; | |
| 260 case JS_TNULL: return "object"; | |
| 261 case JS_TBOOLEAN: return "boolean"; | |
| 262 case JS_TNUMBER: return "number"; | |
| 263 case JS_TLITSTR: return "string"; | |
| 264 case JS_TMEMSTR: return "string"; | |
| 265 case JS_TOBJECT: | |
| 266 if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) | |
| 267 return "function"; | |
| 268 return "object"; | |
| 269 } | |
| 270 } | |
| 271 | |
| 272 int js_type(js_State *J, int idx) | |
| 273 { | |
| 274 js_Value *v = stackidx(J, idx); | |
| 275 switch (v->t.type) { | |
| 276 default: | |
| 277 case JS_TSHRSTR: return JS_ISSTRING; | |
| 278 case JS_TUNDEFINED: return JS_ISUNDEFINED; | |
| 279 case JS_TNULL: return JS_ISNULL; | |
| 280 case JS_TBOOLEAN: return JS_ISBOOLEAN; | |
| 281 case JS_TNUMBER: return JS_ISNUMBER; | |
| 282 case JS_TLITSTR: return JS_ISSTRING; | |
| 283 case JS_TMEMSTR: return JS_ISSTRING; | |
| 284 case JS_TOBJECT: | |
| 285 if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) | |
| 286 return JS_ISFUNCTION; | |
| 287 return JS_ISOBJECT; | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 int js_toboolean(js_State *J, int idx) | |
| 292 { | |
| 293 return jsV_toboolean(J, stackidx(J, idx)); | |
| 294 } | |
| 295 | |
| 296 double js_tonumber(js_State *J, int idx) | |
| 297 { | |
| 298 return jsV_tonumber(J, stackidx(J, idx)); | |
| 299 } | |
| 300 | |
| 301 int js_tointeger(js_State *J, int idx) | |
| 302 { | |
| 303 return jsV_numbertointeger(jsV_tonumber(J, stackidx(J, idx))); | |
| 304 } | |
| 305 | |
| 306 int js_toint32(js_State *J, int idx) | |
| 307 { | |
| 308 return jsV_numbertoint32(jsV_tonumber(J, stackidx(J, idx))); | |
| 309 } | |
| 310 | |
| 311 unsigned int js_touint32(js_State *J, int idx) | |
| 312 { | |
| 313 return jsV_numbertouint32(jsV_tonumber(J, stackidx(J, idx))); | |
| 314 } | |
| 315 | |
| 316 short js_toint16(js_State *J, int idx) | |
| 317 { | |
| 318 return jsV_numbertoint16(jsV_tonumber(J, stackidx(J, idx))); | |
| 319 } | |
| 320 | |
| 321 unsigned short js_touint16(js_State *J, int idx) | |
| 322 { | |
| 323 return jsV_numbertouint16(jsV_tonumber(J, stackidx(J, idx))); | |
| 324 } | |
| 325 | |
| 326 const char *js_tostring(js_State *J, int idx) | |
| 327 { | |
| 328 return jsV_tostring(J, stackidx(J, idx)); | |
| 329 } | |
| 330 | |
| 331 js_Object *js_toobject(js_State *J, int idx) | |
| 332 { | |
| 333 return jsV_toobject(J, stackidx(J, idx)); | |
| 334 } | |
| 335 | |
| 336 void js_toprimitive(js_State *J, int idx, int hint) | |
| 337 { | |
| 338 jsV_toprimitive(J, stackidx(J, idx), hint); | |
| 339 } | |
| 340 | |
| 341 js_Regexp *js_toregexp(js_State *J, int idx) | |
| 342 { | |
| 343 js_Value *v = stackidx(J, idx); | |
| 344 if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CREGEXP) | |
| 345 return &v->u.object->u.r; | |
| 346 js_typeerror(J, "not a regexp"); | |
| 347 } | |
| 348 | |
| 349 void *js_touserdata(js_State *J, int idx, const char *tag) | |
| 350 { | |
| 351 js_Value *v = stackidx(J, idx); | |
| 352 if (v->t.type == JS_TOBJECT && v->u.object->type == JS_CUSERDATA) | |
| 353 if (!strcmp(tag, v->u.object->u.user.tag)) | |
| 354 return v->u.object->u.user.data; | |
| 355 js_typeerror(J, "not a %s", tag); | |
| 356 } | |
| 357 | |
| 358 static js_Object *jsR_tofunction(js_State *J, int idx) | |
| 359 { | |
| 360 js_Value *v = stackidx(J, idx); | |
| 361 if (v->t.type == JS_TUNDEFINED || v->t.type == JS_TNULL) | |
| 362 return NULL; | |
| 363 if (v->t.type == JS_TOBJECT) | |
| 364 if (v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION) | |
| 365 return v->u.object; | |
| 366 js_typeerror(J, "not a function"); | |
| 367 } | |
| 368 | |
| 369 /* Stack manipulation */ | |
| 370 | |
| 371 int js_gettop(js_State *J) | |
| 372 { | |
| 373 return TOP - BOT; | |
| 374 } | |
| 375 | |
| 376 void js_pop(js_State *J, int n) | |
| 377 { | |
| 378 TOP -= n; | |
| 379 if (TOP < BOT) { | |
| 380 TOP = BOT; | |
| 381 js_error(J, "stack underflow!"); | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 void js_remove(js_State *J, int idx) | |
| 386 { | |
| 387 idx = idx < 0 ? TOP + idx : BOT + idx; | |
| 388 if (idx < BOT || idx >= TOP) | |
| 389 js_error(J, "stack error!"); | |
| 390 for (;idx < TOP - 1; ++idx) | |
| 391 STACK[idx] = STACK[idx+1]; | |
| 392 --TOP; | |
| 393 } | |
| 394 | |
| 395 void js_insert(js_State *J, int idx) | |
| 396 { | |
| 397 js_error(J, "not implemented yet"); | |
| 398 } | |
| 399 | |
| 400 void js_replace(js_State* J, int idx) | |
| 401 { | |
| 402 idx = idx < 0 ? TOP + idx : BOT + idx; | |
| 403 if (idx < BOT || idx >= TOP) | |
| 404 js_error(J, "stack error!"); | |
| 405 STACK[idx] = STACK[--TOP]; | |
| 406 } | |
| 407 | |
| 408 void js_copy(js_State *J, int idx) | |
| 409 { | |
| 410 CHECKSTACK(1); | |
| 411 STACK[TOP] = *stackidx(J, idx); | |
| 412 ++TOP; | |
| 413 } | |
| 414 | |
| 415 void js_dup(js_State *J) | |
| 416 { | |
| 417 CHECKSTACK(1); | |
| 418 STACK[TOP] = STACK[TOP-1]; | |
| 419 ++TOP; | |
| 420 } | |
| 421 | |
| 422 void js_dup2(js_State *J) | |
| 423 { | |
| 424 CHECKSTACK(2); | |
| 425 STACK[TOP] = STACK[TOP-2]; | |
| 426 STACK[TOP+1] = STACK[TOP-1]; | |
| 427 TOP += 2; | |
| 428 } | |
| 429 | |
| 430 void js_rot2(js_State *J) | |
| 431 { | |
| 432 /* A B -> B A */ | |
| 433 js_Value tmp = STACK[TOP-1]; /* A B (B) */ | |
| 434 STACK[TOP-1] = STACK[TOP-2]; /* A A */ | |
| 435 STACK[TOP-2] = tmp; /* B A */ | |
| 436 } | |
| 437 | |
| 438 void js_rot3(js_State *J) | |
| 439 { | |
| 440 /* A B C -> C A B */ | |
| 441 js_Value tmp = STACK[TOP-1]; /* A B C (C) */ | |
| 442 STACK[TOP-1] = STACK[TOP-2]; /* A B B */ | |
| 443 STACK[TOP-2] = STACK[TOP-3]; /* A A B */ | |
| 444 STACK[TOP-3] = tmp; /* C A B */ | |
| 445 } | |
| 446 | |
| 447 void js_rot4(js_State *J) | |
| 448 { | |
| 449 /* A B C D -> D A B C */ | |
| 450 js_Value tmp = STACK[TOP-1]; /* A B C D (D) */ | |
| 451 STACK[TOP-1] = STACK[TOP-2]; /* A B C C */ | |
| 452 STACK[TOP-2] = STACK[TOP-3]; /* A B B C */ | |
| 453 STACK[TOP-3] = STACK[TOP-4]; /* A A B C */ | |
| 454 STACK[TOP-4] = tmp; /* D A B C */ | |
| 455 } | |
| 456 | |
| 457 void js_rot2pop1(js_State *J) | |
| 458 { | |
| 459 /* A B -> B */ | |
| 460 STACK[TOP-2] = STACK[TOP-1]; | |
| 461 --TOP; | |
| 462 } | |
| 463 | |
| 464 void js_rot3pop2(js_State *J) | |
| 465 { | |
| 466 /* A B C -> C */ | |
| 467 STACK[TOP-3] = STACK[TOP-1]; | |
| 468 TOP -= 2; | |
| 469 } | |
| 470 | |
| 471 void js_rot(js_State *J, int n) | |
| 472 { | |
| 473 int i; | |
| 474 js_Value tmp = STACK[TOP-1]; | |
| 475 for (i = 1; i < n; ++i) | |
| 476 STACK[TOP-i] = STACK[TOP-i-1]; | |
| 477 STACK[TOP-i] = tmp; | |
| 478 } | |
| 479 | |
| 480 /* Property access that takes care of attributes and getters/setters */ | |
| 481 | |
| 482 int js_isarrayindex(js_State *J, const char *p, int *idx) | |
| 483 { | |
| 484 int n = 0; | |
| 485 | |
| 486 /* check for empty string */ | |
| 487 if (p[0] == 0) | |
| 488 return 0; | |
| 489 | |
| 490 /* check for '0' and integers with leading zero */ | |
| 491 if (p[0] == '0') | |
| 492 return (p[1] == 0) ? *idx = 0, 1 : 0; | |
| 493 | |
| 494 while (*p) { | |
| 495 int c = *p++; | |
| 496 if (c >= '0' && c <= '9') { | |
| 497 if (n >= INT_MAX / 10) | |
| 498 return 0; | |
| 499 n = n * 10 + (c - '0'); | |
| 500 } else { | |
| 501 return 0; | |
| 502 } | |
| 503 } | |
| 504 return *idx = n, 1; | |
| 505 } | |
| 506 | |
| 507 static void js_pushrune(js_State *J, Rune rune) | |
| 508 { | |
| 509 char buf[UTFmax + 1]; | |
| 510 if (rune >= 0) { | |
| 511 buf[runetochar(buf, &rune)] = 0; | |
| 512 js_pushstring(J, buf); | |
| 513 } else { | |
| 514 js_pushundefined(J); | |
| 515 } | |
| 516 } | |
| 517 | |
| 518 void jsR_unflattenarray(js_State *J, js_Object *obj) { | |
| 519 if (obj->type == JS_CARRAY && obj->u.a.simple) { | |
| 520 js_Property *ref; | |
| 521 int i; | |
| 522 char name[32]; | |
| 523 if (js_try(J)) { | |
| 524 obj->properties = NULL; | |
| 525 js_throw(J); | |
| 526 } | |
| 527 for (i = 0; i < obj->u.a.flat_length; ++i) { | |
| 528 js_itoa(name, i); | |
| 529 ref = jsV_setproperty(J, obj, name); | |
| 530 ref->value = obj->u.a.array[i]; | |
| 531 } | |
| 532 js_free(J, obj->u.a.array); | |
| 533 obj->u.a.simple = 0; | |
| 534 obj->u.a.flat_length = 0; | |
| 535 obj->u.a.flat_capacity = 0; | |
| 536 obj->u.a.array = NULL; | |
| 537 js_endtry(J); | |
| 538 } | |
| 539 } | |
| 540 | |
| 541 static int jsR_hasproperty(js_State *J, js_Object *obj, const char *name) | |
| 542 { | |
| 543 js_Property *ref; | |
| 544 int k; | |
| 545 | |
| 546 if (obj->type == JS_CARRAY) { | |
| 547 if (!strcmp(name, "length")) { | |
| 548 js_pushnumber(J, obj->u.a.length); | |
| 549 return 1; | |
| 550 } | |
| 551 if (obj->u.a.simple) { | |
| 552 if (js_isarrayindex(J, name, &k)) { | |
| 553 if (k >= 0 && k < obj->u.a.flat_length) { | |
| 554 js_pushvalue(J, obj->u.a.array[k]); | |
| 555 return 1; | |
| 556 } | |
| 557 return 0; | |
| 558 } | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 else if (obj->type == JS_CSTRING) { | |
| 563 if (!strcmp(name, "length")) { | |
| 564 js_pushnumber(J, obj->u.s.length); | |
| 565 return 1; | |
| 566 } | |
| 567 if (js_isarrayindex(J, name, &k)) { | |
| 568 if (k >= 0 && k < obj->u.s.length) { | |
| 569 js_pushrune(J, js_runeat(J, obj->u.s.string, k)); | |
| 570 return 1; | |
| 571 } | |
| 572 } | |
| 573 } | |
| 574 | |
| 575 else if (obj->type == JS_CREGEXP) { | |
| 576 if (!strcmp(name, "source")) { | |
| 577 js_pushstring(J, obj->u.r.source); | |
| 578 return 1; | |
| 579 } | |
| 580 if (!strcmp(name, "global")) { | |
| 581 js_pushboolean(J, obj->u.r.flags & JS_REGEXP_G); | |
| 582 return 1; | |
| 583 } | |
| 584 if (!strcmp(name, "ignoreCase")) { | |
| 585 js_pushboolean(J, obj->u.r.flags & JS_REGEXP_I); | |
| 586 return 1; | |
| 587 } | |
| 588 if (!strcmp(name, "multiline")) { | |
| 589 js_pushboolean(J, obj->u.r.flags & JS_REGEXP_M); | |
| 590 return 1; | |
| 591 } | |
| 592 if (!strcmp(name, "lastIndex")) { | |
| 593 js_pushnumber(J, obj->u.r.last); | |
| 594 return 1; | |
| 595 } | |
| 596 } | |
| 597 | |
| 598 else if (obj->type == JS_CUSERDATA) { | |
| 599 if (obj->u.user.has && obj->u.user.has(J, obj->u.user.data, name)) | |
| 600 return 1; | |
| 601 } | |
| 602 | |
| 603 ref = jsV_getproperty(J, obj, name); | |
| 604 if (ref) { | |
| 605 if (ref->getter) { | |
| 606 js_pushobject(J, ref->getter); | |
| 607 js_pushobject(J, obj); | |
| 608 js_call(J, 0); | |
| 609 } else { | |
| 610 js_pushvalue(J, ref->value); | |
| 611 } | |
| 612 return 1; | |
| 613 } | |
| 614 | |
| 615 return 0; | |
| 616 } | |
| 617 | |
| 618 static void jsR_getproperty(js_State *J, js_Object *obj, const char *name) | |
| 619 { | |
| 620 if (!jsR_hasproperty(J, obj, name)) | |
| 621 js_pushundefined(J); | |
| 622 } | |
| 623 | |
| 624 static int jsR_hasindex(js_State *J, js_Object *obj, int k) | |
| 625 { | |
| 626 char buf[32]; | |
| 627 if (obj->type == JS_CARRAY && obj->u.a.simple) { | |
| 628 if (k >= 0 && k < obj->u.a.flat_length) { | |
| 629 js_pushvalue(J, obj->u.a.array[k]); | |
| 630 return 1; | |
| 631 } | |
| 632 return 0; | |
| 633 } | |
| 634 return jsR_hasproperty(J, obj, js_itoa(buf, k)); | |
| 635 } | |
| 636 | |
| 637 static void jsR_getindex(js_State *J, js_Object *obj, int k) | |
| 638 { | |
| 639 if (!jsR_hasindex(J, obj, k)) | |
| 640 js_pushundefined(J); | |
| 641 } | |
| 642 | |
| 643 static void jsR_setarrayindex(js_State *J, js_Object *obj, int k, js_Value *value) | |
| 644 { | |
| 645 int newlen = k + 1; | |
| 646 assert(obj->u.a.simple); | |
| 647 assert(k >= 0); | |
| 648 if (newlen > JS_ARRAYLIMIT) | |
| 649 js_rangeerror(J, "array too large"); | |
| 650 if (newlen > obj->u.a.flat_length) { | |
| 651 assert(newlen == obj->u.a.flat_length + 1); | |
| 652 if (newlen > obj->u.a.flat_capacity) { | |
| 653 int newcap = obj->u.a.flat_capacity; | |
| 654 if (newcap == 0) | |
| 655 newcap = 8; | |
| 656 while (newcap < newlen) | |
| 657 newcap <<= 1; | |
| 658 obj->u.a.array = js_realloc(J, obj->u.a.array, newcap * sizeof(js_Value)); | |
| 659 obj->u.a.flat_capacity = newcap; | |
| 660 } | |
| 661 obj->u.a.flat_length = newlen; | |
| 662 } | |
| 663 if (newlen > obj->u.a.length) | |
| 664 obj->u.a.length = newlen; | |
| 665 obj->u.a.array[k] = *value; | |
| 666 } | |
| 667 | |
| 668 static void jsR_setproperty(js_State *J, js_Object *obj, const char *name, int transient) | |
| 669 { | |
| 670 js_Value *value = stackidx(J, -1); | |
| 671 js_Property *ref; | |
| 672 int k; | |
| 673 int own; | |
| 674 | |
| 675 if (obj->type == JS_CARRAY) { | |
| 676 if (!strcmp(name, "length")) { | |
| 677 double rawlen = jsV_tonumber(J, value); | |
| 678 int newlen = jsV_numbertointeger(rawlen); | |
| 679 if (newlen != rawlen || newlen < 0) | |
| 680 js_rangeerror(J, "invalid array length"); | |
| 681 if (newlen > JS_ARRAYLIMIT) | |
| 682 js_rangeerror(J, "array too large"); | |
| 683 if (obj->u.a.simple) { | |
| 684 obj->u.a.length = newlen; | |
| 685 if (newlen <= obj->u.a.flat_length) | |
| 686 obj->u.a.flat_length = newlen; | |
| 687 } else { | |
| 688 jsV_resizearray(J, obj, newlen); | |
| 689 } | |
| 690 return; | |
| 691 } | |
| 692 | |
| 693 if (js_isarrayindex(J, name, &k)) { | |
| 694 if (obj->u.a.simple) { | |
| 695 if (k >= 0 && k <= obj->u.a.flat_length) { | |
| 696 jsR_setarrayindex(J, obj, k, value); | |
| 697 } else { | |
| 698 jsR_unflattenarray(J, obj); | |
| 699 if (obj->u.a.length < k + 1) | |
| 700 obj->u.a.length = k + 1; | |
| 701 } | |
| 702 } else { | |
| 703 if (obj->u.a.length < k + 1) | |
| 704 obj->u.a.length = k + 1; | |
| 705 } | |
| 706 } | |
| 707 } | |
| 708 | |
| 709 else if (obj->type == JS_CSTRING) { | |
| 710 if (!strcmp(name, "length")) | |
| 711 goto readonly; | |
| 712 if (js_isarrayindex(J, name, &k)) | |
| 713 if (k >= 0 && k < obj->u.s.length) | |
| 714 goto readonly; | |
| 715 } | |
| 716 | |
| 717 else if (obj->type == JS_CREGEXP) { | |
| 718 if (!strcmp(name, "source")) goto readonly; | |
| 719 if (!strcmp(name, "global")) goto readonly; | |
| 720 if (!strcmp(name, "ignoreCase")) goto readonly; | |
| 721 if (!strcmp(name, "multiline")) goto readonly; | |
| 722 if (!strcmp(name, "lastIndex")) { | |
| 723 obj->u.r.last = jsV_tointeger(J, value); | |
| 724 return; | |
| 725 } | |
| 726 } | |
| 727 | |
| 728 else if (obj->type == JS_CUSERDATA) { | |
| 729 if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name)) | |
| 730 return; | |
| 731 } | |
| 732 | |
| 733 /* First try to find a setter in prototype chain */ | |
| 734 ref = jsV_getpropertyx(J, obj, name, &own); | |
| 735 if (ref) { | |
| 736 if (ref->setter) { | |
| 737 js_pushobject(J, ref->setter); | |
| 738 js_pushobject(J, obj); | |
| 739 js_pushvalue(J, *value); | |
| 740 js_call(J, 1); | |
| 741 js_pop(J, 1); | |
| 742 return; | |
| 743 } else { | |
| 744 if (J->strict) | |
| 745 if (ref->getter) | |
| 746 js_typeerror(J, "setting property '%s' that only has a getter", name); | |
| 747 if (ref->atts & JS_READONLY) | |
| 748 goto readonly; | |
| 749 } | |
| 750 } | |
| 751 | |
| 752 /* Property not found on this object, so create one */ | |
| 753 if (!ref || !own) { | |
| 754 if (transient) { | |
| 755 if (J->strict) | |
| 756 js_typeerror(J, "cannot create property '%s' on transient object", name); | |
| 757 return; | |
| 758 } | |
| 759 ref = jsV_setproperty(J, obj, name); | |
| 760 } | |
| 761 | |
| 762 if (ref) { | |
| 763 if (!(ref->atts & JS_READONLY)) | |
| 764 ref->value = *value; | |
| 765 else | |
| 766 goto readonly; | |
| 767 } | |
| 768 | |
| 769 return; | |
| 770 | |
| 771 readonly: | |
| 772 if (J->strict) | |
| 773 js_typeerror(J, "'%s' is read-only", name); | |
| 774 } | |
| 775 | |
| 776 static void jsR_setindex(js_State *J, js_Object *obj, int k, int transient) | |
| 777 { | |
| 778 char buf[32]; | |
| 779 if (obj->type == JS_CARRAY && obj->u.a.simple && k >= 0 && k <= obj->u.a.flat_length) { | |
| 780 jsR_setarrayindex(J, obj, k, stackidx(J, -1)); | |
| 781 } else { | |
| 782 jsR_setproperty(J, obj, js_itoa(buf, k), transient); | |
| 783 } | |
| 784 } | |
| 785 | |
| 786 static void jsR_defproperty(js_State *J, js_Object *obj, const char *name, | |
| 787 int atts, js_Value *value, js_Object *getter, js_Object *setter, | |
| 788 int throw) | |
| 789 { | |
| 790 js_Property *ref; | |
| 791 int k; | |
| 792 | |
| 793 if (obj->type == JS_CARRAY) { | |
| 794 if (!strcmp(name, "length")) | |
| 795 goto readonly; | |
| 796 if (obj->u.a.simple) | |
| 797 jsR_unflattenarray(J, obj); | |
| 798 } | |
| 799 | |
| 800 else if (obj->type == JS_CSTRING) { | |
| 801 if (!strcmp(name, "length")) | |
| 802 goto readonly; | |
| 803 if (js_isarrayindex(J, name, &k)) | |
| 804 if (k >= 0 && k < obj->u.s.length) | |
| 805 goto readonly; | |
| 806 } | |
| 807 | |
| 808 else if (obj->type == JS_CREGEXP) { | |
| 809 if (!strcmp(name, "source")) goto readonly; | |
| 810 if (!strcmp(name, "global")) goto readonly; | |
| 811 if (!strcmp(name, "ignoreCase")) goto readonly; | |
| 812 if (!strcmp(name, "multiline")) goto readonly; | |
| 813 if (!strcmp(name, "lastIndex")) goto readonly; | |
| 814 } | |
| 815 | |
| 816 else if (obj->type == JS_CUSERDATA) { | |
| 817 if (obj->u.user.put && obj->u.user.put(J, obj->u.user.data, name)) | |
| 818 return; | |
| 819 } | |
| 820 | |
| 821 ref = jsV_setproperty(J, obj, name); | |
| 822 if (ref) { | |
| 823 if (value) { | |
| 824 if (!(ref->atts & JS_READONLY)) | |
| 825 ref->value = *value; | |
| 826 else if (J->strict) | |
| 827 js_typeerror(J, "'%s' is read-only", name); | |
| 828 } | |
| 829 if (getter) { | |
| 830 if (!(ref->atts & JS_DONTCONF)) | |
| 831 ref->getter = getter; | |
| 832 else if (J->strict) | |
| 833 js_typeerror(J, "'%s' is non-configurable", name); | |
| 834 } | |
| 835 if (setter) { | |
| 836 if (!(ref->atts & JS_DONTCONF)) | |
| 837 ref->setter = setter; | |
| 838 else if (J->strict) | |
| 839 js_typeerror(J, "'%s' is non-configurable", name); | |
| 840 } | |
| 841 ref->atts |= atts; | |
| 842 } | |
| 843 | |
| 844 return; | |
| 845 | |
| 846 readonly: | |
| 847 if (J->strict || throw) | |
| 848 js_typeerror(J, "'%s' is read-only or non-configurable", name); | |
| 849 } | |
| 850 | |
| 851 static int jsR_delproperty(js_State *J, js_Object *obj, const char *name) | |
| 852 { | |
| 853 js_Property *ref; | |
| 854 int k; | |
| 855 | |
| 856 if (obj->type == JS_CARRAY) { | |
| 857 if (!strcmp(name, "length")) | |
| 858 goto dontconf; | |
| 859 if (obj->u.a.simple) | |
| 860 jsR_unflattenarray(J, obj); | |
| 861 } | |
| 862 | |
| 863 else if (obj->type == JS_CSTRING) { | |
| 864 if (!strcmp(name, "length")) | |
| 865 goto dontconf; | |
| 866 if (js_isarrayindex(J, name, &k)) | |
| 867 if (k >= 0 && k < obj->u.s.length) | |
| 868 goto dontconf; | |
| 869 } | |
| 870 | |
| 871 else if (obj->type == JS_CREGEXP) { | |
| 872 if (!strcmp(name, "source")) goto dontconf; | |
| 873 if (!strcmp(name, "global")) goto dontconf; | |
| 874 if (!strcmp(name, "ignoreCase")) goto dontconf; | |
| 875 if (!strcmp(name, "multiline")) goto dontconf; | |
| 876 if (!strcmp(name, "lastIndex")) goto dontconf; | |
| 877 } | |
| 878 | |
| 879 else if (obj->type == JS_CUSERDATA) { | |
| 880 if (obj->u.user.delete && obj->u.user.delete(J, obj->u.user.data, name)) | |
| 881 return 1; | |
| 882 } | |
| 883 | |
| 884 ref = jsV_getownproperty(J, obj, name); | |
| 885 if (ref) { | |
| 886 if (ref->atts & JS_DONTCONF) | |
| 887 goto dontconf; | |
| 888 jsV_delproperty(J, obj, name); | |
| 889 } | |
| 890 return 1; | |
| 891 | |
| 892 dontconf: | |
| 893 if (J->strict) | |
| 894 js_typeerror(J, "'%s' is non-configurable", name); | |
| 895 return 0; | |
| 896 } | |
| 897 | |
| 898 static void jsR_delindex(js_State *J, js_Object *obj, int k) | |
| 899 { | |
| 900 char buf[32]; | |
| 901 /* Allow deleting last element of a simple array without unflattening */ | |
| 902 if (obj->type == JS_CARRAY && obj->u.a.simple && k == obj->u.a.flat_length - 1) | |
| 903 obj->u.a.flat_length = k; | |
| 904 else | |
| 905 jsR_delproperty(J, obj, js_itoa(buf, k)); | |
| 906 } | |
| 907 | |
| 908 /* Registry, global and object property accessors */ | |
| 909 | |
| 910 const char *js_ref(js_State *J) | |
| 911 { | |
| 912 js_Value *v = stackidx(J, -1); | |
| 913 const char *s; | |
| 914 char buf[32]; | |
| 915 switch (v->t.type) { | |
| 916 case JS_TUNDEFINED: s = "_Undefined"; break; | |
| 917 case JS_TNULL: s = "_Null"; break; | |
| 918 case JS_TBOOLEAN: | |
| 919 s = v->u.boolean ? "_True" : "_False"; | |
| 920 break; | |
| 921 case JS_TOBJECT: | |
| 922 sprintf(buf, "%p", (void*)v->u.object); | |
| 923 s = js_intern(J, buf); | |
| 924 break; | |
| 925 default: | |
| 926 sprintf(buf, "%d", J->nextref++); | |
| 927 s = js_intern(J, buf); | |
| 928 break; | |
| 929 } | |
| 930 js_setregistry(J, s); | |
| 931 return s; | |
| 932 } | |
| 933 | |
| 934 void js_unref(js_State *J, const char *ref) | |
| 935 { | |
| 936 js_delregistry(J, ref); | |
| 937 } | |
| 938 | |
| 939 void js_getregistry(js_State *J, const char *name) | |
| 940 { | |
| 941 jsR_getproperty(J, J->R, name); | |
| 942 } | |
| 943 | |
| 944 void js_setregistry(js_State *J, const char *name) | |
| 945 { | |
| 946 jsR_setproperty(J, J->R, name, 0); | |
| 947 js_pop(J, 1); | |
| 948 } | |
| 949 | |
| 950 void js_delregistry(js_State *J, const char *name) | |
| 951 { | |
| 952 jsR_delproperty(J, J->R, name); | |
| 953 } | |
| 954 | |
| 955 void js_getglobal(js_State *J, const char *name) | |
| 956 { | |
| 957 jsR_getproperty(J, J->G, name); | |
| 958 } | |
| 959 | |
| 960 void js_setglobal(js_State *J, const char *name) | |
| 961 { | |
| 962 jsR_setproperty(J, J->G, name, 0); | |
| 963 js_pop(J, 1); | |
| 964 } | |
| 965 | |
| 966 void js_defglobal(js_State *J, const char *name, int atts) | |
| 967 { | |
| 968 jsR_defproperty(J, J->G, name, atts, stackidx(J, -1), NULL, NULL, 0); | |
| 969 js_pop(J, 1); | |
| 970 } | |
| 971 | |
| 972 void js_delglobal(js_State *J, const char *name) | |
| 973 { | |
| 974 jsR_delproperty(J, J->G, name); | |
| 975 } | |
| 976 | |
| 977 void js_getproperty(js_State *J, int idx, const char *name) | |
| 978 { | |
| 979 jsR_getproperty(J, js_toobject(J, idx), name); | |
| 980 } | |
| 981 | |
| 982 void js_setproperty(js_State *J, int idx, const char *name) | |
| 983 { | |
| 984 jsR_setproperty(J, js_toobject(J, idx), name, !js_isobject(J, idx)); | |
| 985 js_pop(J, 1); | |
| 986 } | |
| 987 | |
| 988 void js_defproperty(js_State *J, int idx, const char *name, int atts) | |
| 989 { | |
| 990 jsR_defproperty(J, js_toobject(J, idx), name, atts, stackidx(J, -1), NULL, NULL, 1); | |
| 991 js_pop(J, 1); | |
| 992 } | |
| 993 | |
| 994 void js_delproperty(js_State *J, int idx, const char *name) | |
| 995 { | |
| 996 jsR_delproperty(J, js_toobject(J, idx), name); | |
| 997 } | |
| 998 | |
| 999 void js_defaccessor(js_State *J, int idx, const char *name, int atts) | |
| 1000 { | |
| 1001 jsR_defproperty(J, js_toobject(J, idx), name, atts, NULL, jsR_tofunction(J, -2), jsR_tofunction(J, -1), 1); | |
| 1002 js_pop(J, 2); | |
| 1003 } | |
| 1004 | |
| 1005 int js_hasproperty(js_State *J, int idx, const char *name) | |
| 1006 { | |
| 1007 return jsR_hasproperty(J, js_toobject(J, idx), name); | |
| 1008 } | |
| 1009 | |
| 1010 void js_getindex(js_State *J, int idx, int i) | |
| 1011 { | |
| 1012 jsR_getindex(J, js_toobject(J, idx), i); | |
| 1013 } | |
| 1014 | |
| 1015 int js_hasindex(js_State *J, int idx, int i) | |
| 1016 { | |
| 1017 return jsR_hasindex(J, js_toobject(J, idx), i); | |
| 1018 } | |
| 1019 | |
| 1020 void js_setindex(js_State *J, int idx, int i) | |
| 1021 { | |
| 1022 jsR_setindex(J, js_toobject(J, idx), i, !js_isobject(J, idx)); | |
| 1023 js_pop(J, 1); | |
| 1024 } | |
| 1025 | |
| 1026 void js_delindex(js_State *J, int idx, int i) | |
| 1027 { | |
| 1028 jsR_delindex(J, js_toobject(J, idx), i); | |
| 1029 } | |
| 1030 | |
| 1031 /* Iterator */ | |
| 1032 | |
| 1033 void js_pushiterator(js_State *J, int idx, int own) | |
| 1034 { | |
| 1035 js_pushobject(J, jsV_newiterator(J, js_toobject(J, idx), own)); | |
| 1036 } | |
| 1037 | |
| 1038 const char *js_nextiterator(js_State *J, int idx) | |
| 1039 { | |
| 1040 return jsV_nextiterator(J, js_toobject(J, idx)); | |
| 1041 } | |
| 1042 | |
| 1043 /* Environment records */ | |
| 1044 | |
| 1045 js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer) | |
| 1046 { | |
| 1047 js_Environment *E = js_malloc(J, sizeof *E); | |
| 1048 E->gcmark = 0; | |
| 1049 E->gcnext = J->gcenv; | |
| 1050 J->gcenv = E; | |
| 1051 ++J->gccounter; | |
| 1052 | |
| 1053 E->outer = outer; | |
| 1054 E->variables = vars; | |
| 1055 return E; | |
| 1056 } | |
| 1057 | |
| 1058 static void js_initvar(js_State *J, const char *name, int idx) | |
| 1059 { | |
| 1060 jsR_defproperty(J, J->E->variables, name, JS_DONTENUM | JS_DONTCONF, stackidx(J, idx), NULL, NULL, 0); | |
| 1061 } | |
| 1062 | |
| 1063 static int js_hasvar(js_State *J, const char *name) | |
| 1064 { | |
| 1065 js_Environment *E = J->E; | |
| 1066 do { | |
| 1067 js_Property *ref = jsV_getproperty(J, E->variables, name); | |
| 1068 if (ref) { | |
| 1069 if (ref->getter) { | |
| 1070 js_pushobject(J, ref->getter); | |
| 1071 js_pushobject(J, E->variables); | |
| 1072 js_call(J, 0); | |
| 1073 } else { | |
| 1074 js_pushvalue(J, ref->value); | |
| 1075 } | |
| 1076 return 1; | |
| 1077 } | |
| 1078 E = E->outer; | |
| 1079 } while (E); | |
| 1080 return 0; | |
| 1081 } | |
| 1082 | |
| 1083 static void js_setvar(js_State *J, const char *name) | |
| 1084 { | |
| 1085 js_Environment *E = J->E; | |
| 1086 do { | |
| 1087 js_Property *ref = jsV_getproperty(J, E->variables, name); | |
| 1088 if (ref) { | |
| 1089 if (ref->setter) { | |
| 1090 js_pushobject(J, ref->setter); | |
| 1091 js_pushobject(J, E->variables); | |
| 1092 js_copy(J, -3); | |
| 1093 js_call(J, 1); | |
| 1094 js_pop(J, 1); | |
| 1095 return; | |
| 1096 } | |
| 1097 if (!(ref->atts & JS_READONLY)) | |
| 1098 ref->value = *stackidx(J, -1); | |
| 1099 else if (J->strict) | |
| 1100 js_typeerror(J, "'%s' is read-only", name); | |
| 1101 return; | |
| 1102 } | |
| 1103 E = E->outer; | |
| 1104 } while (E); | |
| 1105 if (J->strict) | |
| 1106 js_referenceerror(J, "assignment to undeclared variable '%s'", name); | |
| 1107 jsR_setproperty(J, J->G, name, 0); | |
| 1108 } | |
| 1109 | |
| 1110 static int js_delvar(js_State *J, const char *name) | |
| 1111 { | |
| 1112 js_Environment *E = J->E; | |
| 1113 do { | |
| 1114 js_Property *ref = jsV_getownproperty(J, E->variables, name); | |
| 1115 if (ref) { | |
| 1116 if (ref->atts & JS_DONTCONF) { | |
| 1117 if (J->strict) | |
| 1118 js_typeerror(J, "'%s' is non-configurable", name); | |
| 1119 return 0; | |
| 1120 } | |
| 1121 jsV_delproperty(J, E->variables, name); | |
| 1122 return 1; | |
| 1123 } | |
| 1124 E = E->outer; | |
| 1125 } while (E); | |
| 1126 return jsR_delproperty(J, J->G, name); | |
| 1127 } | |
| 1128 | |
| 1129 /* Function calls */ | |
| 1130 | |
| 1131 static void jsR_savescope(js_State *J, js_Environment *newE) | |
| 1132 { | |
| 1133 if (J->envtop + 1 >= JS_ENVLIMIT) | |
| 1134 js_stackoverflow(J); | |
| 1135 J->envstack[J->envtop++] = J->E; | |
| 1136 J->E = newE; | |
| 1137 } | |
| 1138 | |
| 1139 static void jsR_restorescope(js_State *J) | |
| 1140 { | |
| 1141 J->E = J->envstack[--J->envtop]; | |
| 1142 } | |
| 1143 | |
| 1144 static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environment *scope) | |
| 1145 { | |
| 1146 js_Value v; | |
| 1147 int i; | |
| 1148 | |
| 1149 jsR_savescope(J, scope); | |
| 1150 | |
| 1151 if (n > F->numparams) { | |
| 1152 js_pop(J, n - F->numparams); | |
| 1153 n = F->numparams; | |
| 1154 } | |
| 1155 | |
| 1156 for (i = n; i < F->varlen; ++i) | |
| 1157 js_pushundefined(J); | |
| 1158 | |
| 1159 jsR_run(J, F); | |
| 1160 v = *stackidx(J, -1); | |
| 1161 TOP = --BOT; /* clear stack */ | |
| 1162 js_pushvalue(J, v); | |
| 1163 | |
| 1164 jsR_restorescope(J); | |
| 1165 } | |
| 1166 | |
| 1167 static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope) | |
| 1168 { | |
| 1169 js_Value v; | |
| 1170 int i; | |
| 1171 | |
| 1172 scope = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope); | |
| 1173 | |
| 1174 jsR_savescope(J, scope); | |
| 1175 | |
| 1176 if (F->arguments) { | |
| 1177 js_newarguments(J); | |
| 1178 if (!J->strict) { | |
| 1179 js_currentfunction(J); | |
| 1180 js_defproperty(J, -2, "callee", JS_DONTENUM); | |
| 1181 } | |
| 1182 js_pushnumber(J, n); | |
| 1183 js_defproperty(J, -2, "length", JS_DONTENUM); | |
| 1184 for (i = 0; i < n; ++i) { | |
| 1185 js_copy(J, i + 1); | |
| 1186 js_setindex(J, -2, i); | |
| 1187 } | |
| 1188 js_initvar(J, "arguments", -1); | |
| 1189 js_pop(J, 1); | |
| 1190 } | |
| 1191 | |
| 1192 for (i = 0; i < n && i < F->numparams; ++i) | |
| 1193 js_initvar(J, F->vartab[i], i + 1); | |
| 1194 js_pop(J, n); | |
| 1195 | |
| 1196 for (; i < F->varlen; ++i) { | |
| 1197 js_pushundefined(J); | |
| 1198 js_initvar(J, F->vartab[i], -1); | |
| 1199 js_pop(J, 1); | |
| 1200 } | |
| 1201 | |
| 1202 jsR_run(J, F); | |
| 1203 v = *stackidx(J, -1); | |
| 1204 TOP = --BOT; /* clear stack */ | |
| 1205 js_pushvalue(J, v); | |
| 1206 | |
| 1207 jsR_restorescope(J); | |
| 1208 } | |
| 1209 | |
| 1210 static void jsR_callscript(js_State *J, int n, js_Function *F, js_Environment *scope) | |
| 1211 { | |
| 1212 js_Value v; | |
| 1213 int i; | |
| 1214 | |
| 1215 if (scope) | |
| 1216 jsR_savescope(J, scope); | |
| 1217 | |
| 1218 /* scripts take no arguments */ | |
| 1219 js_pop(J, n); | |
| 1220 | |
| 1221 for (i = 0; i < F->varlen; ++i) { | |
| 1222 /* Bug 701886: don't redefine existing vars in eval/scripts */ | |
| 1223 if (!js_hasvar(J, F->vartab[i])) { | |
| 1224 js_pushundefined(J); | |
| 1225 js_initvar(J, F->vartab[i], -1); | |
| 1226 js_pop(J, 1); | |
| 1227 } | |
| 1228 } | |
| 1229 | |
| 1230 jsR_run(J, F); | |
| 1231 v = *stackidx(J, -1); | |
| 1232 TOP = --BOT; /* clear stack */ | |
| 1233 js_pushvalue(J, v); | |
| 1234 | |
| 1235 if (scope) | |
| 1236 jsR_restorescope(J); | |
| 1237 } | |
| 1238 | |
| 1239 static void jsR_callcfunction(js_State *J, int n, int min, js_CFunction F) | |
| 1240 { | |
| 1241 int save_top; | |
| 1242 int i; | |
| 1243 js_Value v; | |
| 1244 | |
| 1245 for (i = n; i < min; ++i) | |
| 1246 js_pushundefined(J); | |
| 1247 | |
| 1248 save_top = TOP; | |
| 1249 F(J); | |
| 1250 if (TOP > save_top) { | |
| 1251 v = *stackidx(J, -1); | |
| 1252 TOP = --BOT; /* clear stack */ | |
| 1253 js_pushvalue(J, v); | |
| 1254 } else { | |
| 1255 TOP = --BOT; /* clear stack */ | |
| 1256 js_pushundefined(J); | |
| 1257 } | |
| 1258 } | |
| 1259 | |
| 1260 static void jsR_pushtrace(js_State *J, const char *name, const char *file, int line) | |
| 1261 { | |
| 1262 if (J->tracetop + 1 == JS_ENVLIMIT) | |
| 1263 js_error(J, "call stack overflow"); | |
| 1264 ++J->tracetop; | |
| 1265 J->trace[J->tracetop].name = name; | |
| 1266 J->trace[J->tracetop].file = file; | |
| 1267 J->trace[J->tracetop].line = line; | |
| 1268 } | |
| 1269 | |
| 1270 void js_call(js_State *J, int n) | |
| 1271 { | |
| 1272 js_Object *obj; | |
| 1273 int savebot; | |
| 1274 | |
| 1275 if (n < 0) | |
| 1276 js_rangeerror(J, "number of arguments cannot be negative"); | |
| 1277 | |
| 1278 if (!js_iscallable(J, -n-2)) | |
| 1279 js_typeerror(J, "%s is not callable", js_typeof(J, -n-2)); | |
| 1280 | |
| 1281 obj = js_toobject(J, -n-2); | |
| 1282 | |
| 1283 savebot = BOT; | |
| 1284 BOT = TOP - n - 1; | |
| 1285 | |
| 1286 if (obj->type == JS_CFUNCTION) { | |
| 1287 jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line); | |
| 1288 if (obj->u.f.function->lightweight) | |
| 1289 jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope); | |
| 1290 else | |
| 1291 jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope); | |
| 1292 --J->tracetop; | |
| 1293 } else if (obj->type == JS_CSCRIPT) { | |
| 1294 jsR_pushtrace(J, obj->u.f.function->name, obj->u.f.function->filename, obj->u.f.function->line); | |
| 1295 jsR_callscript(J, n, obj->u.f.function, obj->u.f.scope); | |
| 1296 --J->tracetop; | |
| 1297 } else if (obj->type == JS_CCFUNCTION) { | |
| 1298 jsR_pushtrace(J, obj->u.c.name, "native", 0); | |
| 1299 jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function); | |
| 1300 --J->tracetop; | |
| 1301 } | |
| 1302 | |
| 1303 BOT = savebot; | |
| 1304 } | |
| 1305 | |
| 1306 void js_construct(js_State *J, int n) | |
| 1307 { | |
| 1308 js_Object *obj; | |
| 1309 js_Object *prototype; | |
| 1310 js_Object *newobj; | |
| 1311 | |
| 1312 if (!js_iscallable(J, -n-1)) | |
| 1313 js_typeerror(J, "%s is not callable", js_typeof(J, -n-1)); | |
| 1314 | |
| 1315 obj = js_toobject(J, -n-1); | |
| 1316 | |
| 1317 /* built-in constructors create their own objects, give them a 'null' this */ | |
| 1318 if (obj->type == JS_CCFUNCTION && obj->u.c.constructor) { | |
| 1319 int savebot = BOT; | |
| 1320 js_pushnull(J); | |
| 1321 if (n > 0) | |
| 1322 js_rot(J, n + 1); | |
| 1323 BOT = TOP - n - 1; | |
| 1324 | |
| 1325 jsR_pushtrace(J, obj->u.c.name, "native", 0); | |
| 1326 jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.constructor); | |
| 1327 --J->tracetop; | |
| 1328 | |
| 1329 BOT = savebot; | |
| 1330 return; | |
| 1331 } | |
| 1332 | |
| 1333 /* extract the function object's prototype property */ | |
| 1334 js_getproperty(J, -n - 1, "prototype"); | |
| 1335 if (js_isobject(J, -1)) | |
| 1336 prototype = js_toobject(J, -1); | |
| 1337 else | |
| 1338 prototype = J->Object_prototype; | |
| 1339 js_pop(J, 1); | |
| 1340 | |
| 1341 /* create a new object with above prototype, and shift it into the 'this' slot */ | |
| 1342 newobj = jsV_newobject(J, JS_COBJECT, prototype); | |
| 1343 js_pushobject(J, newobj); | |
| 1344 if (n > 0) | |
| 1345 js_rot(J, n + 1); | |
| 1346 | |
| 1347 /* and save a copy to return */ | |
| 1348 js_pushobject(J, newobj); | |
| 1349 js_rot(J, n + 3); | |
| 1350 | |
| 1351 /* call the function */ | |
| 1352 js_call(J, n); | |
| 1353 | |
| 1354 /* if result is not an object, return the original object we created */ | |
| 1355 if (!js_isobject(J, -1)) { | |
| 1356 js_pop(J, 1); | |
| 1357 } else { | |
| 1358 js_rot2pop1(J); | |
| 1359 } | |
| 1360 } | |
| 1361 | |
| 1362 void js_eval(js_State *J) | |
| 1363 { | |
| 1364 if (!js_isstring(J, -1)) | |
| 1365 return; | |
| 1366 js_loadeval(J, "(eval)", js_tostring(J, -1)); | |
| 1367 js_rot2pop1(J); | |
| 1368 js_copy(J, 0); /* copy 'this' */ | |
| 1369 js_call(J, 0); | |
| 1370 } | |
| 1371 | |
| 1372 int js_pconstruct(js_State *J, int n) | |
| 1373 { | |
| 1374 int savetop = TOP - n - 2; | |
| 1375 if (js_try(J)) { | |
| 1376 /* clean up the stack to only hold the error object */ | |
| 1377 STACK[savetop] = STACK[TOP-1]; | |
| 1378 TOP = savetop + 1; | |
| 1379 return 1; | |
| 1380 } | |
| 1381 js_construct(J, n); | |
| 1382 js_endtry(J); | |
| 1383 return 0; | |
| 1384 } | |
| 1385 | |
| 1386 int js_pcall(js_State *J, int n) | |
| 1387 { | |
| 1388 int savetop = TOP - n - 2; | |
| 1389 if (js_try(J)) { | |
| 1390 /* clean up the stack to only hold the error object */ | |
| 1391 STACK[savetop] = STACK[TOP-1]; | |
| 1392 TOP = savetop + 1; | |
| 1393 return 1; | |
| 1394 } | |
| 1395 js_call(J, n); | |
| 1396 js_endtry(J); | |
| 1397 return 0; | |
| 1398 } | |
| 1399 | |
| 1400 /* Exceptions */ | |
| 1401 | |
| 1402 void *js_savetrypc(js_State *J, js_Instruction *pc) | |
| 1403 { | |
| 1404 if (J->trytop == JS_TRYLIMIT) | |
| 1405 js_trystackoverflow(J); | |
| 1406 J->trybuf[J->trytop].E = J->E; | |
| 1407 J->trybuf[J->trytop].envtop = J->envtop; | |
| 1408 J->trybuf[J->trytop].tracetop = J->tracetop; | |
| 1409 J->trybuf[J->trytop].top = J->top; | |
| 1410 J->trybuf[J->trytop].bot = J->bot; | |
| 1411 J->trybuf[J->trytop].strict = J->strict; | |
| 1412 J->trybuf[J->trytop].pc = pc; | |
| 1413 return J->trybuf[J->trytop++].buf; | |
| 1414 } | |
| 1415 | |
| 1416 void *js_savetry(js_State *J) | |
| 1417 { | |
| 1418 if (J->trytop == JS_TRYLIMIT) | |
| 1419 js_trystackoverflow(J); | |
| 1420 J->trybuf[J->trytop].E = J->E; | |
| 1421 J->trybuf[J->trytop].envtop = J->envtop; | |
| 1422 J->trybuf[J->trytop].tracetop = J->tracetop; | |
| 1423 J->trybuf[J->trytop].top = J->top; | |
| 1424 J->trybuf[J->trytop].bot = J->bot; | |
| 1425 J->trybuf[J->trytop].strict = J->strict; | |
| 1426 J->trybuf[J->trytop].pc = NULL; | |
| 1427 return J->trybuf[J->trytop++].buf; | |
| 1428 } | |
| 1429 | |
| 1430 void js_endtry(js_State *J) | |
| 1431 { | |
| 1432 if (J->trytop == 0) | |
| 1433 js_error(J, "endtry: exception stack underflow"); | |
| 1434 --J->trytop; | |
| 1435 } | |
| 1436 | |
| 1437 void js_throw(js_State *J) | |
| 1438 { | |
| 1439 if (J->trytop > 0) { | |
| 1440 js_Value v = *stackidx(J, -1); | |
| 1441 --J->trytop; | |
| 1442 J->E = J->trybuf[J->trytop].E; | |
| 1443 J->envtop = J->trybuf[J->trytop].envtop; | |
| 1444 J->tracetop = J->trybuf[J->trytop].tracetop; | |
| 1445 J->top = J->trybuf[J->trytop].top; | |
| 1446 J->bot = J->trybuf[J->trytop].bot; | |
| 1447 J->strict = J->trybuf[J->trytop].strict; | |
| 1448 js_pushvalue(J, v); | |
| 1449 longjmp(J->trybuf[J->trytop].buf, 1); | |
| 1450 } | |
| 1451 if (J->panic) | |
| 1452 J->panic(J); | |
| 1453 abort(); | |
| 1454 } | |
| 1455 | |
| 1456 /* Main interpreter loop */ | |
| 1457 | |
| 1458 static void js_dumpvalue(js_State *J, js_Value v) | |
| 1459 { | |
| 1460 switch (v.t.type) { | |
| 1461 case JS_TUNDEFINED: printf("undefined"); break; | |
| 1462 case JS_TNULL: printf("null"); break; | |
| 1463 case JS_TBOOLEAN: printf(v.u.boolean ? "true" : "false"); break; | |
| 1464 case JS_TNUMBER: printf("%.9g", v.u.number); break; | |
| 1465 case JS_TSHRSTR: printf("'%s'", v.u.shrstr); break; | |
| 1466 case JS_TLITSTR: printf("'%s'", v.u.litstr); break; | |
| 1467 case JS_TMEMSTR: printf("'%s'", v.u.memstr->p); break; | |
| 1468 case JS_TOBJECT: | |
| 1469 if (v.u.object == J->G) { | |
| 1470 printf("[Global]"); | |
| 1471 break; | |
| 1472 } | |
| 1473 switch (v.u.object->type) { | |
| 1474 case JS_COBJECT: printf("[Object %p]", (void*)v.u.object); break; | |
| 1475 case JS_CARRAY: printf("[Array %p]", (void*)v.u.object); break; | |
| 1476 case JS_CFUNCTION: | |
| 1477 printf("[Function %p, %s, %s:%d]", | |
| 1478 (void*)v.u.object, | |
| 1479 v.u.object->u.f.function->name, | |
| 1480 v.u.object->u.f.function->filename, | |
| 1481 v.u.object->u.f.function->line); | |
| 1482 break; | |
| 1483 case JS_CSCRIPT: printf("[Script %s]", v.u.object->u.f.function->filename); break; | |
| 1484 case JS_CCFUNCTION: printf("[CFunction %s]", v.u.object->u.c.name); break; | |
| 1485 case JS_CBOOLEAN: printf("[Boolean %d]", v.u.object->u.boolean); break; | |
| 1486 case JS_CNUMBER: printf("[Number %g]", v.u.object->u.number); break; | |
| 1487 case JS_CSTRING: printf("[String'%s']", v.u.object->u.s.string); break; | |
| 1488 case JS_CERROR: printf("[Error]"); break; | |
| 1489 case JS_CARGUMENTS: printf("[Arguments %p]", (void*)v.u.object); break; | |
| 1490 case JS_CITERATOR: printf("[Iterator %p]", (void*)v.u.object); break; | |
| 1491 case JS_CUSERDATA: | |
| 1492 printf("[Userdata %s %p]", v.u.object->u.user.tag, v.u.object->u.user.data); | |
| 1493 break; | |
| 1494 default: printf("[Object %p]", (void*)v.u.object); break; | |
| 1495 } | |
| 1496 break; | |
| 1497 } | |
| 1498 } | |
| 1499 | |
| 1500 static void js_stacktrace(js_State *J) | |
| 1501 { | |
| 1502 int n; | |
| 1503 printf("stack trace:\n"); | |
| 1504 for (n = J->tracetop; n >= 0; --n) { | |
| 1505 const char *name = J->trace[n].name; | |
| 1506 const char *file = J->trace[n].file; | |
| 1507 int line = J->trace[n].line; | |
| 1508 if (line > 0) { | |
| 1509 if (name[0]) | |
| 1510 printf("\tat %s (%s:%d)\n", name, file, line); | |
| 1511 else | |
| 1512 printf("\tat %s:%d\n", file, line); | |
| 1513 } else | |
| 1514 printf("\tat %s (%s)\n", name, file); | |
| 1515 } | |
| 1516 } | |
| 1517 | |
| 1518 static void js_dumpstack(js_State *J) | |
| 1519 { | |
| 1520 int i; | |
| 1521 printf("stack {\n"); | |
| 1522 for (i = 0; i < TOP; ++i) { | |
| 1523 putchar(i == BOT ? '>' : ' '); | |
| 1524 printf("%4d: ", i); | |
| 1525 js_dumpvalue(J, STACK[i]); | |
| 1526 putchar('\n'); | |
| 1527 } | |
| 1528 printf("}\n"); | |
| 1529 } | |
| 1530 | |
| 1531 void js_trap(js_State *J, int pc) | |
| 1532 { | |
| 1533 js_dumpstack(J); | |
| 1534 js_stacktrace(J); | |
| 1535 } | |
| 1536 | |
| 1537 static int jsR_isindex(js_State *J, int idx, int *k) | |
| 1538 { | |
| 1539 js_Value *v = stackidx(J, idx); | |
| 1540 if (v->t.type == JS_TNUMBER) { | |
| 1541 *k = v->u.number; | |
| 1542 return *k == v->u.number && *k >= 0; | |
| 1543 } | |
| 1544 return 0; | |
| 1545 } | |
| 1546 | |
| 1547 static void jsR_run(js_State *J, js_Function *F) | |
| 1548 { | |
| 1549 js_Function **FT = F->funtab; | |
| 1550 const char **VT = F->vartab-1; | |
| 1551 int lightweight = F->lightweight; | |
| 1552 js_Instruction *pcstart = F->code; | |
| 1553 js_Instruction *pc = F->code; | |
| 1554 enum js_OpCode opcode; | |
| 1555 int offset; | |
| 1556 int savestrict; | |
| 1557 | |
| 1558 const char *str; | |
| 1559 js_Object *obj; | |
| 1560 double x, y; | |
| 1561 unsigned int ux, uy; | |
| 1562 int ix, iy, okay; | |
| 1563 int b; | |
| 1564 int transient; | |
| 1565 | |
| 1566 savestrict = J->strict; | |
| 1567 J->strict = F->strict; | |
| 1568 | |
| 1569 #define READSTRING() \ | |
| 1570 memcpy(&str, pc, sizeof(str)); \ | |
| 1571 pc += sizeof(str) / sizeof(*pc) | |
| 1572 | |
| 1573 while (1) { | |
| 1574 if (J->gccounter > J->gcthresh) | |
| 1575 js_gc(J, 0); | |
| 1576 | |
| 1577 J->trace[J->tracetop].line = *pc++; | |
| 1578 | |
| 1579 opcode = *pc++; | |
| 1580 | |
| 1581 switch (opcode) { | |
| 1582 case OP_POP: js_pop(J, 1); break; | |
| 1583 case OP_DUP: js_dup(J); break; | |
| 1584 case OP_DUP2: js_dup2(J); break; | |
| 1585 case OP_ROT2: js_rot2(J); break; | |
| 1586 case OP_ROT3: js_rot3(J); break; | |
| 1587 case OP_ROT4: js_rot4(J); break; | |
| 1588 | |
| 1589 case OP_INTEGER: | |
| 1590 js_pushnumber(J, *pc++ - 32768); | |
| 1591 break; | |
| 1592 | |
| 1593 case OP_NUMBER: | |
| 1594 memcpy(&x, pc, sizeof(x)); | |
| 1595 pc += sizeof(x) / sizeof(*pc); | |
| 1596 js_pushnumber(J, x); | |
| 1597 break; | |
| 1598 | |
| 1599 case OP_STRING: | |
| 1600 READSTRING(); | |
| 1601 js_pushliteral(J, str); | |
| 1602 break; | |
| 1603 | |
| 1604 case OP_CLOSURE: js_newfunction(J, FT[*pc++], J->E); break; | |
| 1605 case OP_NEWOBJECT: js_newobject(J); break; | |
| 1606 case OP_NEWARRAY: js_newarray(J); break; | |
| 1607 case OP_NEWREGEXP: | |
| 1608 READSTRING(); | |
| 1609 js_newregexp(J, str, *pc++); | |
| 1610 break; | |
| 1611 | |
| 1612 case OP_UNDEF: js_pushundefined(J); break; | |
| 1613 case OP_NULL: js_pushnull(J); break; | |
| 1614 case OP_TRUE: js_pushboolean(J, 1); break; | |
| 1615 case OP_FALSE: js_pushboolean(J, 0); break; | |
| 1616 | |
| 1617 case OP_THIS: | |
| 1618 if (J->strict) { | |
| 1619 js_copy(J, 0); | |
| 1620 } else { | |
| 1621 if (js_iscoercible(J, 0)) | |
| 1622 js_copy(J, 0); | |
| 1623 else | |
| 1624 js_pushglobal(J); | |
| 1625 } | |
| 1626 break; | |
| 1627 | |
| 1628 case OP_CURRENT: | |
| 1629 js_currentfunction(J); | |
| 1630 break; | |
| 1631 | |
| 1632 case OP_GETLOCAL: | |
| 1633 if (lightweight) { | |
| 1634 CHECKSTACK(1); | |
| 1635 STACK[TOP++] = STACK[BOT + *pc++]; | |
| 1636 } else { | |
| 1637 str = VT[*pc++]; | |
| 1638 if (!js_hasvar(J, str)) | |
| 1639 js_referenceerror(J, "'%s' is not defined", str); | |
| 1640 } | |
| 1641 break; | |
| 1642 | |
| 1643 case OP_SETLOCAL: | |
| 1644 if (lightweight) { | |
| 1645 STACK[BOT + *pc++] = STACK[TOP-1]; | |
| 1646 } else { | |
| 1647 js_setvar(J, VT[*pc++]); | |
| 1648 } | |
| 1649 break; | |
| 1650 | |
| 1651 case OP_DELLOCAL: | |
| 1652 if (lightweight) { | |
| 1653 ++pc; | |
| 1654 js_pushboolean(J, 0); | |
| 1655 } else { | |
| 1656 b = js_delvar(J, VT[*pc++]); | |
| 1657 js_pushboolean(J, b); | |
| 1658 } | |
| 1659 break; | |
| 1660 | |
| 1661 case OP_GETVAR: | |
| 1662 READSTRING(); | |
| 1663 if (!js_hasvar(J, str)) | |
| 1664 js_referenceerror(J, "'%s' is not defined", str); | |
| 1665 break; | |
| 1666 | |
| 1667 case OP_HASVAR: | |
| 1668 READSTRING(); | |
| 1669 if (!js_hasvar(J, str)) | |
| 1670 js_pushundefined(J); | |
| 1671 break; | |
| 1672 | |
| 1673 case OP_SETVAR: | |
| 1674 READSTRING(); | |
| 1675 js_setvar(J, str); | |
| 1676 break; | |
| 1677 | |
| 1678 case OP_DELVAR: | |
| 1679 READSTRING(); | |
| 1680 b = js_delvar(J, str); | |
| 1681 js_pushboolean(J, b); | |
| 1682 break; | |
| 1683 | |
| 1684 case OP_IN: | |
| 1685 str = js_tostring(J, -2); | |
| 1686 if (!js_isobject(J, -1)) | |
| 1687 js_typeerror(J, "operand to 'in' is not an object"); | |
| 1688 b = js_hasproperty(J, -1, str); | |
| 1689 js_pop(J, 2 + b); | |
| 1690 js_pushboolean(J, b); | |
| 1691 break; | |
| 1692 | |
| 1693 case OP_SKIPARRAY: | |
| 1694 js_setlength(J, -1, js_getlength(J, -1) + 1); | |
| 1695 break; | |
| 1696 case OP_INITARRAY: | |
| 1697 js_setindex(J, -2, js_getlength(J, -2)); | |
| 1698 break; | |
| 1699 | |
| 1700 case OP_INITPROP: | |
| 1701 obj = js_toobject(J, -3); | |
| 1702 str = js_tostring(J, -2); | |
| 1703 jsR_setproperty(J, obj, str, 0); | |
| 1704 js_pop(J, 2); | |
| 1705 break; | |
| 1706 | |
| 1707 case OP_INITGETTER: | |
| 1708 obj = js_toobject(J, -3); | |
| 1709 str = js_tostring(J, -2); | |
| 1710 jsR_defproperty(J, obj, str, 0, NULL, jsR_tofunction(J, -1), NULL, 0); | |
| 1711 js_pop(J, 2); | |
| 1712 break; | |
| 1713 | |
| 1714 case OP_INITSETTER: | |
| 1715 obj = js_toobject(J, -3); | |
| 1716 str = js_tostring(J, -2); | |
| 1717 jsR_defproperty(J, obj, str, 0, NULL, NULL, jsR_tofunction(J, -1), 0); | |
| 1718 js_pop(J, 2); | |
| 1719 break; | |
| 1720 | |
| 1721 case OP_GETPROP: | |
| 1722 if (jsR_isindex(J, -1, &ix)) { | |
| 1723 obj = js_toobject(J, -2); | |
| 1724 jsR_getindex(J, obj, ix); | |
| 1725 } else { | |
| 1726 str = js_tostring(J, -1); | |
| 1727 obj = js_toobject(J, -2); | |
| 1728 jsR_getproperty(J, obj, str); | |
| 1729 } | |
| 1730 js_rot3pop2(J); | |
| 1731 break; | |
| 1732 | |
| 1733 case OP_GETPROP_S: | |
| 1734 READSTRING(); | |
| 1735 obj = js_toobject(J, -1); | |
| 1736 jsR_getproperty(J, obj, str); | |
| 1737 js_rot2pop1(J); | |
| 1738 break; | |
| 1739 | |
| 1740 case OP_SETPROP: | |
| 1741 if (jsR_isindex(J, -2, &ix)) { | |
| 1742 obj = js_toobject(J, -3); | |
| 1743 transient = !js_isobject(J, -3); | |
| 1744 jsR_setindex(J, obj, ix, transient); | |
| 1745 } else { | |
| 1746 str = js_tostring(J, -2); | |
| 1747 obj = js_toobject(J, -3); | |
| 1748 transient = !js_isobject(J, -3); | |
| 1749 jsR_setproperty(J, obj, str, transient); | |
| 1750 } | |
| 1751 js_rot3pop2(J); | |
| 1752 break; | |
| 1753 | |
| 1754 case OP_SETPROP_S: | |
| 1755 READSTRING(); | |
| 1756 obj = js_toobject(J, -2); | |
| 1757 transient = !js_isobject(J, -2); | |
| 1758 jsR_setproperty(J, obj, str, transient); | |
| 1759 js_rot2pop1(J); | |
| 1760 break; | |
| 1761 | |
| 1762 case OP_DELPROP: | |
| 1763 str = js_tostring(J, -1); | |
| 1764 obj = js_toobject(J, -2); | |
| 1765 b = jsR_delproperty(J, obj, str); | |
| 1766 js_pop(J, 2); | |
| 1767 js_pushboolean(J, b); | |
| 1768 break; | |
| 1769 | |
| 1770 case OP_DELPROP_S: | |
| 1771 READSTRING(); | |
| 1772 obj = js_toobject(J, -1); | |
| 1773 b = jsR_delproperty(J, obj, str); | |
| 1774 js_pop(J, 1); | |
| 1775 js_pushboolean(J, b); | |
| 1776 break; | |
| 1777 | |
| 1778 case OP_ITERATOR: | |
| 1779 if (js_iscoercible(J, -1)) { | |
| 1780 obj = jsV_newiterator(J, js_toobject(J, -1), 0); | |
| 1781 js_pop(J, 1); | |
| 1782 js_pushobject(J, obj); | |
| 1783 } | |
| 1784 break; | |
| 1785 | |
| 1786 case OP_NEXTITER: | |
| 1787 if (js_isobject(J, -1)) { | |
| 1788 obj = js_toobject(J, -1); | |
| 1789 str = jsV_nextiterator(J, obj); | |
| 1790 if (str) { | |
| 1791 js_pushstring(J, str); | |
| 1792 js_pushboolean(J, 1); | |
| 1793 } else { | |
| 1794 js_pop(J, 1); | |
| 1795 js_pushboolean(J, 0); | |
| 1796 } | |
| 1797 } else { | |
| 1798 js_pop(J, 1); | |
| 1799 js_pushboolean(J, 0); | |
| 1800 } | |
| 1801 break; | |
| 1802 | |
| 1803 /* Function calls */ | |
| 1804 | |
| 1805 case OP_EVAL: | |
| 1806 js_eval(J); | |
| 1807 break; | |
| 1808 | |
| 1809 case OP_CALL: | |
| 1810 js_call(J, *pc++); | |
| 1811 break; | |
| 1812 | |
| 1813 case OP_NEW: | |
| 1814 js_construct(J, *pc++); | |
| 1815 break; | |
| 1816 | |
| 1817 /* Unary operators */ | |
| 1818 | |
| 1819 case OP_TYPEOF: | |
| 1820 str = js_typeof(J, -1); | |
| 1821 js_pop(J, 1); | |
| 1822 js_pushliteral(J, str); | |
| 1823 break; | |
| 1824 | |
| 1825 case OP_POS: | |
| 1826 x = js_tonumber(J, -1); | |
| 1827 js_pop(J, 1); | |
| 1828 js_pushnumber(J, x); | |
| 1829 break; | |
| 1830 | |
| 1831 case OP_NEG: | |
| 1832 x = js_tonumber(J, -1); | |
| 1833 js_pop(J, 1); | |
| 1834 js_pushnumber(J, -x); | |
| 1835 break; | |
| 1836 | |
| 1837 case OP_BITNOT: | |
| 1838 ix = js_toint32(J, -1); | |
| 1839 js_pop(J, 1); | |
| 1840 js_pushnumber(J, ~ix); | |
| 1841 break; | |
| 1842 | |
| 1843 case OP_LOGNOT: | |
| 1844 b = js_toboolean(J, -1); | |
| 1845 js_pop(J, 1); | |
| 1846 js_pushboolean(J, !b); | |
| 1847 break; | |
| 1848 | |
| 1849 case OP_INC: | |
| 1850 x = js_tonumber(J, -1); | |
| 1851 js_pop(J, 1); | |
| 1852 js_pushnumber(J, x + 1); | |
| 1853 break; | |
| 1854 | |
| 1855 case OP_DEC: | |
| 1856 x = js_tonumber(J, -1); | |
| 1857 js_pop(J, 1); | |
| 1858 js_pushnumber(J, x - 1); | |
| 1859 break; | |
| 1860 | |
| 1861 case OP_POSTINC: | |
| 1862 x = js_tonumber(J, -1); | |
| 1863 js_pop(J, 1); | |
| 1864 js_pushnumber(J, x + 1); | |
| 1865 js_pushnumber(J, x); | |
| 1866 break; | |
| 1867 | |
| 1868 case OP_POSTDEC: | |
| 1869 x = js_tonumber(J, -1); | |
| 1870 js_pop(J, 1); | |
| 1871 js_pushnumber(J, x - 1); | |
| 1872 js_pushnumber(J, x); | |
| 1873 break; | |
| 1874 | |
| 1875 /* Multiplicative operators */ | |
| 1876 | |
| 1877 case OP_MUL: | |
| 1878 x = js_tonumber(J, -2); | |
| 1879 y = js_tonumber(J, -1); | |
| 1880 js_pop(J, 2); | |
| 1881 js_pushnumber(J, x * y); | |
| 1882 break; | |
| 1883 | |
| 1884 case OP_DIV: | |
| 1885 x = js_tonumber(J, -2); | |
| 1886 y = js_tonumber(J, -1); | |
| 1887 js_pop(J, 2); | |
| 1888 js_pushnumber(J, x / y); | |
| 1889 break; | |
| 1890 | |
| 1891 case OP_MOD: | |
| 1892 x = js_tonumber(J, -2); | |
| 1893 y = js_tonumber(J, -1); | |
| 1894 js_pop(J, 2); | |
| 1895 js_pushnumber(J, fmod(x, y)); | |
| 1896 break; | |
| 1897 | |
| 1898 /* Additive operators */ | |
| 1899 | |
| 1900 case OP_ADD: | |
| 1901 js_concat(J); | |
| 1902 break; | |
| 1903 | |
| 1904 case OP_SUB: | |
| 1905 x = js_tonumber(J, -2); | |
| 1906 y = js_tonumber(J, -1); | |
| 1907 js_pop(J, 2); | |
| 1908 js_pushnumber(J, x - y); | |
| 1909 break; | |
| 1910 | |
| 1911 /* Shift operators */ | |
| 1912 | |
| 1913 case OP_SHL: | |
| 1914 ix = js_toint32(J, -2); | |
| 1915 uy = js_touint32(J, -1); | |
| 1916 js_pop(J, 2); | |
| 1917 js_pushnumber(J, ix << (uy & 0x1F)); | |
| 1918 break; | |
| 1919 | |
| 1920 case OP_SHR: | |
| 1921 ix = js_toint32(J, -2); | |
| 1922 uy = js_touint32(J, -1); | |
| 1923 js_pop(J, 2); | |
| 1924 js_pushnumber(J, ix >> (uy & 0x1F)); | |
| 1925 break; | |
| 1926 | |
| 1927 case OP_USHR: | |
| 1928 ux = js_touint32(J, -2); | |
| 1929 uy = js_touint32(J, -1); | |
| 1930 js_pop(J, 2); | |
| 1931 js_pushnumber(J, ux >> (uy & 0x1F)); | |
| 1932 break; | |
| 1933 | |
| 1934 /* Relational operators */ | |
| 1935 | |
| 1936 case OP_LT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b < 0); break; | |
| 1937 case OP_GT: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b > 0); break; | |
| 1938 case OP_LE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b <= 0); break; | |
| 1939 case OP_GE: b = js_compare(J, &okay); js_pop(J, 2); js_pushboolean(J, okay && b >= 0); break; | |
| 1940 | |
| 1941 case OP_INSTANCEOF: | |
| 1942 b = js_instanceof(J); | |
| 1943 js_pop(J, 2); | |
| 1944 js_pushboolean(J, b); | |
| 1945 break; | |
| 1946 | |
| 1947 /* Equality */ | |
| 1948 | |
| 1949 case OP_EQ: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, b); break; | |
| 1950 case OP_NE: b = js_equal(J); js_pop(J, 2); js_pushboolean(J, !b); break; | |
| 1951 case OP_STRICTEQ: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, b); break; | |
| 1952 case OP_STRICTNE: b = js_strictequal(J); js_pop(J, 2); js_pushboolean(J, !b); break; | |
| 1953 | |
| 1954 case OP_JCASE: | |
| 1955 offset = *pc++; | |
| 1956 b = js_strictequal(J); | |
| 1957 if (b) { | |
| 1958 js_pop(J, 2); | |
| 1959 pc = pcstart + offset; | |
| 1960 } else { | |
| 1961 js_pop(J, 1); | |
| 1962 } | |
| 1963 break; | |
| 1964 | |
| 1965 /* Binary bitwise operators */ | |
| 1966 | |
| 1967 case OP_BITAND: | |
| 1968 ix = js_toint32(J, -2); | |
| 1969 iy = js_toint32(J, -1); | |
| 1970 js_pop(J, 2); | |
| 1971 js_pushnumber(J, ix & iy); | |
| 1972 break; | |
| 1973 | |
| 1974 case OP_BITXOR: | |
| 1975 ix = js_toint32(J, -2); | |
| 1976 iy = js_toint32(J, -1); | |
| 1977 js_pop(J, 2); | |
| 1978 js_pushnumber(J, ix ^ iy); | |
| 1979 break; | |
| 1980 | |
| 1981 case OP_BITOR: | |
| 1982 ix = js_toint32(J, -2); | |
| 1983 iy = js_toint32(J, -1); | |
| 1984 js_pop(J, 2); | |
| 1985 js_pushnumber(J, ix | iy); | |
| 1986 break; | |
| 1987 | |
| 1988 /* Try and Catch */ | |
| 1989 | |
| 1990 case OP_THROW: | |
| 1991 js_throw(J); | |
| 1992 | |
| 1993 case OP_TRY: | |
| 1994 offset = *pc++; | |
| 1995 if (js_trypc(J, pc)) { | |
| 1996 pc = J->trybuf[J->trytop].pc; | |
| 1997 } else { | |
| 1998 pc = pcstart + offset; | |
| 1999 } | |
| 2000 break; | |
| 2001 | |
| 2002 case OP_ENDTRY: | |
| 2003 js_endtry(J); | |
| 2004 break; | |
| 2005 | |
| 2006 case OP_CATCH: | |
| 2007 READSTRING(); | |
| 2008 obj = jsV_newobject(J, JS_COBJECT, NULL); | |
| 2009 js_pushobject(J, obj); | |
| 2010 js_rot2(J); | |
| 2011 js_setproperty(J, -2, str); | |
| 2012 J->E = jsR_newenvironment(J, obj, J->E); | |
| 2013 js_pop(J, 1); | |
| 2014 break; | |
| 2015 | |
| 2016 case OP_ENDCATCH: | |
| 2017 J->E = J->E->outer; | |
| 2018 break; | |
| 2019 | |
| 2020 /* With */ | |
| 2021 | |
| 2022 case OP_WITH: | |
| 2023 obj = js_toobject(J, -1); | |
| 2024 J->E = jsR_newenvironment(J, obj, J->E); | |
| 2025 js_pop(J, 1); | |
| 2026 break; | |
| 2027 | |
| 2028 case OP_ENDWITH: | |
| 2029 J->E = J->E->outer; | |
| 2030 break; | |
| 2031 | |
| 2032 /* Branching */ | |
| 2033 | |
| 2034 case OP_DEBUGGER: | |
| 2035 js_trap(J, (int)(pc - pcstart) - 1); | |
| 2036 break; | |
| 2037 | |
| 2038 case OP_JUMP: | |
| 2039 pc = pcstart + *pc; | |
| 2040 break; | |
| 2041 | |
| 2042 case OP_JTRUE: | |
| 2043 offset = *pc++; | |
| 2044 b = js_toboolean(J, -1); | |
| 2045 js_pop(J, 1); | |
| 2046 if (b) | |
| 2047 pc = pcstart + offset; | |
| 2048 break; | |
| 2049 | |
| 2050 case OP_JFALSE: | |
| 2051 offset = *pc++; | |
| 2052 b = js_toboolean(J, -1); | |
| 2053 js_pop(J, 1); | |
| 2054 if (!b) | |
| 2055 pc = pcstart + offset; | |
| 2056 break; | |
| 2057 | |
| 2058 case OP_RETURN: | |
| 2059 J->strict = savestrict; | |
| 2060 return; | |
| 2061 } | |
| 2062 } | |
| 2063 } |
