| 1 | /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- | |
| 2 | * vim: set ts=8 sw=4 et tw=78: | |
| 3 | * | |
| 4 | * ***** BEGIN LICENSE BLOCK ***** | |
| 5 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
| 6 | * | |
| 7 | * The contents of this file are subject to the Mozilla Public License Version | |
| 8 | * 1.1 (the "License"); you may not use this file except in compliance with | |
| 9 | * the License. You may obtain a copy of the License at | |
| 10 | * http://www.mozilla.org/MPL/ | |
| 11 | * | |
| 12 | * Software distributed under the License is distributed on an "AS IS" basis, | |
| 13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
| 14 | * for the specific language governing rights and limitations under the | |
| 15 | * License. | |
| 16 | * | |
| 17 | * The Original Code is Mozilla Communicator client code, released | |
| 18 | * March 31, 1998. | |
| 19 | * | |
| 20 | * The Initial Developer of the Original Code is | |
| 21 | * Netscape Communications Corporation. | |
| 22 | * Portions created by the Initial Developer are Copyright (C) 1998 | |
| 23 | * the Initial Developer. All Rights Reserved. | |
| 24 | * | |
| 25 | * Contributor(s): | |
| 26 | * | |
| 27 | * Alternatively, the contents of this file may be used under the terms of | |
| 28 | * either of the GNU General Public License Version 2 or later (the "GPL"), | |
| 29 | * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
| 30 | * in which case the provisions of the GPL or the LGPL are applicable instead | |
| 31 | * of those above. If you wish to allow use of your version of this file only | |
| 32 | * under the terms of either the GPL or the LGPL, and not to allow others to | |
| 33 | * use your version of this file under the terms of the MPL, indicate your | |
| 34 | * decision by deleting the provisions above and replace them with the notice | |
| 35 | * and other provisions required by the GPL or the LGPL. If you do not delete | |
| 36 | * the provisions above, a recipient may use your version of this file under | |
| 37 | * the terms of any one of the MPL, the GPL or the LGPL. | |
| 38 | * | |
| 39 | * ***** END LICENSE BLOCK ***** */ | |
| 40 | ||
| 41 | /* | |
| 42 | * JavaScript bytecode interpreter. | |
| 43 | */ | |
| 44 | #include "jsstddef.h" | |
| 45 | #include <stdio.h> | |
| 46 | #include <string.h> | |
| 47 | #include <math.h> | |
| 48 | #include "jstypes.h" | |
| 49 | #include "jsarena.h" /* Added by JSIFY */ | |
| 50 | #include "jsutil.h" /* Added by JSIFY */ | |
| 51 | #include "jsprf.h" | |
| 52 | #include "jsapi.h" | |
| 53 | #include "jsarray.h" | |
| 54 | #include "jsatom.h" | |
| 55 | #include "jsbool.h" | |
| 56 | #include "jscntxt.h" | |
| 57 | #include "jsconfig.h" | |
| 58 | #include "jsdbgapi.h" | |
| 59 | #include "jsfun.h" | |
| 60 | #include "jsgc.h" | |
| 61 | #include "jsinterp.h" | |
| 62 | #include "jslock.h" | |
| 63 | #include "jsnum.h" | |
| 64 | #include "jsobj.h" | |
| 65 | #include "jsopcode.h" | |
| 66 | #include "jsscope.h" | |
| 67 | #include "jsscript.h" | |
| 68 | #include "jsstr.h" | |
| 69 | ||
| 70 | #if JS_HAS_JIT | |
| 71 | #include "jsjit.h" | |
| 72 | #endif | |
| 73 | ||
| 74 | #if JS_HAS_XML_SUPPORT | |
| 75 | #include "jsxml.h" | |
| 76 | #endif | |
| 77 | ||
| 78 | #ifdef DEBUG | |
| 79 | #define ASSERT_CACHE_IS_EMPTY(cache) \ | |
| 80 | JS_BEGIN_MACRO \ | |
| 81 | JSPropertyCacheEntry *end_, *pce_, entry_; \ | |
| 82 | JSPropertyCache *cache_ = (cache); \ | |
| 83 | JS_ASSERT(cache_->empty); \ | |
| 84 | end_ = &cache_->table[PROPERTY_CACHE_SIZE]; \ | |
| 85 | for (pce_ = &cache_->table[0]; pce_ < end_; pce_++) { \ | |
| 86 | PCE_LOAD(cache_, pce_, entry_); \ | |
| 87 | JS_ASSERT(!PCE_OBJECT(entry_)); \ | |
| 88 | JS_ASSERT(!PCE_PROPERTY(entry_)); \ | |
| 89 | } \ | |
| 90 | JS_END_MACRO | |
| 91 | #else | |
| 92 | #define ASSERT_CACHE_IS_EMPTY(cache) ((void)0) | |
| 93 | #endif | |
| 94 | ||
| 95 | void | |
| 96 | js_FlushPropertyCache(JSContext *cx) | |
| 97 | 17 | { |
| 98 | JSPropertyCache *cache; | |
| 99 | ||
| 100 | 17 | cache = &cx->runtime->propertyCache; |
| 101 | 17 | if (cache->empty) { |
| 102 | ASSERT_CACHE_IS_EMPTY(cache); | |
| 103 | 17 | return; |
| 104 | } | |
| 105 | 17 | memset(cache->table, 0, sizeof cache->table); |
| 106 | 17 | cache->empty = JS_TRUE; |
| 107 | #ifdef JS_PROPERTY_CACHE_METERING | |
| 108 | cache->flushes++; | |
| 109 | #endif | |
| 110 | } | |
| 111 | ||
| 112 | void | |
| 113 | js_DisablePropertyCache(JSContext *cx) | |
| 114 | 17 | { |
| 115 | JS_ASSERT(!cx->runtime->propertyCache.disabled); | |
| 116 | 17 | cx->runtime->propertyCache.disabled = JS_TRUE; |
| 117 | } | |
| 118 | ||
| 119 | void | |
| 120 | js_EnablePropertyCache(JSContext *cx) | |
| 121 | 17 | { |
| 122 | JS_ASSERT(cx->runtime->propertyCache.disabled); | |
| 123 | ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache); | |
| 124 | 17 | cx->runtime->propertyCache.disabled = JS_FALSE; |
| 125 | } | |
| 126 | ||
| 127 | /* | |
| 128 | * Class for for/in loop property iterator objects. | |
| 129 | */ | |
| 130 | #define JSSLOT_ITER_STATE JSSLOT_PRIVATE | |
| 131 | ||
| 132 | static void | |
| 133 | prop_iterator_finalize(JSContext *cx, JSObject *obj) | |
| 134 | 22308 | { |
| 135 | jsval iter_state; | |
| 136 | jsval iteratee; | |
| 137 | ||
| 138 | /* Protect against stillborn iterators. */ | |
| 139 | 22308 | iter_state = obj->slots[JSSLOT_ITER_STATE]; |
| 140 | 22308 | iteratee = obj->slots[JSSLOT_PARENT]; |
| 141 | 22308 | if (!JSVAL_IS_NULL(iter_state) && !JSVAL_IS_PRIMITIVE(iteratee)) { |
| 142 | 2 | OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY, |
| 143 | &iter_state, NULL); | |
| 144 | } | |
| 145 | 22308 | js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]); |
| 146 | } | |
| 147 | ||
| 148 | static JSClass prop_iterator_class = { | |
| 149 | "PropertyIterator", | |
| 150 | 0, | |
| 151 | JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, | |
| 152 | JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, prop_iterator_finalize, | |
| 153 | JSCLASS_NO_OPTIONAL_MEMBERS | |
| 154 | }; | |
| 155 | ||
| 156 | /* | |
| 157 | * Stack macros and functions. These all use a local variable, jsval *sp, to | |
| 158 | * point to the next free stack slot. SAVE_SP must be called before any call | |
| 159 | * to a function that may invoke the interpreter. RESTORE_SP must be called | |
| 160 | * only after return from js_Invoke, because only js_Invoke changes fp->sp. | |
| 161 | */ | |
| 162 | #define PUSH(v) (*sp++ = (v)) | |
| 163 | #define POP() (*--sp) | |
| 164 | #ifdef DEBUG | |
| 165 | #define SAVE_SP(fp) \ | |
| 166 | (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ | |
| 167 | (fp)->sp = sp) | |
| 168 | #else | |
| 169 | #define SAVE_SP(fp) ((fp)->sp = sp) | |
| 170 | #endif | |
| 171 | #define RESTORE_SP(fp) (sp = (fp)->sp) | |
| 172 | ||
| 173 | /* | |
| 174 | * Push the generating bytecode's pc onto the parallel pc stack that runs | |
| 175 | * depth slots below the operands. | |
| 176 | * | |
| 177 | * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See | |
| 178 | * js_Interpret for these local variables' declarations and uses. | |
| 179 | */ | |
| 180 | #define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) | |
| 181 | #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) | |
| 182 | #define POP_OPND() POP() | |
| 183 | #define FETCH_OPND(n) (sp[n]) | |
| 184 | ||
| 185 | /* | |
| 186 | * Push the jsdouble d using sp, depth, and pc from the lexical environment. | |
| 187 | * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space | |
| 188 | * for it and push a reference. | |
| 189 | */ | |
| 190 | #define STORE_NUMBER(cx, n, d) \ | |
| 191 | JS_BEGIN_MACRO \ | |
| 192 | jsint i_; \ | |
| 193 | jsval v_; \ | |
| 194 | \ | |
| 195 | if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) { \ | |
| 196 | v_ = INT_TO_JSVAL(i_); \ | |
| 197 | } else { \ | |
| 198 | ok = js_NewDoubleValue(cx, d, &v_); \ | |
| 199 | if (!ok) \ | |
| 200 | goto out; \ | |
| 201 | } \ | |
| 202 | STORE_OPND(n, v_); \ | |
| 203 | JS_END_MACRO | |
| 204 | ||
| 205 | #define FETCH_NUMBER(cx, n, d) \ | |
| 206 | JS_BEGIN_MACRO \ | |
| 207 | jsval v_; \ | |
| 208 | \ | |
| 209 | v_ = FETCH_OPND(n); \ | |
| 210 | VALUE_TO_NUMBER(cx, v_, d); \ | |
| 211 | JS_END_MACRO | |
| 212 | ||
| 213 | #define FETCH_INT(cx, n, i) \ | |
| 214 | JS_BEGIN_MACRO \ | |
| 215 | jsval v_ = FETCH_OPND(n); \ | |
| 216 | if (JSVAL_IS_INT(v_)) { \ | |
| 217 | i = JSVAL_TO_INT(v_); \ | |
| 218 | } else { \ | |
| 219 | SAVE_SP(fp); \ | |
| 220 | ok = js_ValueToECMAInt32(cx, v_, &i); \ | |
| 221 | if (!ok) \ | |
| 222 | goto out; \ | |
| 223 | } \ | |
| 224 | JS_END_MACRO | |
| 225 | ||
| 226 | #define FETCH_UINT(cx, n, ui) \ | |
| 227 | JS_BEGIN_MACRO \ | |
| 228 | jsval v_ = FETCH_OPND(n); \ | |
| 229 | jsint i_; \ | |
| 230 | if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ | |
| 231 | ui = (uint32) i_; \ | |
| 232 | } else { \ | |
| 233 | SAVE_SP(fp); \ | |
| 234 | ok = js_ValueToECMAUint32(cx, v_, &ui); \ | |
| 235 | if (!ok) \ | |
| 236 | goto out; \ | |
| 237 | } \ | |
| 238 | JS_END_MACRO | |
| 239 | ||
| 240 | /* | |
| 241 | * Optimized conversion macros that test for the desired type in v before | |
| 242 | * homing sp and calling a conversion function. | |
| 243 | */ | |
| 244 | #define VALUE_TO_NUMBER(cx, v, d) \ | |
| 245 | JS_BEGIN_MACRO \ | |
| 246 | if (JSVAL_IS_INT(v)) { \ | |
| 247 | d = (jsdouble)JSVAL_TO_INT(v); \ | |
| 248 | } else if (JSVAL_IS_DOUBLE(v)) { \ | |
| 249 | d = *JSVAL_TO_DOUBLE(v); \ | |
| 250 | } else { \ | |
| 251 | SAVE_SP(fp); \ | |
| 252 | ok = js_ValueToNumber(cx, v, &d); \ | |
| 253 | if (!ok) \ | |
| 254 | goto out; \ | |
| 255 | } \ | |
| 256 | JS_END_MACRO | |
| 257 | ||
| 258 | #define POP_BOOLEAN(cx, v, b) \ | |
| 259 | JS_BEGIN_MACRO \ | |
| 260 | v = FETCH_OPND(-1); \ | |
| 261 | if (v == JSVAL_NULL) { \ | |
| 262 | b = JS_FALSE; \ | |
| 263 | } else if (JSVAL_IS_BOOLEAN(v)) { \ | |
| 264 | b = JSVAL_TO_BOOLEAN(v); \ | |
| 265 | } else { \ | |
| 266 | SAVE_SP(fp); \ | |
| 267 | ok = js_ValueToBoolean(cx, v, &b); \ | |
| 268 | if (!ok) \ | |
| 269 | goto out; \ | |
| 270 | } \ | |
| 271 | sp--; \ | |
| 272 | JS_END_MACRO | |
| 273 | ||
| 274 | #define VALUE_TO_OBJECT(cx, v, obj) \ | |
| 275 | JS_BEGIN_MACRO \ | |
| 276 | if (!JSVAL_IS_PRIMITIVE(v)) { \ | |
| 277 | obj = JSVAL_TO_OBJECT(v); \ | |
| 278 | } else { \ | |
| 279 | SAVE_SP(fp); \ | |
| 280 | obj = js_ValueToNonNullObject(cx, v); \ | |
| 281 | if (!obj) { \ | |
| 282 | ok = JS_FALSE; \ | |
| 283 | goto out; \ | |
| 284 | } \ | |
| 285 | } \ | |
| 286 | JS_END_MACRO | |
| 287 | ||
| 288 | #define FETCH_OBJECT(cx, n, v, obj) \ | |
| 289 | JS_BEGIN_MACRO \ | |
| 290 | v = FETCH_OPND(n); \ | |
| 291 | VALUE_TO_OBJECT(cx, v, obj); \ | |
| 292 | STORE_OPND(n, OBJECT_TO_JSVAL(obj)); \ | |
| 293 | JS_END_MACRO | |
| 294 | ||
| 295 | #if JS_BUG_VOID_TOSTRING | |
| 296 | #define CHECK_VOID_TOSTRING(cx, v) \ | |
| 297 | if (JSVAL_IS_VOID(v)) { \ | |
| 298 | JSString *str_; \ | |
| 299 | str_ = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \ | |
| 300 | v = STRING_TO_JSVAL(str_); \ | |
| 301 | } | |
| 302 | #else | |
| 303 | #define CHECK_VOID_TOSTRING(cx, v) ((void)0) | |
| 304 | #endif | |
| 305 | ||
| 306 | #if JS_BUG_EAGER_TOSTRING | |
| 307 | #define CHECK_EAGER_TOSTRING(hint) (hint = JSTYPE_STRING) | |
| 308 | #else | |
| 309 | #define CHECK_EAGER_TOSTRING(hint) ((void)0) | |
| 310 | #endif | |
| 311 | ||
| 312 | #define VALUE_TO_PRIMITIVE(cx, v, hint, vp) \ | |
| 313 | JS_BEGIN_MACRO \ | |
| 314 | if (JSVAL_IS_PRIMITIVE(v)) { \ | |
| 315 | CHECK_VOID_TOSTRING(cx, v); \ | |
| 316 | *vp = v; \ | |
| 317 | } else { \ | |
| 318 | SAVE_SP(fp); \ | |
| 319 | CHECK_EAGER_TOSTRING(hint); \ | |
| 320 | ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp); \ | |
| 321 | if (!ok) \ | |
| 322 | goto out; \ | |
| 323 | } \ | |
| 324 | JS_END_MACRO | |
| 325 | ||
| 326 | JS_FRIEND_API(jsval *) | |
| 327 | js_AllocRawStack(JSContext *cx, uintN nslots, void **markp) | |
| 328 | 172018 | { |
| 329 | jsval *sp; | |
| 330 | ||
| 331 | 172018 | if (markp) |
| 332 | 170258 | *markp = JS_ARENA_MARK(&cx->stackPool); |
| 333 | 172018 | JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval)); |
| 334 | 172018 | if (!sp) { |
| 335 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW, |
| 336 | (cx->fp && cx->fp->fun) | |
| 337 | ? JS_GetFunctionName(cx->fp->fun) | |
| 338 | : "script"); | |
| 339 | } | |
| 340 | 172018 | return sp; |
| 341 | } | |
| 342 | ||
| 343 | JS_FRIEND_API(void) | |
| 344 | js_FreeRawStack(JSContext *cx, void *mark) | |
| 345 | 1485 | { |
| 346 | 1485 | JS_ARENA_RELEASE(&cx->stackPool, mark); |
| 347 | } | |
| 348 | ||
| 349 | JS_FRIEND_API(jsval *) | |
| 350 | js_AllocStack(JSContext *cx, uintN nslots, void **markp) | |
| 351 | 46338 | { |
| 352 | jsval *sp, *vp, *end; | |
| 353 | JSArena *a; | |
| 354 | JSStackHeader *sh; | |
| 355 | JSStackFrame *fp; | |
| 356 | ||
| 357 | /* Callers don't check for zero nslots: we do to avoid empty segments. */ | |
| 358 | 46338 | if (nslots == 0) { |
| 359 | 0 | *markp = NULL; |
| 360 | 0 | return JS_ARENA_MARK(&cx->stackPool); |
| 361 | } | |
| 362 | ||
| 363 | /* Allocate 2 extra slots for the stack segment header we'll likely need. */ | |
| 364 | 46338 | sp = js_AllocRawStack(cx, 2 + nslots, markp); |
| 365 | 46338 | if (!sp) |
| 366 | 0 | return NULL; |
| 367 | ||
| 368 | /* Try to avoid another header if we can piggyback on the last segment. */ | |
| 369 | 46338 | a = cx->stackPool.current; |
| 370 | 46338 | sh = cx->stackHeaders; |
| 371 | 46338 | if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) { |
| 372 | /* Extend the last stack segment, give back the 2 header slots. */ | |
| 373 | 0 | sh->nslots += nslots; |
| 374 | 0 | a->avail -= 2 * sizeof(jsval); |
| 375 | } else { | |
| 376 | /* | |
| 377 | * Need a new stack segment, so we must initialize unused slots in the | |
| 378 | * current frame. See js_GC, just before marking the "operand" jsvals, | |
| 379 | * where we scan from fp->spbase to fp->sp or through fp->script->depth | |
| 380 | * (whichever covers fewer slots). | |
| 381 | */ | |
| 382 | 46338 | fp = cx->fp; |
| 383 | 46338 | if (fp && fp->script && fp->spbase) { |
| 384 | #ifdef DEBUG | |
| 385 | jsuword depthdiff = fp->script->depth * sizeof(jsval); | |
| 386 | JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff); | |
| 387 | JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff); | |
| 388 | #endif | |
| 389 | 80 | end = fp->spbase + fp->script->depth; |
| 390 | 228 | for (vp = fp->sp; vp < end; vp++) |
| 391 | 148 | *vp = JSVAL_VOID; |
| 392 | } | |
| 393 | ||
| 394 | /* Allocate and push a stack segment header from the 2 extra slots. */ | |
| 395 | 46338 | sh = (JSStackHeader *)sp; |
| 396 | 46338 | sh->nslots = nslots; |
| 397 | 46338 | sh->down = cx->stackHeaders; |
| 398 | 46338 | cx->stackHeaders = sh; |
| 399 | 46338 | sp += 2; |
| 400 | } | |
| 401 | ||
| 402 | /* | |
| 403 | * Store JSVAL_NULL using memset, to let compilers optimize as they see | |
| 404 | * fit, in case a caller allocates and pushes GC-things one by one, which | |
| 405 | * could nest a last-ditch GC that will scan this segment. | |
| 406 | */ | |
| 407 | 46338 | memset(sp, 0, nslots * sizeof(jsval)); |
| 408 | 46338 | return sp; |
| 409 | } | |
| 410 | ||
| 411 | JS_FRIEND_API(void) | |
| 412 | js_FreeStack(JSContext *cx, void *mark) | |
| 413 | 46338 | { |
| 414 | JSStackHeader *sh; | |
| 415 | jsuword slotdiff; | |
| 416 | ||
| 417 | /* Check for zero nslots allocation special case. */ | |
| 418 | 46338 | if (!mark) |
| 419 | 46338 | return; |
| 420 | ||
| 421 | /* We can assert because js_FreeStack always balances js_AllocStack. */ | |
| 422 | 46338 | sh = cx->stackHeaders; |
| 423 | JS_ASSERT(sh); | |
| 424 | ||
| 425 | /* If mark is in the current segment, reduce sh->nslots, else pop sh. */ | |
| 426 | 46338 | slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval); |
| 427 | 46338 | if (slotdiff < (jsuword)sh->nslots) |
| 428 | 0 | sh->nslots = slotdiff; |
| 429 | else | |
| 430 | 46338 | cx->stackHeaders = sh->down; |
| 431 | ||
| 432 | /* Release the stackPool space allocated since mark was set. */ | |
| 433 | 46338 | JS_ARENA_RELEASE(&cx->stackPool, mark); |
| 434 | } | |
| 435 | ||
| 436 | JSBool | |
| 437 | js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 438 | 0 | { |
| 439 | 0 | return JS_TRUE; |
| 440 | } | |
| 441 | ||
| 442 | JSBool | |
| 443 | js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 444 | 0 | { |
| 445 | 0 | return JS_TRUE; |
| 446 | } | |
| 447 | ||
| 448 | JSBool | |
| 449 | js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 450 | 0 | { |
| 451 | 0 | return JS_TRUE; |
| 452 | } | |
| 453 | ||
| 454 | JSBool | |
| 455 | js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp) | |
| 456 | 0 | { |
| 457 | 0 | return JS_TRUE; |
| 458 | } | |
| 459 | ||
| 460 | JSBool | |
| 461 | js_ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp) | |
| 462 | 639460 | { |
| 463 | 639460 | if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) { |
| 464 | /* Some objects (e.g., With) delegate 'this' to another object. */ | |
| 465 | 638138 | thisp = OBJ_THIS_OBJECT(cx, thisp); |
| 466 | 638138 | if (!thisp) |
| 467 | 0 | return JS_FALSE; |
| 468 | ||
| 469 | /* Default return value for a constructor is the new object. */ | |
| 470 | 638138 | if (fp->flags & JSFRAME_CONSTRUCTING) |
| 471 | 60617 | fp->rval = OBJECT_TO_JSVAL(thisp); |
| 472 | } else { | |
| 473 | /* | |
| 474 | * ECMA requires "the global object", but in the presence of multiple | |
| 475 | * top-level objects (windows, frames, or certain layers in the client | |
| 476 | * object model), we prefer fun's parent. An example that causes this | |
| 477 | * code to run: | |
| 478 | * | |
| 479 | * // in window w1 | |
| 480 | * function f() { return this } | |
| 481 | * function g() { return f } | |
| 482 | * | |
| 483 | * // in window w2 | |
| 484 | * var h = w1.g() | |
| 485 | * alert(h() == w1) | |
| 486 | * | |
| 487 | * The alert should display "true". | |
| 488 | */ | |
| 489 | JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING)); | |
| 490 | 1322 | if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) || |
| 491 | !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2]))) { | |
| 492 | 0 | thisp = cx->globalObject; |
| 493 | } else { | |
| 494 | jsid id; | |
| 495 | jsval v; | |
| 496 | uintN attrs; | |
| 497 | ||
| 498 | /* Walk up the parent chain. */ | |
| 499 | 1322 | thisp = JSVAL_TO_OBJECT(fp->argv[-2]); |
| 500 | 1322 | id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); |
| 501 | for (;;) { | |
| 502 | 2644 | if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs)) |
| 503 | 0 | return JS_FALSE; |
| 504 | 2644 | if (JSVAL_IS_VOID(v)) |
| 505 | 0 | v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT); |
| 506 | 2644 | if (JSVAL_IS_NULL(v)) |
| 507 | 1322 | break; |
| 508 | 1322 | thisp = JSVAL_TO_OBJECT(v); |
| 509 | 1322 | } |
| 510 | } | |
| 511 | } | |
| 512 | 639460 | fp->thisp = thisp; |
| 513 | 639460 | fp->argv[-1] = OBJECT_TO_JSVAL(thisp); |
| 514 | 639460 | return JS_TRUE; |
| 515 | } | |
| 516 | ||
| 517 | #ifdef DUMP_CALL_TABLE | |
| 518 | ||
| 519 | #include "jsclist.h" | |
| 520 | #include "jshash.h" | |
| 521 | #include "jsdtoa.h" | |
| 522 | ||
| 523 | typedef struct CallKey { | |
| 524 | jsval callee; /* callee value */ | |
| 525 | const char *filename; /* function filename or null */ | |
| 526 | uintN lineno; /* function lineno or 0 */ | |
| 527 | } CallKey; | |
| 528 | ||
| 529 | /* Compensate for typeof null == "object" brain damage. */ | |
| 530 | #define JSTYPE_NULL JSTYPE_LIMIT | |
| 531 | #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) | |
| 532 | #define TYPENAME(t) (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t]) | |
| 533 | #define NTYPEHIST (JSTYPE_LIMIT + 1) | |
| 534 | ||
| 535 | typedef struct CallValue { | |
| 536 | uint32 total; /* total call count */ | |
| 537 | uint32 recycled; /* LRU-recycled calls lost */ | |
| 538 | uint16 minargc; /* minimum argument count */ | |
| 539 | uint16 maxargc; /* maximum argument count */ | |
| 540 | struct ArgInfo { | |
| 541 | uint32 typeHist[NTYPEHIST]; /* histogram by type */ | |
| 542 | JSCList lruList; /* top 10 values LRU list */ | |
| 543 | struct ArgValCount { | |
| 544 | JSCList lruLink; /* LRU list linkage */ | |
| 545 | jsval value; /* recently passed value */ | |
| 546 | uint32 count; /* number of times passed */ | |
| 547 | char strbuf[112]; /* string conversion buffer */ | |
| 548 | } topValCounts[10]; /* top 10 value storage */ | |
| 549 | } argInfo[8]; | |
| 550 | } CallValue; | |
| 551 | ||
| 552 | typedef struct CallEntry { | |
| 553 | JSHashEntry entry; | |
| 554 | CallKey key; | |
| 555 | CallValue value; | |
| 556 | char name[32]; /* function name copy */ | |
| 557 | } CallEntry; | |
| 558 | ||
| 559 | static void * | |
| 560 | AllocCallTable(void *pool, size_t size) | |
| 561 | { | |
| 562 | return malloc(size); | |
| 563 | } | |
| 564 | ||
| 565 | static void | |
| 566 | FreeCallTable(void *pool, void *item) | |
| 567 | { | |
| 568 | free(item); | |
| 569 | } | |
| 570 | ||
| 571 | static JSHashEntry * | |
| 572 | AllocCallEntry(void *pool, const void *key) | |
| 573 | { | |
| 574 | return (JSHashEntry*) calloc(1, sizeof(CallEntry)); | |
| 575 | } | |
| 576 | ||
| 577 | static void | |
| 578 | FreeCallEntry(void *pool, JSHashEntry *he, uintN flag) | |
| 579 | { | |
| 580 | JS_ASSERT(flag == HT_FREE_ENTRY); | |
| 581 | free(he); | |
| 582 | } | |
| 583 | ||
| 584 | static JSHashAllocOps callTableAllocOps = { | |
| 585 | AllocCallTable, FreeCallTable, | |
| 586 | AllocCallEntry, FreeCallEntry | |
| 587 | }; | |
| 588 | ||
| 589 | JS_STATIC_DLL_CALLBACK(JSHashNumber) | |
| 590 | js_hash_call_key(const void *key) | |
| 591 | { | |
| 592 | CallKey *ck = (CallKey *) key; | |
| 593 | JSHashNumber hash = (jsuword)ck->callee >> 3; | |
| 594 | ||
| 595 | if (ck->filename) { | |
| 596 | hash = (hash << 4) ^ JS_HashString(ck->filename); | |
| 597 | hash = (hash << 4) ^ ck->lineno; | |
| 598 | } | |
| 599 | return hash; | |
| 600 | } | |
| 601 | ||
| 602 | JS_STATIC_DLL_CALLBACK(intN) | |
| 603 | js_compare_call_keys(const void *k1, const void *k2) | |
| 604 | { | |
| 605 | CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2; | |
| 606 | ||
| 607 | return ck1->callee == ck2->callee && | |
| 608 | ((ck1->filename && ck2->filename) | |
| 609 | ? strcmp(ck1->filename, ck2->filename) == 0 | |
| 610 | : ck1->filename == ck2->filename) && | |
| 611 | ck1->lineno == ck2->lineno; | |
| 612 | } | |
| 613 | ||
| 614 | JSHashTable *js_CallTable; | |
| 615 | size_t js_LogCallToSourceLimit; | |
| 616 | ||
| 617 | JS_STATIC_DLL_CALLBACK(intN) | |
| 618 | CallTableDumper(JSHashEntry *he, intN k, void *arg) | |
| 619 | { | |
| 620 | CallEntry *ce = (CallEntry *)he; | |
| 621 | FILE *fp = (FILE *)arg; | |
| 622 | uintN argc, i, n; | |
| 623 | struct ArgInfo *ai; | |
| 624 | JSType save, type; | |
| 625 | JSCList *cl; | |
| 626 | struct ArgValCount *avc; | |
| 627 | jsval argval; | |
| 628 | ||
| 629 | if (ce->key.filename) { | |
| 630 | /* We're called at the end of the mark phase, so mark our filenames. */ | |
| 631 | js_MarkScriptFilename(ce->key.filename); | |
| 632 | fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno); | |
| 633 | } else { | |
| 634 | fprintf(fp, "@%p ", (void *) ce->key.callee); | |
| 635 | } | |
| 636 | ||
| 637 | if (ce->name[0]) | |
| 638 | fprintf(fp, "name %s ", ce->name); | |
| 639 | fprintf(fp, "calls %lu (%lu) argc %u/%u\n", | |
| 640 | (unsigned long) ce->value.total, | |
| 641 | (unsigned long) ce->value.recycled, | |
| 642 | ce->value.minargc, ce->value.maxargc); | |
| 643 | ||
| 644 | argc = JS_MIN(ce->value.maxargc, 8); | |
| 645 | for (i = 0; i < argc; i++) { | |
| 646 | ai = &ce->value.argInfo[i]; | |
| 647 | ||
| 648 | n = 0; | |
| 649 | save = -1; | |
| 650 | for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { | |
| 651 | if (ai->typeHist[type]) { | |
| 652 | save = type; | |
| 653 | ++n; | |
| 654 | } | |
| 655 | } | |
| 656 | if (n == 1) { | |
| 657 | fprintf(fp, " arg %u type %s: %lu\n", | |
| 658 | i, TYPENAME(save), (unsigned long) ai->typeHist[save]); | |
| 659 | } else { | |
| 660 | fprintf(fp, " arg %u type histogram:\n", i); | |
| 661 | for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) { | |
| 662 | fprintf(fp, " %9s: %8lu ", | |
| 663 | TYPENAME(type), (unsigned long) ai->typeHist[type]); | |
| 664 | for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n) | |
| 665 | fputc('*', fp); | |
| 666 | fputc('\n', fp); | |
| 667 | } | |
| 668 | } | |
| 669 | ||
| 670 | fprintf(fp, " arg %u top 10 values:\n", i); | |
| 671 | n = 1; | |
| 672 | for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) { | |
| 673 | avc = (struct ArgValCount *)cl; | |
| 674 | if (!avc->count) | |
| 675 | break; | |
| 676 | argval = avc->value; | |
| 677 | fprintf(fp, " %9u: %8lu %.*s (%#lx)\n", | |
| 678 | n, (unsigned long) avc->count, | |
| 679 | sizeof avc->strbuf, avc->strbuf, argval); | |
| 680 | ++n; | |
| 681 | } | |
| 682 | } | |
| 683 | ||
| 684 | return HT_ENUMERATE_NEXT; | |
| 685 | } | |
| 686 | ||
| 687 | void | |
| 688 | js_DumpCallTable(JSContext *cx) | |
| 689 | { | |
| 690 | char name[24]; | |
| 691 | FILE *fp; | |
| 692 | static uintN dumpCount; | |
| 693 | ||
| 694 | if (!js_CallTable) | |
| 695 | return; | |
| 696 | ||
| 697 | JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7); | |
| 698 | dumpCount++; | |
| 699 | fp = fopen(name, "w"); | |
| 700 | if (!fp) | |
| 701 | return; | |
| 702 | ||
| 703 | JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp); | |
| 704 | fclose(fp); | |
| 705 | } | |
| 706 | ||
| 707 | static void | |
| 708 | LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv) | |
| 709 | { | |
| 710 | CallKey key; | |
| 711 | const char *name, *cstr; | |
| 712 | JSFunction *fun; | |
| 713 | JSHashNumber keyHash; | |
| 714 | JSHashEntry **hep, *he; | |
| 715 | CallEntry *ce; | |
| 716 | uintN i, j; | |
| 717 | jsval argval; | |
| 718 | JSType type; | |
| 719 | struct ArgInfo *ai; | |
| 720 | struct ArgValCount *avc; | |
| 721 | JSString *str; | |
| 722 | ||
| 723 | if (!js_CallTable) { | |
| 724 | js_CallTable = JS_NewHashTable(1024, js_hash_call_key, | |
| 725 | js_compare_call_keys, NULL, | |
| 726 | &callTableAllocOps, NULL); | |
| 727 | if (!js_CallTable) | |
| 728 | return; | |
| 729 | } | |
| 730 | ||
| 731 | key.callee = callee; | |
| 732 | key.filename = NULL; | |
| 733 | key.lineno = 0; | |
| 734 | name = ""; | |
| 735 | if (JSVAL_IS_FUNCTION(cx, callee)) { | |
| 736 | fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee)); | |
| 737 | if (fun->atom) | |
| 738 | name = js_AtomToPrintableString(cx, fun->atom); | |
| 739 | if (fun->interpreted) { | |
| 740 | key.filename = fun->u.script->filename; | |
| 741 | key.lineno = fun->u.script->lineno; | |
| 742 | } | |
| 743 | } | |
| 744 | keyHash = js_hash_call_key(&key); | |
| 745 | ||
| 746 | hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key); | |
| 747 | he = *hep; | |
| 748 | if (he) { | |
| 749 | ce = (CallEntry *) he; | |
| 750 | JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0); | |
| 751 | } else { | |
| 752 | he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL); | |
| 753 | if (!he) | |
| 754 | return; | |
| 755 | ce = (CallEntry *) he; | |
| 756 | ce->entry.key = &ce->key; | |
| 757 | ce->entry.value = &ce->value; | |
| 758 | ce->key = key; | |
| 759 | for (i = 0; i < 8; i++) { | |
| 760 | ai = &ce->value.argInfo[i]; | |
| 761 | JS_INIT_CLIST(&ai->lruList); | |
| 762 | for (j = 0; j < 10; j++) | |
| 763 | JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList); | |
| 764 | } | |
| 765 | strncpy(ce->name, name, sizeof ce->name); | |
| 766 | } | |
| 767 | ||
| 768 | ++ce->value.total; | |
| 769 | if (ce->value.minargc < argc) | |
| 770 | ce->value.minargc = argc; | |
| 771 | if (ce->value.maxargc < argc) | |
| 772 | ce->value.maxargc = argc; | |
| 773 | if (argc > 8) | |
| 774 | argc = 8; | |
| 775 | for (i = 0; i < argc; i++) { | |
| 776 | ai = &ce->value.argInfo[i]; | |
| 777 | argval = argv[i]; | |
| 778 | type = TYPEOF(cx, argval); | |
| 779 | ++ai->typeHist[type]; | |
| 780 | ||
| 781 | for (j = 0; ; j++) { | |
| 782 | if (j == 10) { | |
| 783 | avc = (struct ArgValCount *) ai->lruList.next; | |
| 784 | ce->value.recycled += avc->count; | |
| 785 | avc->value = argval; | |
| 786 | avc->count = 1; | |
| 787 | break; | |
| 788 | } | |
| 789 | avc = &ai->topValCounts[j]; | |
| 790 | if (avc->value == argval) { | |
| 791 | ++avc->count; | |
| 792 | break; | |
| 793 | } | |
| 794 | } | |
| 795 | ||
| 796 | /* Move avc to the back of the LRU list. */ | |
| 797 | JS_REMOVE_LINK(&avc->lruLink); | |
| 798 | JS_APPEND_LINK(&avc->lruLink, &ai->lruList); | |
| 799 | ||
| 800 | str = NULL; | |
| 801 | cstr = ""; | |
| 802 | switch (TYPEOF(cx, argval)) { | |
| 803 | case JSTYPE_VOID: | |
| 804 | cstr = js_type_str[JSTYPE_VOID]; | |
| 805 | break; | |
| 806 | case JSTYPE_NULL: | |
| 807 | cstr = js_null_str; | |
| 808 | break; | |
| 809 | case JSTYPE_BOOLEAN: | |
| 810 | cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)]; | |
| 811 | break; | |
| 812 | case JSTYPE_NUMBER: | |
| 813 | if (JSVAL_IS_INT(argval)) { | |
| 814 | JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld", | |
| 815 | JSVAL_TO_INT(argval)); | |
| 816 | } else { | |
| 817 | JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0, | |
| 818 | *JSVAL_TO_DOUBLE(argval)); | |
| 819 | } | |
| 820 | continue; | |
| 821 | case JSTYPE_STRING: | |
| 822 | str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"'); | |
| 823 | break; | |
| 824 | case JSTYPE_FUNCTION: | |
| 825 | if (JSVAL_IS_FUNCTION(cx, argval)) { | |
| 826 | fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval)); | |
| 827 | if (fun && fun->atom) { | |
| 828 | str = ATOM_TO_STRING(fun->atom); | |
| 829 | break; | |
| 830 | } | |
| 831 | } | |
| 832 | /* FALL THROUGH */ | |
| 833 | case JSTYPE_OBJECT: | |
| 834 | js_LogCallToSourceLimit = sizeof avc->strbuf; | |
| 835 | cx->options |= JSOPTION_LOGCALL_TOSOURCE; | |
| 836 | str = js_ValueToSource(cx, argval); | |
| 837 | cx->options &= ~JSOPTION_LOGCALL_TOSOURCE; | |
| 838 | break; | |
| 839 | } | |
| 840 | if (str) | |
| 841 | cstr = JS_GetStringBytes(str); | |
| 842 | strncpy(avc->strbuf, cstr, sizeof avc->strbuf); | |
| 843 | } | |
| 844 | } | |
| 845 | ||
| 846 | #endif /* DUMP_CALL_TABLE */ | |
| 847 | ||
| 848 | /* | |
| 849 | * Find a function reference and its 'this' object implicit first parameter | |
| 850 | * under argc arguments on cx's stack, and call the function. Push missing | |
| 851 | * required arguments, allocate declared local variables, and pop everything | |
| 852 | * when done. Then push the return value. | |
| 853 | */ | |
| 854 | JS_FRIEND_API(JSBool) | |
| 855 | js_Invoke(JSContext *cx, uintN argc, uintN flags) | |
| 856 | 517025 | { |
| 857 | void *mark; | |
| 858 | JSStackFrame *fp, frame; | |
| 859 | jsval *sp, *newsp, *limit; | |
| 860 | jsval *vp, v; | |
| 861 | JSObject *funobj, *parent, *thisp; | |
| 862 | JSBool ok; | |
| 863 | JSClass *clasp; | |
| 864 | JSObjectOps *ops; | |
| 865 | JSNative native; | |
| 866 | JSFunction *fun; | |
| 867 | JSScript *script; | |
| 868 | uintN nslots, nvars, nalloc, surplus; | |
| 869 | JSInterpreterHook hook; | |
| 870 | void *hookData; | |
| 871 | ||
| 872 | /* Mark the top of stack and load frequently-used registers. */ | |
| 873 | 517025 | mark = JS_ARENA_MARK(&cx->stackPool); |
| 874 | 517025 | fp = cx->fp; |
| 875 | 517025 | sp = fp->sp; |
| 876 | ||
| 877 | /* | |
| 878 | * Set vp to the callee value's stack slot (it's where rval goes). | |
| 879 | * Once vp is set, control should flow through label out2: to return. | |
| 880 | * Set frame.rval early so native class and object ops can throw and | |
| 881 | * return false, causing a goto out2 with ok set to false. Also set | |
| 882 | * frame.flags to flags so that ComputeThis can test bits in it. | |
| 883 | */ | |
| 884 | 517025 | vp = sp - (2 + argc); |
| 885 | 517025 | v = *vp; |
| 886 | 517025 | frame.rval = JSVAL_VOID; |
| 887 | 517025 | frame.flags = flags; |
| 888 | 517025 | thisp = JSVAL_TO_OBJECT(vp[1]); |
| 889 | ||
| 890 | /* | |
| 891 | * A callee must be an object reference, unless its |this| parameter | |
| 892 | * implements the __noSuchMethod__ method, in which case that method will | |
| 893 | * be called like so: | |
| 894 | * | |
| 895 | * thisp.__noSuchMethod__(id, args) | |
| 896 | * | |
| 897 | * where id is the name of the method that this invocation attempted to | |
| 898 | * call by name, and args is an Array containing this invocation's actual | |
| 899 | * parameters. | |
| 900 | */ | |
| 901 | 517025 | if (JSVAL_IS_PRIMITIVE(v)) { |
| 902 | #if JS_HAS_NO_SUCH_METHOD | |
| 903 | jsid id; | |
| 904 | jsbytecode *pc; | |
| 905 | jsatomid atomIndex; | |
| 906 | JSAtom *atom; | |
| 907 | JSObject *argsobj; | |
| 908 | JSArena *a; | |
| 909 | ||
| 910 | 0 | if (!fp->script || (flags & JSINVOKE_INTERNAL)) |
| 911 | goto bad; | |
| 912 | ||
| 913 | /* | |
| 914 | * We must ComputeThis here to censor Call objects; performance hit, | |
| 915 | * but at least it's idempotent. | |
| 916 | * | |
| 917 | * Normally, we call ComputeThis after all frame members have been | |
| 918 | * set, and in particular, after any revision of the callee value at | |
| 919 | * *vp due to clasp->convert (see below). This matters because | |
| 920 | * ComputeThis may access *vp via fp->argv[-2], to follow the parent | |
| 921 | * chain to a global object to use as the |this| parameter. | |
| 922 | * | |
| 923 | * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be | |
| 924 | * any such defaulting of |this| to callee (v, *vp) ancestor. | |
| 925 | */ | |
| 926 | 0 | frame.argv = vp + 2; |
| 927 | 0 | ok = js_ComputeThis(cx, thisp, &frame); |
| 928 | 0 | if (!ok) |
| 929 | 0 | goto out2; |
| 930 | 0 | thisp = frame.thisp; |
| 931 | ||
| 932 | 0 | id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom); |
| 933 | 0 | if (OBJECT_IS_XML(cx, thisp)) { |
| 934 | JSXMLObjectOps *ops; | |
| 935 | ||
| 936 | 0 | ops = (JSXMLObjectOps *) thisp->map->ops; |
| 937 | 0 | thisp = ops->getMethod(cx, thisp, id, &v); |
| 938 | 0 | if (!thisp) { |
| 939 | 0 | ok = JS_FALSE; |
| 940 | 0 | goto out2; |
| 941 | } | |
| 942 | 0 | vp[1] = OBJECT_TO_JSVAL(thisp); |
| 943 | } else { | |
| 944 | 0 | ok = OBJ_GET_PROPERTY(cx, thisp, id, &v); |
| 945 | } | |
| 946 | 0 | if (!ok) |
| 947 | 0 | goto out2; |
| 948 | 0 | if (JSVAL_IS_PRIMITIVE(v)) |
| 949 | goto bad; | |
| 950 | ||
| 951 | 0 | pc = (jsbytecode *) vp[-(intN)fp->script->depth]; |
| 952 | 0 | switch ((JSOp) *pc) { |
| 953 | case JSOP_NAME: | |
| 954 | case JSOP_GETPROP: | |
| 955 | #if JS_HAS_XML_SUPPORT | |
| 956 | case JSOP_GETMETHOD: | |
| 957 | #endif | |
| 958 | 0 | atomIndex = GET_ATOM_INDEX(pc); |
| 959 | 0 | atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex); |
| 960 | 0 | argsobj = js_NewArrayObject(cx, argc, vp + 2); |
| 961 | 0 | if (!argsobj) { |
| 962 | 0 | ok = JS_FALSE; |
| 963 | 0 | goto out2; |
| 964 | } | |
| 965 | ||
| 966 | 0 | sp = vp + 4; |
| 967 | 0 | if (argc < 2) { |
| 968 | 0 | a = cx->stackPool.current; |
| 969 | 0 | if ((jsuword)sp > a->limit) { |
| 970 | /* | |
| 971 | * Arguments must be contiguous, and must include argv[-1] | |
| 972 | * and argv[-2], so allocate more stack, advance sp, and | |
| 973 | * set newsp[1] to thisp (vp[1]). The other argv elements | |
| 974 | * will be set below, using negative indexing from sp. | |
| 975 | */ | |
| 976 | 0 | newsp = js_AllocRawStack(cx, 4, NULL); |
| 977 | 0 | if (!newsp) { |
| 978 | 0 | ok = JS_FALSE; |
| 979 | 0 | goto out2; |
| 980 | } | |
| 981 | 0 | newsp[1] = OBJECT_TO_JSVAL(thisp); |
| 982 | 0 | sp = newsp + 4; |
| 983 | 0 | } else if ((jsuword)sp > a->avail) { |
| 984 | /* | |
| 985 | * Inline, optimized version of JS_ARENA_ALLOCATE to claim | |
| 986 | * the small number of words not already allocated as part | |
| 987 | * of the caller's operand stack. | |
| 988 | */ | |
| 989 | JS_ArenaCountAllocation(&cx->stackPool, | |
| 990 | (jsuword)sp - a->avail); | |
| 991 | 0 | a->avail = (jsuword)sp; |
| 992 | } | |
| 993 | } | |
| 994 | ||
| 995 | 0 | sp[-4] = v; |
| 996 | JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp)); | |
| 997 | 0 | sp[-2] = ATOM_KEY(atom); |
| 998 | 0 | sp[-1] = OBJECT_TO_JSVAL(argsobj); |
| 999 | 0 | fp->sp = sp; |
| 1000 | 0 | argc = 2; |
| 1001 | break; | |
| 1002 | ||
| 1003 | default: | |
| 1004 | goto bad; | |
| 1005 | } | |
| 1006 | #else | |
| 1007 | goto bad; | |
| 1008 | #endif | |
| 1009 | } | |
| 1010 | ||
| 1011 | 517025 | funobj = JSVAL_TO_OBJECT(v); |
| 1012 | 517025 | parent = OBJ_GET_PARENT(cx, funobj); |
| 1013 | 517025 | clasp = OBJ_GET_CLASS(cx, funobj); |
| 1014 | 517025 | if (clasp != &js_FunctionClass) { |
| 1015 | /* Function is inlined, all other classes use object ops. */ | |
| 1016 | 0 | ops = funobj->map->ops; |
| 1017 | ||
| 1018 | /* | |
| 1019 | * XXX this makes no sense -- why convert to function if clasp->call? | |
| 1020 | * XXX better to call that hook without converting | |
| 1021 | * XXX the only thing that needs fixing is liveconnect | |
| 1022 | * | |
| 1023 | * Try converting to function, for closure and API compatibility. | |
| 1024 | * We attempt the conversion under all circumstances for 1.2, but | |
| 1025 | * only if there is a call op defined otherwise. | |
| 1026 | */ | |
| 1027 | 0 | if (JS_VERSION_IS_1_2(cx) || |
| 1028 | ((ops == &js_ObjectOps) ? clasp->call : ops->call)) { | |
| 1029 | 0 | ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v); |
| 1030 | 0 | if (!ok) |
| 1031 | 0 | goto out2; |
| 1032 | ||
| 1033 | 0 | if (JSVAL_IS_FUNCTION(cx, v)) { |
| 1034 | /* Make vp refer to funobj to keep it available as argv[-2]. */ | |
| 1035 | 0 | *vp = v; |
| 1036 | 0 | funobj = JSVAL_TO_OBJECT(v); |
| 1037 | 0 | parent = OBJ_GET_PARENT(cx, funobj); |
| 1038 | 0 | goto have_fun; |
| 1039 | } | |
| 1040 | } | |
| 1041 | 0 | fun = NULL; |
| 1042 | 0 | script = NULL; |
| 1043 | 0 | nslots = nvars = 0; |
| 1044 | ||
| 1045 | /* Try a call or construct native object op. */ | |
| 1046 | 0 | native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call; |
| 1047 | 0 | if (!native) |
| 1048 | goto bad; | |
| 1049 | } else { | |
| 1050 | 517025 | have_fun: |
| 1051 | /* Get private data and set derived locals from it. */ | |
| 1052 | 517025 | fun = (JSFunction *) JS_GetPrivate(cx, funobj); |
| 1053 | 517025 | if (fun->interpreted) { |
| 1054 | 242 | native = NULL; |
| 1055 | 242 | script = fun->u.script; |
| 1056 | } else { | |
| 1057 | 516783 | native = fun->u.native; |
| 1058 | 516783 | script = NULL; |
| 1059 | } | |
| 1060 | 517025 | nslots = (fun->nargs > argc) ? fun->nargs - argc : 0; |
| 1061 | 517025 | nslots += fun->extra; |
| 1062 | 517025 | nvars = fun->nvars; |
| 1063 | ||
| 1064 | /* Handle bound method special case. */ | |
| 1065 | 517025 | if (fun->flags & JSFUN_BOUND_METHOD) |
| 1066 | 0 | thisp = parent; |
| 1067 | } | |
| 1068 | ||
| 1069 | /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */ | |
| 1070 | 517025 | frame.varobj = NULL; |
| 1071 | 517025 | frame.callobj = frame.argsobj = NULL; |
| 1072 | 517025 | frame.script = script; |
| 1073 | 517025 | frame.fun = fun; |
| 1074 | 517025 | frame.argc = argc; |
| 1075 | 517025 | frame.argv = sp - argc; |
| 1076 | 517025 | frame.nvars = nvars; |
| 1077 | 517025 | frame.vars = sp; |
| 1078 | 517025 | frame.down = fp; |
| 1079 | 517025 | frame.annotation = NULL; |
| 1080 | 517025 | frame.scopeChain = NULL; /* set below for real, after cx->fp is set */ |
| 1081 | 517025 | frame.pc = NULL; |
| 1082 | 517025 | frame.spbase = NULL; |
| 1083 | 517025 | frame.sharpDepth = 0; |
| 1084 | 517025 | frame.sharpArray = NULL; |
| 1085 | 517025 | frame.dormantNext = NULL; |
| 1086 | 517025 | frame.xmlNamespace = NULL; |
| 1087 | ||
| 1088 | /* Compute the 'this' parameter and store it in frame as frame.thisp. */ | |
| 1089 | 517025 | ok = js_ComputeThis(cx, thisp, &frame); |
| 1090 | 517025 | if (!ok) |
| 1091 | 517025 | goto out2; |
| 1092 | ||
| 1093 | /* From here on, control must flow through label out: to return. */ | |
| 1094 | 517025 | cx->fp = &frame; |
| 1095 | ||
| 1096 | /* Init these now in case we goto out before first hook call. */ | |
| 1097 | 517025 | hook = cx->runtime->callHook; |
| 1098 | 517025 | hookData = NULL; |
| 1099 | ||
| 1100 | /* Check for argument slots required by the function. */ | |
| 1101 | 517025 | if (nslots) { |
| 1102 | /* All arguments must be contiguous, so we may have to copy actuals. */ | |
| 1103 | 52568 | nalloc = nslots; |
| 1104 | 52568 | limit = (jsval *) cx->stackPool.current->limit; |
| 1105 | 52568 | if (sp + nslots > limit) { |
| 1106 | /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */ | |
| 1107 | 0 | nalloc += 2 + argc; |
| 1108 | } else { | |
| 1109 | /* Take advantage of surplus slots in the caller's frame depth. */ | |
| 1110 | JS_ASSERT((jsval *)mark >= sp); | |
| 1111 | 52568 | surplus = (jsval *)mark - sp; |
| 1112 | 52568 | nalloc -= surplus; |
| 1113 | } | |
| 1114 | ||
| 1115 | /* Check whether we have enough space in the caller's frame. */ | |
| 1116 | 52568 | if ((intN)nalloc > 0) { |
| 1117 | /* Need space for actuals plus missing formals minus surplus. */ | |
| 1118 | 1518 | newsp = js_AllocRawStack(cx, nalloc, NULL); |
| 1119 | 1518 | if (!newsp) { |
| 1120 | 0 | ok = JS_FALSE; |
| 1121 | 0 | goto out; |
| 1122 | } | |
| 1123 | ||
| 1124 | /* If we couldn't allocate contiguous args, copy actuals now. */ | |
| 1125 | 1518 | if (newsp != mark) { |
| 1126 | JS_ASSERT(sp + nslots > limit); | |
| 1127 | JS_ASSERT(2 + argc + nslots == nalloc); | |
| 1128 | 0 | *newsp++ = vp[0]; |
| 1129 | 0 | *newsp++ = vp[1]; |
| 1130 | 0 | if (argc) |
| 1131 | 0 | memcpy(newsp, frame.argv, argc * sizeof(jsval)); |
| 1132 | 0 | frame.argv = newsp; |
| 1133 | 0 | sp = frame.vars = newsp + argc; |
| 1134 | } | |
| 1135 | } | |
| 1136 | ||
| 1137 | /* Advance frame.vars to make room for the missing args. */ | |
| 1138 | 52568 | frame.vars += nslots; |
| 1139 | ||
| 1140 | /* Push void to initialize missing args. */ | |
| 1141 | do { | |
| 1142 | 53792 | PUSH(JSVAL_VOID); |
| 1143 | 53792 | } while (--nslots != 0); |
| 1144 | } | |
| 1145 | JS_ASSERT(nslots == 0); | |
| 1146 | ||
| 1147 | /* Now allocate stack space for local variables. */ | |
| 1148 | 517025 | if (nvars) { |
| 1149 | JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars); | |
| 1150 | 242 | surplus = (jsval *)cx->stackPool.current->avail - frame.vars; |
| 1151 | 242 | if (surplus < nvars) { |
| 1152 | 242 | newsp = js_AllocRawStack(cx, nvars, NULL); |
| 1153 | 242 | if (!newsp) { |
| 1154 | 0 | ok = JS_FALSE; |
| 1155 | 0 | goto out; |
| 1156 | } | |
| 1157 | 242 | if (newsp != sp) { |
| 1158 | /* NB: Discontinuity between argv and vars. */ | |
| 1159 | 0 | sp = frame.vars = newsp; |
| 1160 | } | |
| 1161 | } | |
| 1162 | ||
| 1163 | /* Push void to initialize local variables. */ | |
| 1164 | do { | |
| 1165 | 326 | PUSH(JSVAL_VOID); |
| 1166 | 326 | } while (--nvars != 0); |
| 1167 | } | |
| 1168 | JS_ASSERT(nvars == 0); | |
| 1169 | ||
| 1170 | /* Store the current sp in frame before calling fun. */ | |
| 1171 | 517025 | SAVE_SP(&frame); |
| 1172 | ||
| 1173 | /* call the hook if present */ | |
| 1174 | 517025 | if (hook && (native || script)) |
| 1175 | 0 | hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData); |
| 1176 | ||
| 1177 | /* Call the function, either a native method or an interpreted script. */ | |
| 1178 | 517025 | if (native) { |
| 1179 | #if JS_HAS_LVALUE_RETURN | |
| 1180 | /* Set by JS_SetCallReturnValue2, used to return reference types. */ | |
| 1181 | 516783 | cx->rval2set = JS_FALSE; |
| 1182 | #endif | |
| 1183 | ||
| 1184 | /* If native, use caller varobj and scopeChain for eval. */ | |
| 1185 | 516783 | frame.varobj = fp->varobj; |
| 1186 | 516783 | frame.scopeChain = fp->scopeChain; |
| 1187 | 516783 | ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval); |
| 1188 | JS_RUNTIME_METER(cx->runtime, nativeCalls); | |
| 1189 | 242 | } else if (script) { |
| 1190 | #ifdef DUMP_CALL_TABLE | |
| 1191 | LogCall(cx, *vp, argc, frame.argv); | |
| 1192 | #endif | |
| 1193 | /* Use parent scope so js_GetCallObject can find the right "Call". */ | |
| 1194 | 242 | frame.scopeChain = parent; |
| 1195 | 242 | if (fun->flags & JSFUN_HEAVYWEIGHT) { |
| 1196 | #if JS_HAS_CALL_OBJECT | |
| 1197 | /* Scope with a call object parented by the callee's parent. */ | |
| 1198 | 0 | if (!js_GetCallObject(cx, &frame, parent)) { |
| 1199 | 0 | ok = JS_FALSE; |
| 1200 | 0 | goto out; |
| 1201 | } | |
| 1202 | #else | |
| 1203 | /* Bad old code used the function as a proxy for all calls to it. */ | |
| 1204 | frame.scopeChain = funobj; | |
| 1205 | #endif | |
| 1206 | } | |
| 1207 | 242 | ok = js_Interpret(cx, script->code, &v); |
| 1208 | } else { | |
| 1209 | /* fun might be onerror trying to report a syntax error in itself. */ | |
| 1210 | 0 | frame.scopeChain = NULL; |
| 1211 | 0 | ok = JS_TRUE; |
| 1212 | } | |
| 1213 | ||
| 1214 | 517025 | out: |
| 1215 | 517025 | if (hookData) { |
| 1216 | 0 | hook = cx->runtime->callHook; |
| 1217 | 0 | if (hook) |
| 1218 | 0 | hook(cx, &frame, JS_FALSE, &ok, hookData); |
| 1219 | } | |
| 1220 | #if JS_HAS_CALL_OBJECT | |
| 1221 | /* If frame has a call object, sync values and clear back-pointer. */ | |
| 1222 | 517025 | if (frame.callobj) |
| 1223 | 0 | ok &= js_PutCallObject(cx, &frame); |
| 1224 | #endif | |
| 1225 | #if JS_HAS_ARGS_OBJECT | |
| 1226 | /* If frame has an arguments object, sync values and clear back-pointer. */ | |
| 1227 | 517025 | if (frame.argsobj) |
| 1228 | 0 | ok &= js_PutArgsObject(cx, &frame); |
| 1229 | #endif | |
| 1230 | ||
| 1231 | /* Restore cx->fp now that we're done releasing frame objects. */ | |
| 1232 | 517025 | cx->fp = fp; |
| 1233 | ||
| 1234 | 517025 | out2: |
| 1235 | /* Pop everything we may have allocated off the stack. */ | |
| 1236 | 517025 | JS_ARENA_RELEASE(&cx->stackPool, mark); |
| 1237 | ||
| 1238 | /* Store the return value and restore sp just above it. */ | |
| 1239 | 517025 | *vp = frame.rval; |
| 1240 | 517025 | fp->sp = vp + 1; |
| 1241 | ||
| 1242 | /* | |
| 1243 | * Store the location of the JSOP_CALL or JSOP_EVAL that generated the | |
| 1244 | * return value, but only if this is an external (compiled from script | |
| 1245 | * source) call that has stack budget for the generating pc. | |
| 1246 | */ | |
| 1247 | 517025 | if (fp->script && !(flags & JSINVOKE_INTERNAL)) |
| 1248 | 470687 | vp[-(intN)fp->script->depth] = (jsval)fp->pc; |
| 1249 | 517025 | return ok; |
| 1250 | ||
| 1251 | 0 | bad: |
| 1252 | 0 | js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT); |
| 1253 | 0 | ok = JS_FALSE; |
| 1254 | 0 | goto out2; |
| 1255 | } | |
| 1256 | ||
| 1257 | JSBool | |
| 1258 | js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags, | |
| 1259 | uintN argc, jsval *argv, jsval *rval) | |
| 1260 | 46338 | { |
| 1261 | JSStackFrame *fp, *oldfp, frame; | |
| 1262 | jsval *oldsp, *sp; | |
| 1263 | void *mark; | |
| 1264 | uintN i; | |
| 1265 | JSBool ok; | |
| 1266 | ||
| 1267 | 46338 | fp = oldfp = cx->fp; |
| 1268 | 46338 | if (!fp) { |
| 1269 | 68 | memset(&frame, 0, sizeof frame); |
| 1270 | 68 | cx->fp = fp = &frame; |
| 1271 | } | |
| 1272 | 46338 | oldsp = fp->sp; |
| 1273 | 46338 | sp = js_AllocStack(cx, 2 + argc, &mark); |
| 1274 | 46338 | if (!sp) { |
| 1275 | 0 | ok = JS_FALSE; |
| 1276 | 0 | goto out; |
| 1277 | } | |
| 1278 | ||
| 1279 | 46338 | PUSH(fval); |
| 1280 | 46338 | PUSH(OBJECT_TO_JSVAL(obj)); |
| 1281 | 46372 | for (i = 0; i < argc; i++) |
| 1282 | 34 | PUSH(argv[i]); |
| 1283 | 46338 | SAVE_SP(fp); |
| 1284 | 46338 | ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL); |
| 1285 | 46338 | if (ok) { |
| 1286 | 46338 | RESTORE_SP(fp); |
| 1287 | ||
| 1288 | /* | |
| 1289 | * Store *rval in the a scoped local root if a scope is open, else in | |
| 1290 | * the cx->lastInternalResult pigeon-hole GC root, solely so users of | |
| 1291 | * js_InternalInvoke and its direct and indirect (js_ValueToString for | |
| 1292 | * example) callers do not need to manage roots for local, temporary | |
| 1293 | * references to such results. | |
| 1294 | */ | |
| 1295 | 46338 | *rval = POP_OPND(); |
| 1296 | 46338 | if (JSVAL_IS_GCTHING(*rval)) { |
| 1297 | 46321 | if (cx->localRootStack) { |
| 1298 | 0 | if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0) |
| 1299 | 0 | ok = JS_FALSE; |
| 1300 | } else { | |
| 1301 | 46321 | cx->lastInternalResult = *rval; |
| 1302 | } | |
| 1303 | } | |
| 1304 | } | |
| 1305 | ||
| 1306 | 46338 | js_FreeStack(cx, mark); |
| 1307 | 46338 | out: |
| 1308 | 46338 | fp->sp = oldsp; |
| 1309 | 46338 | if (oldfp != fp) |
| 1310 | 68 | cx->fp = oldfp; |
| 1311 | ||
| 1312 | 46338 | return ok; |
| 1313 | } | |
| 1314 | ||
| 1315 | JSBool | |
| 1316 | js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval, | |
| 1317 | JSAccessMode mode, uintN argc, jsval *argv, jsval *rval) | |
| 1318 | 0 | { |
| 1319 | /* | |
| 1320 | * Check general (not object-ops/class-specific) access from the running | |
| 1321 | * script to obj.id only if id has a scripted getter or setter that we're | |
| 1322 | * about to invoke. If we don't check this case, nothing else will -- no | |
| 1323 | * other native code has the chance to check. | |
| 1324 | * | |
| 1325 | * Contrast this non-native (scripted) case with native getter and setter | |
| 1326 | * accesses, where the native itself must do an access check, if security | |
| 1327 | * policies requires it. We make a checkAccess or checkObjectAccess call | |
| 1328 | * back to the embedding program only in those cases where we're not going | |
| 1329 | * to call an embedding-defined native function, getter, setter, or class | |
| 1330 | * hook anyway. Where we do call such a native, there's no need for the | |
| 1331 | * engine to impose a separate access check callback on all embeddings -- | |
| 1332 | * many embeddings have no security policy at all. | |
| 1333 | */ | |
| 1334 | JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE); | |
| 1335 | 0 | if (cx->runtime->checkObjectAccess && |
| 1336 | JSVAL_IS_FUNCTION(cx, fval) && | |
| 1337 | ((JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->interpreted && | |
| 1338 | !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode, | |
| 1339 | &fval)) { | |
| 1340 | 0 | return JS_FALSE; |
| 1341 | } | |
| 1342 | ||
| 1343 | 0 | return js_InternalCall(cx, obj, fval, argc, argv, rval); |
| 1344 | } | |
| 1345 | ||
| 1346 | JSBool | |
| 1347 | js_Execute(JSContext *cx, JSObject *chain, JSScript *script, | |
| 1348 | JSStackFrame *down, uintN flags, jsval *result) | |
| 1349 | 1226 | { |
| 1350 | JSInterpreterHook hook; | |
| 1351 | void *hookData, *mark; | |
| 1352 | JSStackFrame *oldfp, frame; | |
| 1353 | JSObject *obj, *tmp; | |
| 1354 | JSBool ok; | |
| 1355 | ||
| 1356 | 1226 | hook = cx->runtime->executeHook; |
| 1357 | 1226 | hookData = mark = NULL; |
| 1358 | 1226 | oldfp = cx->fp; |
| 1359 | 1226 | frame.callobj = frame.argsobj = NULL; |
| 1360 | 1226 | frame.script = script; |
| 1361 | 1226 | if (down) { |
| 1362 | /* Propagate arg/var state for eval and the debugger API. */ | |
| 1363 | 0 | frame.varobj = down->varobj; |
| 1364 | 0 | frame.fun = down->fun; |
| 1365 | 0 | frame.thisp = down->thisp; |
| 1366 | 0 | frame.argc = down->argc; |
| 1367 | 0 | frame.argv = down->argv; |
| 1368 | 0 | frame.nvars = down->nvars; |
| 1369 | 0 | frame.vars = down->vars; |
| 1370 | 0 | frame.annotation = down->annotation; |
| 1371 | 0 | frame.sharpArray = down->sharpArray; |
| 1372 | } else { | |
| 1373 | 1226 | obj = chain; |
| 1374 | 1226 | if (cx->options & JSOPTION_VAROBJFIX) { |
| 1375 | 0 | while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL) |
| 1376 | 0 | obj = tmp; |
| 1377 | } | |
| 1378 | 1226 | frame.varobj = obj; |
| 1379 | 1226 | frame.fun = NULL; |
| 1380 | 1226 | frame.thisp = chain; |
| 1381 | 1226 | frame.argc = 0; |
| 1382 | 1226 | frame.argv = NULL; |
| 1383 | 1226 | frame.nvars = script->numGlobalVars; |
| 1384 | 1226 | if (frame.nvars) { |
| 1385 | 17 | frame.vars = js_AllocRawStack(cx, frame.nvars, &mark); |
| 1386 | 17 | if (!frame.vars) |
| 1387 | 0 | return JS_FALSE; |
| 1388 | 17 | memset(frame.vars, 0, frame.nvars * sizeof(jsval)); |
| 1389 | } else { | |
| 1390 | 1209 | frame.vars = NULL; |
| 1391 | } | |
| 1392 | 1226 | frame.annotation = NULL; |
| 1393 | 1226 | frame.sharpArray = NULL; |
| 1394 | } | |
| 1395 | 1226 | frame.rval = JSVAL_VOID; |
| 1396 | 1226 | frame.down = down; |
| 1397 | 1226 | frame.scopeChain = chain; |
| 1398 | 1226 | frame.pc = NULL; |
| 1399 | 1226 | frame.sp = oldfp ? oldfp->sp : NULL; |
| 1400 | 1226 | frame.spbase = NULL; |
| 1401 | 1226 | frame.sharpDepth = 0; |
| 1402 | 1226 | frame.flags = flags; |
| 1403 | 1226 | frame.dormantNext = NULL; |
| 1404 | 1226 | frame.xmlNamespace = NULL; |
| 1405 | ||
| 1406 | /* | |
| 1407 | * Here we wrap the call to js_Interpret with code to (conditionally) | |
| 1408 | * save and restore the old stack frame chain into a chain of 'dormant' | |
| 1409 | * frame chains. Since we are replacing cx->fp, we were running into | |
| 1410 | * the problem that if GC was called under this frame, some of the GC | |
| 1411 | * things associated with the old frame chain (available here only in | |
| 1412 | * the C variable 'oldfp') were not rooted and were being collected. | |
| 1413 | * | |
| 1414 | * So, now we preserve the links to these 'dormant' frame chains in cx | |
| 1415 | * before calling js_Interpret and cleanup afterwards. The GC walks | |
| 1416 | * these dormant chains and marks objects in the same way that it marks | |
| 1417 | * objects in the primary cx->fp chain. | |
| 1418 | */ | |
| 1419 | 1226 | if (oldfp && oldfp != down) { |
| 1420 | JS_ASSERT(!oldfp->dormantNext); | |
| 1421 | 1226 | oldfp->dormantNext = cx->dormantFrameChain; |
| 1422 | 1226 | cx->dormantFrameChain = oldfp; |
| 1423 | } | |
| 1424 | ||
| 1425 | 1226 | cx->fp = &frame; |
| 1426 | 1226 | if (hook) |
| 1427 | 0 | hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData); |
| 1428 | ||
| 1429 | /* | |
| 1430 | * Use frame.rval, not result, so the last result stays rooted across any | |
| 1431 | * GC activations nested within this js_Interpret. | |
| 1432 | */ | |
| 1433 | 1226 | ok = js_Interpret(cx, script->code, &frame.rval); |
| 1434 | 1226 | *result = frame.rval; |
| 1435 | ||
| 1436 | 1226 | if (hookData) { |
| 1437 | 0 | hook = cx->runtime->executeHook; |
| 1438 | 0 | if (hook) |
| 1439 | 0 | hook(cx, &frame, JS_FALSE, &ok, hookData); |
| 1440 | } | |
| 1441 | 1226 | if (mark) |
| 1442 | 17 | js_FreeRawStack(cx, mark); |
| 1443 | 1226 | cx->fp = oldfp; |
| 1444 | ||
| 1445 | 1226 | if (oldfp && oldfp != down) { |
| 1446 | JS_ASSERT(cx->dormantFrameChain == oldfp); | |
| 1447 | 1226 | cx->dormantFrameChain = oldfp->dormantNext; |
| 1448 | 1226 | oldfp->dormantNext = NULL; |
| 1449 | } | |
| 1450 | ||
| 1451 | 1226 | return ok; |
| 1452 | } | |
| 1453 | ||
| 1454 | #if JS_HAS_EXPORT_IMPORT | |
| 1455 | /* | |
| 1456 | * If id is JSVAL_VOID, import all exported properties from obj. | |
| 1457 | */ | |
| 1458 | static JSBool | |
| 1459 | ImportProperty(JSContext *cx, JSObject *obj, jsid id) | |
| 1460 | 0 | { |
| 1461 | JSBool ok; | |
| 1462 | JSIdArray *ida; | |
| 1463 | JSProperty *prop; | |
| 1464 | JSObject *obj2, *target, *funobj, *closure; | |
| 1465 | JSString *str; | |
| 1466 | uintN attrs; | |
| 1467 | jsint i; | |
| 1468 | jsval value; | |
| 1469 | ||
| 1470 | 0 | if (JSVAL_IS_VOID(id)) { |
| 1471 | 0 | ida = JS_Enumerate(cx, obj); |
| 1472 | 0 | if (!ida) |
| 1473 | 0 | return JS_FALSE; |
| 1474 | 0 | ok = JS_TRUE; |
| 1475 | 0 | if (ida->length == 0) |
| 1476 | goto out; | |
| 1477 | } else { | |
| 1478 | 0 | ida = NULL; |
| 1479 | 0 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
| 1480 | 0 | return JS_FALSE; |
| 1481 | 0 | if (!prop) { |
| 1482 | 0 | str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, |
| 1483 | ID_TO_VALUE(id), NULL); | |
| 1484 | 0 | if (str) |
| 1485 | 0 | js_ReportIsNotDefined(cx, JS_GetStringBytes(str)); |
| 1486 | 0 | return JS_FALSE; |
| 1487 | } | |
| 1488 | 0 | ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); |
| 1489 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 1490 | 0 | if (!ok) |
| 1491 | 0 | return JS_FALSE; |
| 1492 | 0 | if (!(attrs & JSPROP_EXPORTED)) { |
| 1493 | 0 | str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK, |
| 1494 | ID_TO_VALUE(id), NULL); | |
| 1495 | 0 | if (str) { |
| 1496 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 1497 | JSMSG_NOT_EXPORTED, | |
| 1498 | JS_GetStringBytes(str)); | |
| 1499 | } | |
| 1500 | 0 | return JS_FALSE; |
| 1501 | } | |
| 1502 | } | |
| 1503 | ||
| 1504 | 0 | target = cx->fp->varobj; |
| 1505 | 0 | i = 0; |
| 1506 | do { | |
| 1507 | 0 | if (ida) { |
| 1508 | 0 | id = ida->vector[i]; |
| 1509 | 0 | ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); |
| 1510 | 0 | if (!ok) |
| 1511 | 0 | goto out; |
| 1512 | 0 | if (!(attrs & JSPROP_EXPORTED)) |
| 1513 | 0 | continue; |
| 1514 | } | |
| 1515 | 0 | ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs); |
| 1516 | 0 | if (!ok) |
| 1517 | 0 | goto out; |
| 1518 | 0 | if (JSVAL_IS_FUNCTION(cx, value)) { |
| 1519 | 0 | funobj = JSVAL_TO_OBJECT(value); |
| 1520 | 0 | closure = js_CloneFunctionObject(cx, funobj, obj); |
| 1521 | 0 | if (!closure) { |
| 1522 | 0 | ok = JS_FALSE; |
| 1523 | 0 | goto out; |
| 1524 | } | |
| 1525 | 0 | value = OBJECT_TO_JSVAL(closure); |
| 1526 | } | |
| 1527 | ||
| 1528 | /* | |
| 1529 | * Handle the case of importing a property that refers to a local | |
| 1530 | * variable or formal parameter of a function activation. These | |
| 1531 | * properties are accessed by opcodes using stack slot numbers | |
| 1532 | * generated by the compiler rather than runtime name-lookup. These | |
| 1533 | * local references, therefore, bypass the normal scope chain lookup. | |
| 1534 | * So, instead of defining a new property in the activation object, | |
| 1535 | * modify the existing value in the stack slot. | |
| 1536 | */ | |
| 1537 | 0 | if (OBJ_GET_CLASS(cx, target) == &js_CallClass) { |
| 1538 | 0 | ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop); |
| 1539 | 0 | if (!ok) |
| 1540 | goto out; | |
| 1541 | } else { | |
| 1542 | 0 | prop = NULL; |
| 1543 | } | |
| 1544 | 0 | if (prop && target == obj2) { |
| 1545 | 0 | ok = OBJ_SET_PROPERTY(cx, target, id, &value); |
| 1546 | } else { | |
| 1547 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL, |
| 1548 | attrs & ~(JSPROP_EXPORTED | | |
| 1549 | JSPROP_GETTER | | |
| 1550 | JSPROP_SETTER), | |
| 1551 | NULL); | |
| 1552 | } | |
| 1553 | 0 | if (prop) |
| 1554 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 1555 | 0 | if (!ok) |
| 1556 | 0 | goto out; |
| 1557 | 0 | } while (ida && ++i < ida->length); |
| 1558 | ||
| 1559 | 0 | out: |
| 1560 | 0 | if (ida) |
| 1561 | 0 | JS_DestroyIdArray(cx, ida); |
| 1562 | 0 | return ok; |
| 1563 | } | |
| 1564 | #endif /* JS_HAS_EXPORT_IMPORT */ | |
| 1565 | ||
| 1566 | JSBool | |
| 1567 | js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs, | |
| 1568 | JSObject **objp, JSProperty **propp) | |
| 1569 | 8391 | { |
| 1570 | JSObject *obj2; | |
| 1571 | JSProperty *prop; | |
| 1572 | uintN oldAttrs, report; | |
| 1573 | JSBool isFunction; | |
| 1574 | jsval value; | |
| 1575 | const char *type, *name; | |
| 1576 | ||
| 1577 | 8391 | if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) |
| 1578 | 0 | return JS_FALSE; |
| 1579 | 8391 | if (propp) { |
| 1580 | 4617 | *objp = obj2; |
| 1581 | 4617 | *propp = prop; |
| 1582 | } | |
| 1583 | 8391 | if (!prop) |
| 1584 | 2946 | return JS_TRUE; |
| 1585 | ||
| 1586 | /* From here, return true, or goto bad on failure to drop prop. */ | |
| 1587 | 5445 | if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) |
| 1588 | 5445 | goto bad; |
| 1589 | ||
| 1590 | /* If either property is readonly, we have an error. */ | |
| 1591 | 5445 | report = ((oldAttrs | attrs) & JSPROP_READONLY) |
| 1592 | ? JSREPORT_ERROR | |
| 1593 | : JSREPORT_WARNING | JSREPORT_STRICT; | |
| 1594 | ||
| 1595 | 5445 | if (report != JSREPORT_ERROR) { |
| 1596 | /* | |
| 1597 | * Allow redeclaration of variables and functions, but insist that the | |
| 1598 | * new value is not a getter if the old value was, ditto for setters -- | |
| 1599 | * unless prop is impermanent (in which case anyone could delete it and | |
| 1600 | * redefine it, willy-nilly). | |
| 1601 | */ | |
| 1602 | 5445 | if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) |
| 1603 | 5445 | return JS_TRUE; |
| 1604 | 0 | if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0) |
| 1605 | 0 | return JS_TRUE; |
| 1606 | 0 | if (!(oldAttrs & JSPROP_PERMANENT)) |
| 1607 | 0 | return JS_TRUE; |
| 1608 | 0 | report = JSREPORT_ERROR; |
| 1609 | } | |
| 1610 | ||
| 1611 | 0 | isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0; |
| 1612 | 0 | if (!isFunction) { |
| 1613 | 0 | if (!OBJ_GET_PROPERTY(cx, obj, id, &value)) |
| 1614 | 0 | goto bad; |
| 1615 | 0 | isFunction = JSVAL_IS_FUNCTION(cx, value); |
| 1616 | } | |
| 1617 | 0 | type = (oldAttrs & attrs & JSPROP_GETTER) |
| 1618 | ? js_getter_str | |
| 1619 | : (oldAttrs & attrs & JSPROP_SETTER) | |
| 1620 | ? js_setter_str | |
| 1621 | : (oldAttrs & JSPROP_READONLY) | |
| 1622 | ? js_const_str | |
| 1623 | : isFunction | |
| 1624 | ? js_function_str | |
| 1625 | : js_var_str; | |
| 1626 | 0 | name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id)); |
| 1627 | 0 | if (!name) |
| 1628 | 0 | goto bad; |
| 1629 | 0 | return JS_ReportErrorFlagsAndNumber(cx, report, |
| 1630 | js_GetErrorMessage, NULL, | |
| 1631 | JSMSG_REDECLARED_VAR, | |
| 1632 | type, name); | |
| 1633 | ||
| 1634 | 0 | bad: |
| 1635 | 0 | if (propp) { |
| 1636 | 0 | *objp = NULL; |
| 1637 | 0 | *propp = NULL; |
| 1638 | } | |
| 1639 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 1640 | 0 | return JS_FALSE; |
| 1641 | } | |
| 1642 | ||
| 1643 | JSBool | |
| 1644 | js_StrictlyEqual(jsval lval, jsval rval) | |
| 1645 | 0 | { |
| 1646 | 0 | jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval); |
| 1647 | jsdouble ld, rd; | |
| 1648 | ||
| 1649 | 0 | if (ltag == rtag) { |
| 1650 | 0 | if (ltag == JSVAL_STRING) { |
| 1651 | 0 | JSString *lstr = JSVAL_TO_STRING(lval), |
| 1652 | 0 | *rstr = JSVAL_TO_STRING(rval); |
| 1653 | 0 | return js_CompareStrings(lstr, rstr) == 0; |
| 1654 | } | |
| 1655 | 0 | if (ltag == JSVAL_DOUBLE) { |
| 1656 | 0 | ld = *JSVAL_TO_DOUBLE(lval); |
| 1657 | 0 | rd = *JSVAL_TO_DOUBLE(rval); |
| 1658 | 0 | return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); |
| 1659 | } | |
| 1660 | 0 | return lval == rval; |
| 1661 | } | |
| 1662 | 0 | if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) { |
| 1663 | 0 | ld = *JSVAL_TO_DOUBLE(lval); |
| 1664 | 0 | rd = JSVAL_TO_INT(rval); |
| 1665 | 0 | return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); |
| 1666 | } | |
| 1667 | 0 | if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) { |
| 1668 | 0 | ld = JSVAL_TO_INT(lval); |
| 1669 | 0 | rd = *JSVAL_TO_DOUBLE(rval); |
| 1670 | 0 | return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE); |
| 1671 | } | |
| 1672 | 0 | return lval == rval; |
| 1673 | } | |
| 1674 | ||
| 1675 | static JSBool | |
| 1676 | InternStringElementId(JSContext *cx, jsval idval, jsid *idp) | |
| 1677 | 107885 | { |
| 1678 | JSAtom *atom; | |
| 1679 | ||
| 1680 | 107885 | atom = js_ValueToStringAtom(cx, idval); |
| 1681 | 107885 | if (!atom) |
| 1682 | 0 | return JS_FALSE; |
| 1683 | 107885 | *idp = ATOM_TO_JSID(atom); |
| 1684 | 107885 | return JS_TRUE; |
| 1685 | } | |
| 1686 | ||
| 1687 | static JSBool | |
| 1688 | InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp) | |
| 1689 | 107885 | { |
| 1690 | JS_ASSERT(!JSVAL_IS_INT(idval)); | |
| 1691 | ||
| 1692 | #if JS_HAS_XML_SUPPORT | |
| 1693 | 107885 | if (JSVAL_IS_OBJECT(idval)) { |
| 1694 | 0 | *idp = OBJECT_JSVAL_TO_JSID(idval); |
| 1695 | 0 | return JS_TRUE; |
| 1696 | } | |
| 1697 | #endif | |
| 1698 | ||
| 1699 | 107885 | return InternStringElementId(cx, idval, idp); |
| 1700 | } | |
| 1701 | ||
| 1702 | #if JS_HAS_XML_SUPPORT | |
| 1703 | #define CHECK_ELEMENT_ID(obj, id) \ | |
| 1704 | JS_BEGIN_MACRO \ | |
| 1705 | if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ | |
| 1706 | SAVE_SP(fp); \ | |
| 1707 | ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ | |
| 1708 | if (!ok) \ | |
| 1709 | goto out; \ | |
| 1710 | } \ | |
| 1711 | JS_END_MACRO | |
| 1712 | ||
| 1713 | #else | |
| 1714 | #define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) | |
| 1715 | #endif | |
| 1716 | ||
| 1717 | #ifndef MAX_INTERP_LEVEL | |
| 1718 | #if defined(XP_OS2) | |
| 1719 | #define MAX_INTERP_LEVEL 250 | |
| 1720 | #else | |
| 1721 | #define MAX_INTERP_LEVEL 1000 | |
| 1722 | #endif | |
| 1723 | #endif | |
| 1724 | ||
| 1725 | #define MAX_INLINE_CALL_COUNT 1000 | |
| 1726 | ||
| 1727 | JSBool | |
| 1728 | js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) | |
| 1729 | 1468 | { |
| 1730 | JSRuntime *rt; | |
| 1731 | JSStackFrame *fp; | |
| 1732 | JSScript *script; | |
| 1733 | uintN inlineCallCount; | |
| 1734 | JSObject *obj, *obj2, *proto, *parent; | |
| 1735 | JSVersion currentVersion, originalVersion; | |
| 1736 | JSBranchCallback onbranch; | |
| 1737 | JSBool ok, cond; | |
| 1738 | JSTrapHandler interruptHandler; | |
| 1739 | jsint depth, len; | |
| 1740 | jsval *sp, *newsp; | |
| 1741 | void *mark; | |
| 1742 | jsbytecode *endpc, *pc2; | |
| 1743 | JSOp op, op2; | |
| 1744 | const JSCodeSpec *cs; | |
| 1745 | jsatomid atomIndex; | |
| 1746 | JSAtom *atom; | |
| 1747 | uintN argc, slot, attrs; | |
| 1748 | jsval *vp, lval, rval, ltmp, rtmp; | |
| 1749 | jsid id; | |
| 1750 | JSObject *withobj, *origobj, *propobj; | |
| 1751 | jsval iter_state; | |
| 1752 | JSProperty *prop; | |
| 1753 | JSScopeProperty *sprop; | |
| 1754 | JSString *str, *str2; | |
| 1755 | jsint i, j; | |
| 1756 | jsdouble d, d2; | |
| 1757 | JSClass *clasp, *funclasp; | |
| 1758 | JSFunction *fun; | |
| 1759 | JSType type; | |
| 1760 | #ifdef DEBUG | |
| 1761 | FILE *tracefp; | |
| 1762 | #endif | |
| 1763 | #if JS_HAS_EXPORT_IMPORT | |
| 1764 | JSIdArray *ida; | |
| 1765 | #endif | |
| 1766 | #if JS_HAS_SWITCH_STATEMENT | |
| 1767 | jsint low, high, off, npairs; | |
| 1768 | JSBool match; | |
| 1769 | #endif | |
| 1770 | #if JS_HAS_GETTER_SETTER | |
| 1771 | JSPropertyOp getter, setter; | |
| 1772 | #endif | |
| 1773 | #if JS_HAS_XML_SUPPORT | |
| 1774 | 1468 | JSBool foreach = JS_FALSE; |
| 1775 | #endif | |
| 1776 | int stackDummy; | |
| 1777 | ||
| 1778 | 1468 | *result = JSVAL_VOID; |
| 1779 | 1468 | rt = cx->runtime; |
| 1780 | ||
| 1781 | /* Set registerized frame pointer and derived script pointer. */ | |
| 1782 | 1468 | fp = cx->fp; |
| 1783 | 1468 | script = fp->script; |
| 1784 | ||
| 1785 | /* Count of JS function calls that nest in this C js_Interpret frame. */ | |
| 1786 | 1468 | inlineCallCount = 0; |
| 1787 | ||
| 1788 | /* | |
| 1789 | * Optimized Get and SetVersion for proper script language versioning. | |
| 1790 | * | |
| 1791 | * If any native method or JSClass/JSObjectOps hook calls js_SetVersion | |
| 1792 | * and changes cx->version, the effect will "stick" and we will stop | |
| 1793 | * maintaining currentVersion. This is relied upon by testsuites, for | |
| 1794 | * the most part -- web browsers select version before compiling and not | |
| 1795 | * at run-time. | |
| 1796 | */ | |
| 1797 | 1468 | currentVersion = script->version; |
| 1798 | 1468 | originalVersion = cx->version; |
| 1799 | 1468 | if (currentVersion != originalVersion) |
| 1800 | 0 | js_SetVersion(cx, currentVersion); |
| 1801 | ||
| 1802 | /* | |
| 1803 | * Prepare to call a user-supplied branch handler, and abort the script | |
| 1804 | * if it returns false. We reload onbranch after calling out to native | |
| 1805 | * functions (but not to getters, setters, or other native hooks). | |
| 1806 | */ | |
| 1807 | #define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) | |
| 1808 | ||
| 1809 | 1468 | LOAD_BRANCH_CALLBACK(cx); |
| 1810 | 1468 | ok = JS_TRUE; |
| 1811 | #define CHECK_BRANCH(len) \ | |
| 1812 | JS_BEGIN_MACRO \ | |
| 1813 | if (len <= 0 && onbranch) { \ | |
| 1814 | SAVE_SP(fp); \ | |
| 1815 | if (!(ok = (*onbranch)(cx, script))) \ | |
| 1816 | goto out; \ | |
| 1817 | } \ | |
| 1818 | JS_END_MACRO | |
| 1819 | ||
| 1820 | /* | |
| 1821 | * Load the debugger's interrupt hook here and after calling out to native | |
| 1822 | * functions (but not to getters, setters, or other native hooks), so we do | |
| 1823 | * not have to reload it each time through the interpreter loop -- we hope | |
| 1824 | * the compiler can keep it in a register. | |
| 1825 | * XXX if it spills, we still lose | |
| 1826 | */ | |
| 1827 | #define LOAD_INTERRUPT_HANDLER(rt) (interruptHandler = (rt)->interruptHandler) | |
| 1828 | ||
| 1829 | 1468 | LOAD_INTERRUPT_HANDLER(rt); |
| 1830 | ||
| 1831 | /* Check for too much js_Interpret nesting, or too deep a C stack. */ | |
| 1832 | 1468 | if (++cx->interpLevel == MAX_INTERP_LEVEL || |
| 1833 | !JS_CHECK_STACK_SIZE(cx, stackDummy)) { | |
| 1834 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); |
| 1835 | 0 | ok = JS_FALSE; |
| 1836 | 0 | goto out2; |
| 1837 | } | |
| 1838 | ||
| 1839 | /* | |
| 1840 | * Allocate operand and pc stack slots for the script's worst-case depth, | |
| 1841 | * unless we're called to interpret a part of an already active script, a | |
| 1842 | * filtering predicate expression for example. | |
| 1843 | */ | |
| 1844 | 1468 | depth = (jsint) script->depth; |
| 1845 | 1468 | if (JS_LIKELY(!fp->spbase)) { |
| 1846 | 1468 | newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark); |
| 1847 | 1468 | if (!newsp) { |
| 1848 | 0 | ok = JS_FALSE; |
| 1849 | 0 | goto out2; |
| 1850 | } | |
| 1851 | 1468 | sp = newsp + depth; |
| 1852 | 1468 | fp->spbase = sp; |
| 1853 | 1468 | SAVE_SP(fp); |
| 1854 | } else { | |
| 1855 | 0 | sp = fp->sp; |
| 1856 | JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); | |
| 1857 | 0 | newsp = fp->spbase - depth; |
| 1858 | 0 | mark = NULL; |
| 1859 | } | |
| 1860 | ||
| 1861 | 1468 | endpc = script->code + script->length; |
| 1862 | 7582104 | while (pc < endpc) { |
| 1863 | 7548938 | fp->pc = pc; |
| 1864 | 7548938 | op = (JSOp) *pc; |
| 1865 | 0 | do_op: |
| 1866 | 7548938 | cs = &js_CodeSpec[op]; |
| 1867 | 7548938 | len = cs->length; |
| 1868 | ||
| 1869 | #ifdef DEBUG | |
| 1870 | tracefp = (FILE *) cx->tracefp; | |
| 1871 | if (tracefp) { | |
| 1872 | intN nuses, n; | |
| 1873 | ||
| 1874 | fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); | |
| 1875 | js_Disassemble1(cx, script, pc, | |
| 1876 | PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, | |
| 1877 | tracefp); | |
| 1878 | nuses = cs->nuses; | |
| 1879 | if (nuses) { | |
| 1880 | SAVE_SP(fp); | |
| 1881 | for (n = -nuses; n < 0; n++) { | |
| 1882 | str = js_DecompileValueGenerator(cx, n, sp[n], NULL); | |
| 1883 | if (str) { | |
| 1884 | fprintf(tracefp, "%s %s", | |
| 1885 | (n == -nuses) ? " inputs:" : ",", | |
| 1886 | JS_GetStringBytes(str)); | |
| 1887 | } | |
| 1888 | } | |
| 1889 | fprintf(tracefp, " @ %d\n", sp - fp->spbase); | |
| 1890 | } | |
| 1891 | } | |
| 1892 | #endif | |
| 1893 | ||
| 1894 | 7548938 | if (interruptHandler) { |
| 1895 | 0 | SAVE_SP(fp); |
| 1896 | switch (interruptHandler(cx, script, pc, &rval, | |
| 1897 | 0 | rt->interruptHandlerData)) { |
| 1898 | case JSTRAP_ERROR: | |
| 1899 | 0 | ok = JS_FALSE; |
| 1900 | 0 | goto out; |
| 1901 | case JSTRAP_CONTINUE: | |
| 1902 | break; | |
| 1903 | case JSTRAP_RETURN: | |
| 1904 | 0 | fp->rval = rval; |
| 1905 | 0 | goto out; |
| 1906 | #if JS_HAS_EXCEPTIONS | |
| 1907 | case JSTRAP_THROW: | |
| 1908 | 0 | cx->throwing = JS_TRUE; |
| 1909 | 0 | cx->exception = rval; |
| 1910 | 0 | ok = JS_FALSE; |
| 1911 | 0 | goto out; |
| 1912 | #endif /* JS_HAS_EXCEPTIONS */ | |
| 1913 | default:; | |
| 1914 | } | |
| 1915 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 1916 | } | |
| 1917 | ||
| 1918 | 7548938 | switch (op) { |
| 1919 | case JSOP_NOP: | |
| 1920 | break; | |
| 1921 | ||
| 1922 | case JSOP_GROUP: | |
| 1923 | break; | |
| 1924 | ||
| 1925 | case JSOP_PUSH: | |
| 1926 | 32243 | PUSH_OPND(JSVAL_VOID); |
| 1927 | 32243 | break; |
| 1928 | ||
| 1929 | case JSOP_POP: | |
| 1930 | 469988 | sp--; |
| 1931 | 469988 | break; |
| 1932 | ||
| 1933 | case JSOP_POP2: | |
| 1934 | 24386 | sp -= 2; |
| 1935 | 24386 | break; |
| 1936 | ||
| 1937 | case JSOP_SWAP: | |
| 1938 | /* | |
| 1939 | * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs | |
| 1940 | * for the operands it swaps. | |
| 1941 | */ | |
| 1942 | 0 | ltmp = sp[-1]; |
| 1943 | 0 | sp[-1] = sp[-2]; |
| 1944 | 0 | sp[-2] = ltmp; |
| 1945 | 0 | break; |
| 1946 | ||
| 1947 | case JSOP_POPV: | |
| 1948 | 269773 | *result = POP_OPND(); |
| 1949 | 269773 | break; |
| 1950 | ||
| 1951 | case JSOP_ENTERWITH: | |
| 1952 | 0 | FETCH_OBJECT(cx, -1, rval, obj); |
| 1953 | 0 | SAVE_SP(fp); |
| 1954 | 0 | withobj = js_NewWithObject(cx, obj, fp->scopeChain, |
| 1955 | sp - fp->spbase - 1); | |
| 1956 | 0 | if (!withobj) |
| 1957 | 0 | goto out; |
| 1958 | 0 | rval = INT_TO_JSVAL(sp - fp->spbase); |
| 1959 | 0 | fp->scopeChain = withobj; |
| 1960 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); |
| 1961 | 0 | break; |
| 1962 | ||
| 1963 | case JSOP_LEAVEWITH: | |
| 1964 | 0 | rval = POP_OPND(); |
| 1965 | JS_ASSERT(JSVAL_IS_OBJECT(rval)); | |
| 1966 | 0 | withobj = JSVAL_TO_OBJECT(rval); |
| 1967 | JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); | |
| 1968 | ||
| 1969 | 0 | rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT); |
| 1970 | JS_ASSERT(JSVAL_IS_OBJECT(rval)); | |
| 1971 | 0 | fp->scopeChain = JSVAL_TO_OBJECT(rval); |
| 1972 | 0 | JS_SetPrivate(cx, withobj, NULL); |
| 1973 | 0 | break; |
| 1974 | ||
| 1975 | case JSOP_SETRVAL: | |
| 1976 | 0 | fp->rval = POP_OPND(); |
| 1977 | 0 | break; |
| 1978 | ||
| 1979 | case JSOP_RETURN: | |
| 1980 | 92205 | CHECK_BRANCH(-1); |
| 1981 | 92205 | fp->rval = POP_OPND(); |
| 1982 | /* FALL THROUGH */ | |
| 1983 | ||
| 1984 | case JSOP_RETRVAL: /* fp->rval already set */ | |
| 1985 | 92205 | if (inlineCallCount) |
| 1986 | 122435 | inline_return: |
| 1987 | { | |
| 1988 | 122435 | JSInlineFrame *ifp = (JSInlineFrame *) fp; |
| 1989 | 122435 | void *hookData = ifp->hookData; |
| 1990 | ||
| 1991 | 122435 | if (hookData) { |
| 1992 | 0 | JSInterpreterHook hook = cx->runtime->callHook; |
| 1993 | 0 | if (hook) { |
| 1994 | 0 | hook(cx, fp, JS_FALSE, &ok, hookData); |
| 1995 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 1996 | } | |
| 1997 | } | |
| 1998 | ||
| 1999 | #if JS_HAS_CALL_OBJECT | |
| 2000 | /* | |
| 2001 | * If frame has a call object, sync values and clear the back- | |
| 2002 | * pointer. This can happen for a lightweight function if it | |
| 2003 | * calls eval unexpectedly (in a way that is hidden from the | |
| 2004 | * compiler). See bug 325540. | |
| 2005 | */ | |
| 2006 | 122435 | if (fp->callobj) |
| 2007 | 0 | ok &= js_PutCallObject(cx, fp); |
| 2008 | #endif | |
| 2009 | #if JS_HAS_ARGS_OBJECT | |
| 2010 | 122435 | if (fp->argsobj) |
| 2011 | 0 | ok &= js_PutArgsObject(cx, fp); |
| 2012 | #endif | |
| 2013 | ||
| 2014 | /* Restore context version only if callee hasn't set version. */ | |
| 2015 | 122435 | if (cx->version == currentVersion) { |
| 2016 | 122435 | currentVersion = ifp->callerVersion; |
| 2017 | 122435 | if (currentVersion != cx->version) |
| 2018 | 0 | js_SetVersion(cx, currentVersion); |
| 2019 | } | |
| 2020 | ||
| 2021 | /* Store the return value in the caller's operand frame. */ | |
| 2022 | 122435 | vp = fp->argv - 2; |
| 2023 | 122435 | *vp = fp->rval; |
| 2024 | ||
| 2025 | /* Restore cx->fp and release the inline frame's space. */ | |
| 2026 | 122435 | cx->fp = fp = fp->down; |
| 2027 | 122435 | JS_ARENA_RELEASE(&cx->stackPool, ifp->mark); |
| 2028 | ||
| 2029 | /* Restore sp to point just above the return value. */ | |
| 2030 | 122435 | fp->sp = vp + 1; |
| 2031 | 122435 | RESTORE_SP(fp); |
| 2032 | ||
| 2033 | /* Restore the calling script's interpreter registers. */ | |
| 2034 | 122435 | script = fp->script; |
| 2035 | 122435 | depth = (jsint) script->depth; |
| 2036 | 122435 | pc = fp->pc; |
| 2037 | 122435 | endpc = script->code + script->length; |
| 2038 | ||
| 2039 | /* Store the generating pc for the return value. */ | |
| 2040 | 122435 | vp[-depth] = (jsval)pc; |
| 2041 | ||
| 2042 | /* Set remaining variables for 'goto advance_pc'. */ | |
| 2043 | 122435 | op = (JSOp) *pc; |
| 2044 | 122435 | cs = &js_CodeSpec[op]; |
| 2045 | 122435 | len = cs->length; |
| 2046 | ||
| 2047 | /* Resume execution in the calling frame. */ | |
| 2048 | 122435 | inlineCallCount--; |
| 2049 | 122435 | if (ok) |
| 2050 | goto advance_pc; | |
| 2051 | } | |
| 2052 | goto out; | |
| 2053 | ||
| 2054 | #if JS_HAS_SWITCH_STATEMENT | |
| 2055 | case JSOP_DEFAULT: | |
| 2056 | 0 | (void) POP(); |
| 2057 | /* FALL THROUGH */ | |
| 2058 | #endif | |
| 2059 | case JSOP_GOTO: | |
| 2060 | 77768 | len = GET_JUMP_OFFSET(pc); |
| 2061 | 77768 | CHECK_BRANCH(len); |
| 2062 | break; | |
| 2063 | ||
| 2064 | case JSOP_IFEQ: | |
| 2065 | 379006 | POP_BOOLEAN(cx, rval, cond); |
| 2066 | 379006 | if (cond == JS_FALSE) { |
| 2067 | 233976 | len = GET_JUMP_OFFSET(pc); |
| 2068 | 233976 | CHECK_BRANCH(len); |
| 2069 | } | |
| 2070 | break; | |
| 2071 | ||
| 2072 | case JSOP_IFNE: | |
| 2073 | 0 | POP_BOOLEAN(cx, rval, cond); |
| 2074 | 0 | if (cond != JS_FALSE) { |
| 2075 | 0 | len = GET_JUMP_OFFSET(pc); |
| 2076 | 0 | CHECK_BRANCH(len); |
| 2077 | } | |
| 2078 | break; | |
| 2079 | ||
| 2080 | case JSOP_OR: | |
| 2081 | 24876 | POP_BOOLEAN(cx, rval, cond); |
| 2082 | 24876 | if (cond == JS_TRUE) { |
| 2083 | 4255 | len = GET_JUMP_OFFSET(pc); |
| 2084 | 4255 | PUSH_OPND(rval); |
| 2085 | } | |
| 2086 | break; | |
| 2087 | ||
| 2088 | case JSOP_AND: | |
| 2089 | 99395 | POP_BOOLEAN(cx, rval, cond); |
| 2090 | 99395 | if (cond == JS_FALSE) { |
| 2091 | 18066 | len = GET_JUMP_OFFSET(pc); |
| 2092 | 18066 | PUSH_OPND(rval); |
| 2093 | } | |
| 2094 | break; | |
| 2095 | ||
| 2096 | ||
| 2097 | #if JS_HAS_SWITCH_STATEMENT | |
| 2098 | case JSOP_DEFAULTX: | |
| 2099 | 0 | (void) POP(); |
| 2100 | /* FALL THROUGH */ | |
| 2101 | #endif | |
| 2102 | case JSOP_GOTOX: | |
| 2103 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 2104 | 0 | CHECK_BRANCH(len); |
| 2105 | break; | |
| 2106 | ||
| 2107 | case JSOP_IFEQX: | |
| 2108 | 0 | POP_BOOLEAN(cx, rval, cond); |
| 2109 | 0 | if (cond == JS_FALSE) { |
| 2110 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 2111 | 0 | CHECK_BRANCH(len); |
| 2112 | } | |
| 2113 | break; | |
| 2114 | ||
| 2115 | case JSOP_IFNEX: | |
| 2116 | 0 | POP_BOOLEAN(cx, rval, cond); |
| 2117 | 0 | if (cond != JS_FALSE) { |
| 2118 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 2119 | 0 | CHECK_BRANCH(len); |
| 2120 | } | |
| 2121 | break; | |
| 2122 | ||
| 2123 | case JSOP_ORX: | |
| 2124 | 0 | POP_BOOLEAN(cx, rval, cond); |
| 2125 | 0 | if (cond == JS_TRUE) { |
| 2126 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 2127 | 0 | PUSH_OPND(rval); |
| 2128 | } | |
| 2129 | break; | |
| 2130 | ||
| 2131 | case JSOP_ANDX: | |
| 2132 | 0 | POP_BOOLEAN(cx, rval, cond); |
| 2133 | 0 | if (cond == JS_FALSE) { |
| 2134 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 2135 | 0 | PUSH_OPND(rval); |
| 2136 | } | |
| 2137 | break; | |
| 2138 | ||
| 2139 | case JSOP_TOOBJECT: | |
| 2140 | 24386 | rval = FETCH_OPND(-1); |
| 2141 | 46694 | if (!JSVAL_IS_PRIMITIVE(rval)) { |
| 2142 | 22308 | obj = JSVAL_TO_OBJECT(rval); |
| 2143 | } else { | |
| 2144 | 2078 | SAVE_SP(fp); |
| 2145 | 2078 | ok = js_ValueToObject(cx, rval, &obj); |
| 2146 | 2078 | if (!ok) |
| 2147 | 24386 | goto out; |
| 2148 | } | |
| 2149 | 24386 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 2150 | 24386 | break; |
| 2151 | ||
| 2152 | /* | |
| 2153 | * If the index value at sp[n] is not an int that fits in a jsval, it could | |
| 2154 | * be an object (an XML QName, AttributeName, or AnyName), but only if we are | |
| 2155 | * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a | |
| 2156 | * string atom id. | |
| 2157 | */ | |
| 2158 | #define FETCH_ELEMENT_ID(n, id) \ | |
| 2159 | JS_BEGIN_MACRO \ | |
| 2160 | jsval idval_ = FETCH_OPND(n); \ | |
| 2161 | if (JSVAL_IS_INT(idval_)) { \ | |
| 2162 | id = INT_JSVAL_TO_JSID(idval_); \ | |
| 2163 | } else { \ | |
| 2164 | SAVE_SP(fp); \ | |
| 2165 | ok = InternNonIntElementId(cx, idval_, &id); \ | |
| 2166 | if (!ok) \ | |
| 2167 | goto out; \ | |
| 2168 | } \ | |
| 2169 | JS_END_MACRO | |
| 2170 | ||
| 2171 | #if JS_HAS_IN_OPERATOR | |
| 2172 | case JSOP_IN: | |
| 2173 | 0 | SAVE_SP(fp); |
| 2174 | 0 | rval = FETCH_OPND(-1); |
| 2175 | 0 | if (JSVAL_IS_PRIMITIVE(rval)) { |
| 2176 | 0 | str = js_DecompileValueGenerator(cx, -1, rval, NULL); |
| 2177 | 0 | if (str) { |
| 2178 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 2179 | JSMSG_IN_NOT_OBJECT, | |
| 2180 | JS_GetStringBytes(str)); | |
| 2181 | } | |
| 2182 | 0 | ok = JS_FALSE; |
| 2183 | 0 | goto out; |
| 2184 | } | |
| 2185 | 0 | obj = JSVAL_TO_OBJECT(rval); |
| 2186 | 0 | FETCH_ELEMENT_ID(-2, id); |
| 2187 | 0 | CHECK_ELEMENT_ID(obj, id); |
| 2188 | 0 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); |
| 2189 | 0 | if (!ok) |
| 2190 | 0 | goto out; |
| 2191 | 0 | sp--; |
| 2192 | 0 | STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); |
| 2193 | 0 | if (prop) |
| 2194 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 2195 | break; | |
| 2196 | #endif /* JS_HAS_IN_OPERATOR */ | |
| 2197 | ||
| 2198 | case JSOP_FORPROP: | |
| 2199 | /* | |
| 2200 | * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop | |
| 2201 | * is not paid for the more common cases. | |
| 2202 | */ | |
| 2203 | 0 | lval = FETCH_OPND(-1); |
| 2204 | 0 | atom = GET_ATOM(cx, script, pc); |
| 2205 | 0 | id = ATOM_TO_JSID(atom); |
| 2206 | 0 | i = -2; |
| 2207 | 0 | goto do_forinloop; |
| 2208 | ||
| 2209 | case JSOP_FORNAME: | |
| 2210 | 7744 | atom = GET_ATOM(cx, script, pc); |
| 2211 | 7744 | id = ATOM_TO_JSID(atom); |
| 2212 | ||
| 2213 | /* | |
| 2214 | * ECMA 12.6.3 says to eval the LHS after looking for properties | |
| 2215 | * to enumerate, and bail without LHS eval if there are no props. | |
| 2216 | * We do Find here to share the most code at label do_forinloop. | |
| 2217 | * If looking for enumerable properties could have side effects, | |
| 2218 | * then we'd have to move this into the common code and condition | |
| 2219 | * it on op == JSOP_FORNAME. | |
| 2220 | */ | |
| 2221 | 7744 | SAVE_SP(fp); |
| 2222 | 7744 | ok = js_FindProperty(cx, id, &obj, &obj2, &prop); |
| 2223 | 7744 | if (!ok) |
| 2224 | 7744 | goto out; |
| 2225 | 7744 | if (prop) |
| 2226 | 7744 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 2227 | 7744 | lval = OBJECT_TO_JSVAL(obj); |
| 2228 | /* FALL THROUGH */ | |
| 2229 | ||
| 2230 | case JSOP_FORARG: | |
| 2231 | case JSOP_FORVAR: | |
| 2232 | /* | |
| 2233 | * JSOP_FORARG and JSOP_FORVAR don't require any lval computation | |
| 2234 | * here, because they address slots on the stack (in fp->args and | |
| 2235 | * fp->vars, respectively). | |
| 2236 | */ | |
| 2237 | /* FALL THROUGH */ | |
| 2238 | ||
| 2239 | case JSOP_FORELEM: | |
| 2240 | /* | |
| 2241 | * JSOP_FORELEM simply initializes or updates the iteration state | |
| 2242 | * and leaves the index expression evaluation and assignment to the | |
| 2243 | * enumerator until after the next property has been acquired, via | |
| 2244 | * a JSOP_ENUMELEM bytecode. | |
| 2245 | */ | |
| 2246 | 60357 | i = -1; |
| 2247 | ||
| 2248 | 60357 | do_forinloop: |
| 2249 | /* | |
| 2250 | * ECMA-compatible for/in evals the object just once, before loop. | |
| 2251 | * Bad old bytecodes (since removed) did it on every iteration. | |
| 2252 | */ | |
| 2253 | 60357 | obj = JSVAL_TO_OBJECT(sp[i]); |
| 2254 | ||
| 2255 | /* If the thing to the right of 'in' has no properties, break. */ | |
| 2256 | 60357 | if (!obj) { |
| 2257 | 2078 | rval = JSVAL_FALSE; |
| 2258 | 2078 | goto end_forinloop; |
| 2259 | } | |
| 2260 | ||
| 2261 | /* | |
| 2262 | * Save the thing to the right of 'in' as origobj. Later on, we | |
| 2263 | * use this variable to suppress enumeration of shadowed prototype | |
| 2264 | * properties. | |
| 2265 | */ | |
| 2266 | 58279 | origobj = obj; |
| 2267 | ||
| 2268 | /* | |
| 2269 | * Reach under the top of stack to find our property iterator, a | |
| 2270 | * JSObject that contains the iteration state. (An object is used | |
| 2271 | * rather than a native struct so that the iteration state is | |
| 2272 | * cleaned up via GC if the for-in loop terminates abruptly.) | |
| 2273 | */ | |
| 2274 | 58279 | vp = &sp[i - 1]; |
| 2275 | 58279 | rval = *vp; |
| 2276 | ||
| 2277 | /* | |
| 2278 | * Save sp in fp now, before any OBJ_* call-outs that might nest | |
| 2279 | * an interpreter or GC activation on this context. | |
| 2280 | */ | |
| 2281 | 58279 | SAVE_SP(fp); |
| 2282 | ||
| 2283 | /* Is this the first iteration ? */ | |
| 2284 | 58279 | if (JSVAL_IS_VOID(rval)) { |
| 2285 | /* | |
| 2286 | * Yes, create a new JSObject to hold the iterator state. | |
| 2287 | * Use NULL as the nominal parent in js_NewObject to ensure | |
| 2288 | * that we use the correct scope chain lookup to try to find the | |
| 2289 | * PropertyIterator constructor. | |
| 2290 | */ | |
| 2291 | 22308 | propobj = js_NewObject(cx, &prop_iterator_class, NULL, NULL); |
| 2292 | 22308 | if (!propobj) { |
| 2293 | 0 | ok = JS_FALSE; |
| 2294 | 0 | goto out; |
| 2295 | } | |
| 2296 | ||
| 2297 | /* | |
| 2298 | * Now that we've resolved the object, use the PARENT slot to | |
| 2299 | * store the object that we're iterating over. | |
| 2300 | */ | |
| 2301 | 22308 | propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); |
| 2302 | 22308 | propobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL; |
| 2303 | ||
| 2304 | /* | |
| 2305 | * Root the parent slot so we can get it even in our finalizer | |
| 2306 | * (otherwise, it would live as long as we do, but it might be | |
| 2307 | * finalized first). | |
| 2308 | */ | |
| 2309 | 22308 | ok = js_AddRoot(cx, &propobj->slots[JSSLOT_PARENT], |
| 2310 | "propobj->parent"); | |
| 2311 | 22308 | if (!ok) |
| 2312 | 22308 | goto out; |
| 2313 | ||
| 2314 | /* | |
| 2315 | * Rewrite the iterator so we know to do the next case. | |
| 2316 | * Do this before calling the enumerator, which could | |
| 2317 | * displace cx->newborn and cause GC. | |
| 2318 | */ | |
| 2319 | 22308 | *vp = OBJECT_TO_JSVAL(propobj); |
| 2320 | ||
| 2321 | 22308 | ok = |
| 2322 | #if JS_HAS_XML_SUPPORT | |
| 2323 | (foreach && OBJECT_IS_XML(cx, obj)) | |
| 2324 | ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues | |
| 2325 | (cx, obj, JSENUMERATE_INIT, &iter_state, | |
| 2326 | NULL, NULL) | |
| 2327 | : | |
| 2328 | #endif | |
| 2329 | OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, | |
| 2330 | NULL); | |
| 2331 | 22308 | if (!ok) |
| 2332 | 22308 | goto out; |
| 2333 | ||
| 2334 | /* | |
| 2335 | * Stash private iteration state into property iterator object. | |
| 2336 | * NB: This code knows that the first slots are pre-allocated. | |
| 2337 | */ | |
| 2338 | #if JS_INITIAL_NSLOTS < 5 | |
| 2339 | #error JS_INITIAL_NSLOTS must be greater than or equal to 5. | |
| 2340 | #endif | |
| 2341 | 22308 | propobj->slots[JSSLOT_ITER_STATE] = iter_state; |
| 2342 | } else { | |
| 2343 | /* This is not the first iteration. Recover iterator state. */ | |
| 2344 | 35971 | propobj = JSVAL_TO_OBJECT(rval); |
| 2345 | JS_ASSERT(OBJ_GET_CLASS(cx, propobj) == &prop_iterator_class); | |
| 2346 | 35971 | obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]); |
| 2347 | 35971 | iter_state = propobj->slots[JSSLOT_ITER_STATE]; |
| 2348 | } | |
| 2349 | ||
| 2350 | 34115 | enum_next_property: |
| 2351 | { | |
| 2352 | jsid fid; | |
| 2353 | ||
| 2354 | /* Get the next jsid to be enumerated and store it in fid. */ | |
| 2355 | 92394 | ok = |
| 2356 | #if JS_HAS_XML_SUPPORT | |
| 2357 | (foreach && OBJECT_IS_XML(cx, obj)) | |
| 2358 | ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues | |
| 2359 | (cx, obj, JSENUMERATE_NEXT, &iter_state, | |
| 2360 | &fid, &rval) | |
| 2361 | : | |
| 2362 | #endif | |
| 2363 | OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &fid); | |
| 2364 | 92394 | propobj->slots[JSSLOT_ITER_STATE] = iter_state; |
| 2365 | ||
| 2366 | /* No more jsids to iterate in obj? */ | |
| 2367 | 92394 | if (iter_state == JSVAL_NULL) { |
| 2368 | /* Enumerate the properties on obj's prototype chain. */ | |
| 2369 | 56421 | obj = OBJ_GET_PROTO(cx, obj); |
| 2370 | 56421 | if (!obj) { |
| 2371 | /* End of property list -- terminate loop. */ | |
| 2372 | 22306 | rval = JSVAL_FALSE; |
| 2373 | #if JS_HAS_XML_SUPPORT | |
| 2374 | 22306 | foreach = JS_FALSE; |
| 2375 | #endif | |
| 2376 | 22306 | goto end_forinloop; |
| 2377 | } | |
| 2378 | ||
| 2379 | 34115 | ok = |
| 2380 | #if JS_HAS_XML_SUPPORT | |
| 2381 | (foreach && OBJECT_IS_XML(cx, obj)) | |
| 2382 | ? ((JSXMLObjectOps *) obj->map->ops)->enumerateValues | |
| 2383 | (cx, obj, JSENUMERATE_INIT, &iter_state, | |
| 2384 | NULL, NULL) | |
| 2385 | : | |
| 2386 | #endif | |
| 2387 | OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, | |
| 2388 | NULL); | |
| 2389 | ||
| 2390 | /* | |
| 2391 | * Stash private iteration state into property iterator object. | |
| 2392 | * We do this before checking 'ok' to ensure that propobj is | |
| 2393 | * in a valid state even if OBJ_ENUMERATE returned JS_FALSE. | |
| 2394 | * NB: This code knows that the first slots are pre-allocated. | |
| 2395 | */ | |
| 2396 | 34115 | propobj->slots[JSSLOT_ITER_STATE] = iter_state; |
| 2397 | 34115 | if (!ok) |
| 2398 | 34115 | goto out; |
| 2399 | ||
| 2400 | /* | |
| 2401 | * Update the iterator JSObject's parent link to refer to the | |
| 2402 | * current object. This is used in the iterator JSObject's | |
| 2403 | * finalizer. | |
| 2404 | */ | |
| 2405 | 34115 | propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); |
| 2406 | 34115 | goto enum_next_property; |
| 2407 | } | |
| 2408 | ||
| 2409 | /* Skip properties not owned by obj when looking from origobj. */ | |
| 2410 | 35973 | ok = OBJ_LOOKUP_PROPERTY(cx, origobj, fid, &obj2, &prop); |
| 2411 | 35973 | if (!ok) |
| 2412 | 35973 | goto out; |
| 2413 | 35973 | if (prop) |
| 2414 | 35973 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 2415 | ||
| 2416 | /* | |
| 2417 | * If the id was deleted, or found in a prototype or an unrelated | |
| 2418 | * object (specifically, not in an inner object for obj), skip it. | |
| 2419 | * This means that OBJ_LOOKUP_PROPERTY implementations must return | |
| 2420 | * an object either further on the prototype chain, or related by | |
| 2421 | * the JSExtendedClass.outerObject optional hook. | |
| 2422 | */ | |
| 2423 | 35973 | if (!prop) |
| 2424 | 35973 | goto enum_next_property; |
| 2425 | 35973 | if (obj != obj2) { |
| 2426 | 0 | cond = JS_FALSE; |
| 2427 | 0 | clasp = OBJ_GET_CLASS(cx, obj2); |
| 2428 | 0 | if (clasp->flags & JSCLASS_IS_EXTENDED) { |
| 2429 | JSExtendedClass *xclasp; | |
| 2430 | ||
| 2431 | 0 | xclasp = (JSExtendedClass *) clasp; |
| 2432 | 0 | cond = xclasp->outerObject && |
| 2433 | xclasp->outerObject(cx, obj2) == obj; | |
| 2434 | } | |
| 2435 | 0 | if (!cond) |
| 2436 | 35973 | goto enum_next_property; |
| 2437 | } | |
| 2438 | ||
| 2439 | #if JS_HAS_XML_SUPPORT | |
| 2440 | 35973 | if (foreach) { |
| 2441 | /* Clear the local foreach flag set by our prefix bytecode. */ | |
| 2442 | 0 | foreach = JS_FALSE; |
| 2443 | ||
| 2444 | /* If obj is not XML, we must get rval given its fid. */ | |
| 2445 | 0 | if (!OBJECT_IS_XML(cx, obj)) { |
| 2446 | 0 | ok = OBJ_GET_PROPERTY(cx, origobj, fid, &rval); |
| 2447 | 0 | if (!ok) |
| 2448 | goto out; | |
| 2449 | } | |
| 2450 | } else | |
| 2451 | #endif | |
| 2452 | { | |
| 2453 | /* Make rval a string for uniformity and compatibility. */ | |
| 2454 | 35973 | if (JSID_IS_ATOM(fid)) { |
| 2455 | 13189 | rval = ATOM_KEY(JSID_TO_ATOM(fid)); |
| 2456 | } | |
| 2457 | #if JS_HAS_XML_SUPPORT | |
| 2458 | 22784 | else if (JSID_IS_OBJECT(fid)) { |
| 2459 | 0 | str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(fid)); |
| 2460 | 0 | if (!str) { |
| 2461 | 0 | ok = JS_FALSE; |
| 2462 | 0 | goto out; |
| 2463 | } | |
| 2464 | ||
| 2465 | 0 | rval = STRING_TO_JSVAL(str); |
| 2466 | } | |
| 2467 | #endif | |
| 2468 | 22784 | else if (!JS_VERSION_IS_1_2(cx)) { |
| 2469 | 22784 | str = js_NumberToString(cx, (jsdouble) JSID_TO_INT(fid)); |
| 2470 | 22784 | if (!str) { |
| 2471 | 0 | ok = JS_FALSE; |
| 2472 | 0 | goto out; |
| 2473 | } | |
| 2474 | ||
| 2475 | 22784 | rval = STRING_TO_JSVAL(str); |
| 2476 | } else { | |
| 2477 | 0 | rval = INT_JSID_TO_JSVAL(fid); |
| 2478 | } | |
| 2479 | } | |
| 2480 | ||
| 2481 | 35973 | switch (op) { |
| 2482 | case JSOP_FORARG: | |
| 2483 | 0 | slot = GET_ARGNO(pc); |
| 2484 | JS_ASSERT(slot < fp->fun->nargs); | |
| 2485 | 0 | fp->argv[slot] = rval; |
| 2486 | 0 | break; |
| 2487 | ||
| 2488 | case JSOP_FORVAR: | |
| 2489 | 28910 | slot = GET_VARNO(pc); |
| 2490 | JS_ASSERT(slot < fp->fun->nvars); | |
| 2491 | 28910 | fp->vars[slot] = rval; |
| 2492 | 28910 | break; |
| 2493 | ||
| 2494 | case JSOP_FORELEM: | |
| 2495 | /* FORELEM is not a SET operation, it's more like BINDNAME. */ | |
| 2496 | 0 | PUSH_OPND(rval); |
| 2497 | 0 | break; |
| 2498 | ||
| 2499 | default: | |
| 2500 | /* Convert lval to a non-null object containing id. */ | |
| 2501 | 7063 | VALUE_TO_OBJECT(cx, lval, obj); |
| 2502 | 7063 | if (i + 1 < 0) |
| 2503 | 0 | STORE_OPND(i + 1, OBJECT_TO_JSVAL(obj)); |
| 2504 | ||
| 2505 | /* Set the variable obj[id] to refer to rval. */ | |
| 2506 | 7063 | fp->flags |= JSFRAME_ASSIGNING; |
| 2507 | 7063 | ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); |
| 2508 | 7063 | fp->flags &= ~JSFRAME_ASSIGNING; |
| 2509 | 7063 | if (!ok) |
| 2510 | 35973 | goto out; |
| 2511 | break; | |
| 2512 | } | |
| 2513 | ||
| 2514 | /* Push true to keep looping through properties. */ | |
| 2515 | 35973 | rval = JSVAL_TRUE; |
| 2516 | ||
| 2517 | 60357 | end_forinloop: |
| 2518 | 60357 | sp += i + 1; |
| 2519 | 60357 | PUSH_OPND(rval); |
| 2520 | 60357 | break; |
| 2521 | } | |
| 2522 | ||
| 2523 | case JSOP_DUP: | |
| 2524 | JS_ASSERT(sp > fp->spbase); | |
| 2525 | 4 | rval = sp[-1]; |
| 2526 | 4 | PUSH_OPND(rval); |
| 2527 | 4 | break; |
| 2528 | ||
| 2529 | case JSOP_DUP2: | |
| 2530 | JS_ASSERT(sp - 1 > fp->spbase); | |
| 2531 | 0 | lval = FETCH_OPND(-2); |
| 2532 | 0 | rval = FETCH_OPND(-1); |
| 2533 | 0 | PUSH_OPND(lval); |
| 2534 | 0 | PUSH_OPND(rval); |
| 2535 | 0 | break; |
| 2536 | ||
| 2537 | #define PROPERTY_OP(n, call) \ | |
| 2538 | JS_BEGIN_MACRO \ | |
| 2539 | /* Fetch the left part and resolve it to a non-null object. */ \ | |
| 2540 | FETCH_OBJECT(cx, n, lval, obj); \ | |
| 2541 | \ | |
| 2542 | /* Get or set the property, set ok false if error, true if success. */\ | |
| 2543 | SAVE_SP(fp); \ | |
| 2544 | call; \ | |
| 2545 | if (!ok) \ | |
| 2546 | goto out; \ | |
| 2547 | JS_END_MACRO | |
| 2548 | ||
| 2549 | #define ELEMENT_OP(n, call) \ | |
| 2550 | JS_BEGIN_MACRO \ | |
| 2551 | /* Fetch the right part and resolve it to an internal id. */ \ | |
| 2552 | FETCH_ELEMENT_ID(n, id); \ | |
| 2553 | \ | |
| 2554 | /* Fetch the left part and resolve it to a non-null object. */ \ | |
| 2555 | FETCH_OBJECT(cx, n - 1, lval, obj); \ | |
| 2556 | \ | |
| 2557 | /* Ensure that id has a type suitable for use with obj. */ \ | |
| 2558 | CHECK_ELEMENT_ID(obj, id); \ | |
| 2559 | \ | |
| 2560 | /* Get or set the element, set ok false if error, true if success. */ \ | |
| 2561 | SAVE_SP(fp); \ | |
| 2562 | call; \ | |
| 2563 | if (!ok) \ | |
| 2564 | goto out; \ | |
| 2565 | JS_END_MACRO | |
| 2566 | ||
| 2567 | /* | |
| 2568 | * Direct callers, i.e. those who do not wrap CACHED_GET and CACHED_SET calls | |
| 2569 | * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP(fp); beforehand, just | |
| 2570 | * in case a getter or setter function is invoked. CACHED_GET and CACHED_SET | |
| 2571 | * use cx, obj, id, and rval from their caller's lexical environment. | |
| 2572 | */ | |
| 2573 | #define CACHED_GET(call) CACHED_GET_VP(call, &rval) | |
| 2574 | ||
| 2575 | #define CACHED_GET_VP(call,vp) \ | |
| 2576 | JS_BEGIN_MACRO \ | |
| 2577 | if (!OBJ_IS_NATIVE(obj)) { \ | |
| 2578 | ok = call; \ | |
| 2579 | } else { \ | |
| 2580 | JS_LOCK_OBJ(cx, obj); \ | |
| 2581 | PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ | |
| 2582 | if (sprop) { \ | |
| 2583 | JSScope *scope_ = OBJ_SCOPE(obj); \ | |
| 2584 | slot = (uintN)sprop->slot; \ | |
| 2585 | *(vp) = (slot != SPROP_INVALID_SLOT) \ | |
| 2586 | ? LOCKED_OBJ_GET_SLOT(obj, slot) \ | |
| 2587 | : JSVAL_VOID; \ | |
| 2588 | JS_UNLOCK_SCOPE(cx, scope_); \ | |
| 2589 | ok = SPROP_GET(cx, sprop, obj, obj, vp); \ | |
| 2590 | JS_LOCK_SCOPE(cx, scope_); \ | |
| 2591 | if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) \ | |
| 2592 | LOCKED_OBJ_SET_SLOT(obj, slot, *(vp)); \ | |
| 2593 | JS_UNLOCK_SCOPE(cx, scope_); \ | |
| 2594 | } else { \ | |
| 2595 | JS_UNLOCK_OBJ(cx, obj); \ | |
| 2596 | ok = call; \ | |
| 2597 | /* No fill here: js_GetProperty fills the cache. */ \ | |
| 2598 | } \ | |
| 2599 | } \ | |
| 2600 | JS_END_MACRO | |
| 2601 | ||
| 2602 | #define CACHED_SET(call) \ | |
| 2603 | JS_BEGIN_MACRO \ | |
| 2604 | if (!OBJ_IS_NATIVE(obj)) { \ | |
| 2605 | ok = call; \ | |
| 2606 | } else { \ | |
| 2607 | JSScope *scope_; \ | |
| 2608 | JS_LOCK_OBJ(cx, obj); \ | |
| 2609 | PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop); \ | |
| 2610 | if (sprop && \ | |
| 2611 | !(sprop->attrs & JSPROP_READONLY) && \ | |
| 2612 | (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) { \ | |
| 2613 | JS_UNLOCK_SCOPE(cx, scope_); \ | |
| 2614 | ok = SPROP_SET(cx, sprop, obj, obj, &rval); \ | |
| 2615 | JS_LOCK_SCOPE(cx, scope_); \ | |
| 2616 | if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) { \ | |
| 2617 | LOCKED_OBJ_SET_SLOT(obj, sprop->slot, rval); \ | |
| 2618 | GC_POKE(cx, JSVAL_NULL); /* XXX second arg ignored */ \ | |
| 2619 | } \ | |
| 2620 | JS_UNLOCK_SCOPE(cx, scope_); \ | |
| 2621 | } else { \ | |
| 2622 | JS_UNLOCK_OBJ(cx, obj); \ | |
| 2623 | ok = call; \ | |
| 2624 | /* No fill here: js_SetProperty writes through the cache. */ \ | |
| 2625 | } \ | |
| 2626 | } \ | |
| 2627 | JS_END_MACRO | |
| 2628 | ||
| 2629 | #define BEGIN_LITOPX_CASE(OP,PCOFF) \ | |
| 2630 | case OP: \ | |
| 2631 | atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ | |
| 2632 | do_##OP: \ | |
| 2633 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); | |
| 2634 | ||
| 2635 | #define END_LITOPX_CASE \ | |
| 2636 | break; \ | |
| 2637 | ||
| 2638 | 0 | BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) |
| 2639 | 0 | obj = fp->varobj; |
| 2640 | 0 | rval = FETCH_OPND(-1); |
| 2641 | 0 | SAVE_SP(fp); |
| 2642 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, |
| 2643 | NULL, NULL, | |
| 2644 | JSPROP_ENUMERATE | JSPROP_PERMANENT | | |
| 2645 | JSPROP_READONLY, | |
| 2646 | NULL); | |
| 2647 | 0 | if (!ok) |
| 2648 | 0 | goto out; |
| 2649 | 0 | STORE_OPND(-1, rval); |
| 2650 | 0 | END_LITOPX_CASE |
| 2651 | ||
| 2652 | 92919 | BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) |
| 2653 | 92919 | SAVE_SP(fp); |
| 2654 | 92919 | obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); |
| 2655 | 92919 | if (!obj) { |
| 2656 | 0 | ok = JS_FALSE; |
| 2657 | 0 | goto out; |
| 2658 | } | |
| 2659 | 92919 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 2660 | 92919 | END_LITOPX_CASE |
| 2661 | ||
| 2662 | case JSOP_SETNAME: | |
| 2663 | 92919 | atom = GET_ATOM(cx, script, pc); |
| 2664 | 92919 | id = ATOM_TO_JSID(atom); |
| 2665 | 92919 | rval = FETCH_OPND(-1); |
| 2666 | 92919 | lval = FETCH_OPND(-2); |
| 2667 | JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); | |
| 2668 | 92919 | obj = JSVAL_TO_OBJECT(lval); |
| 2669 | 92919 | SAVE_SP(fp); |
| 2670 | 92919 | CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); |
| 2671 | 92919 | if (!ok) |
| 2672 | 92919 | goto out; |
| 2673 | 92919 | sp--; |
| 2674 | 92919 | STORE_OPND(-1, rval); |
| 2675 | 92919 | obj = NULL; |
| 2676 | 92919 | break; |
| 2677 | ||
| 2678 | #define INTEGER_OP(OP, EXTRA_CODE) \ | |
| 2679 | JS_BEGIN_MACRO \ | |
| 2680 | FETCH_INT(cx, -1, j); \ | |
| 2681 | FETCH_INT(cx, -2, i); \ | |
| 2682 | if (!ok) \ | |
| 2683 | goto out; \ | |
| 2684 | EXTRA_CODE \ | |
| 2685 | d = i OP j; \ | |
| 2686 | sp--; \ | |
| 2687 | STORE_NUMBER(cx, -1, d); \ | |
| 2688 | JS_END_MACRO | |
| 2689 | ||
| 2690 | #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) | |
| 2691 | #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) | |
| 2692 | ||
| 2693 | case JSOP_BITOR: | |
| 2694 | 0 | BITWISE_OP(|); |
| 2695 | 0 | break; |
| 2696 | ||
| 2697 | case JSOP_BITXOR: | |
| 2698 | 0 | BITWISE_OP(^); |
| 2699 | 0 | break; |
| 2700 | ||
| 2701 | case JSOP_BITAND: | |
| 2702 | 0 | BITWISE_OP(&); |
| 2703 | 0 | break; |
| 2704 | ||
| 2705 | #define RELATIONAL_OP(OP) \ | |
| 2706 | JS_BEGIN_MACRO \ | |
| 2707 | rval = FETCH_OPND(-1); \ | |
| 2708 | lval = FETCH_OPND(-2); \ | |
| 2709 | /* Optimize for two int-tagged operands (typical loop control). */ \ | |
| 2710 | if ((lval & rval) & JSVAL_INT) { \ | |
| 2711 | ltmp = lval ^ JSVAL_VOID; \ | |
| 2712 | rtmp = rval ^ JSVAL_VOID; \ | |
| 2713 | if (ltmp && rtmp) { \ | |
| 2714 | cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval); \ | |
| 2715 | } else { \ | |
| 2716 | d = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN; \ | |
| 2717 | d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN; \ | |
| 2718 | cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ | |
| 2719 | } \ | |
| 2720 | } else { \ | |
| 2721 | VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval); \ | |
| 2722 | sp[-2] = lval; \ | |
| 2723 | VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval); \ | |
| 2724 | if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) { \ | |
| 2725 | str = JSVAL_TO_STRING(lval); \ | |
| 2726 | str2 = JSVAL_TO_STRING(rval); \ | |
| 2727 | cond = js_CompareStrings(str, str2) OP 0; \ | |
| 2728 | } else { \ | |
| 2729 | VALUE_TO_NUMBER(cx, lval, d); \ | |
| 2730 | VALUE_TO_NUMBER(cx, rval, d2); \ | |
| 2731 | cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE); \ | |
| 2732 | } \ | |
| 2733 | } \ | |
| 2734 | sp--; \ | |
| 2735 | STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ | |
| 2736 | JS_END_MACRO | |
| 2737 | ||
| 2738 | /* | |
| 2739 | * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies | |
| 2740 | * because they begin if/else chains, so callers must not put semicolons after | |
| 2741 | * the call expressions! | |
| 2742 | */ | |
| 2743 | #if JS_HAS_XML_SUPPORT | |
| 2744 | #define XML_EQUALITY_OP(OP) \ | |
| 2745 | if ((ltmp == JSVAL_OBJECT && \ | |
| 2746 | (obj2 = JSVAL_TO_OBJECT(lval)) && \ | |
| 2747 | OBJECT_IS_XML(cx, obj2)) || \ | |
| 2748 | (rtmp == JSVAL_OBJECT && \ | |
| 2749 | (obj2 = JSVAL_TO_OBJECT(rval)) && \ | |
| 2750 | OBJECT_IS_XML(cx, obj2))) { \ | |
| 2751 | JSXMLObjectOps *ops; \ | |
| 2752 | \ | |
| 2753 | ops = (JSXMLObjectOps *) obj2->map->ops; \ | |
| 2754 | if (obj2 == JSVAL_TO_OBJECT(rval)) \ | |
| 2755 | rval = lval; \ | |
| 2756 | SAVE_SP(fp); \ | |
| 2757 | ok = ops->equality(cx, obj2, rval, &cond); \ | |
| 2758 | if (!ok) \ | |
| 2759 | goto out; \ | |
| 2760 | cond = cond OP JS_TRUE; \ | |
| 2761 | } else | |
| 2762 | ||
| 2763 | #define XML_NAME_EQUALITY_OP(OP) \ | |
| 2764 | if (ltmp == JSVAL_OBJECT && \ | |
| 2765 | (obj2 = JSVAL_TO_OBJECT(lval)) && \ | |
| 2766 | ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ | |
| 2767 | JSExtendedClass *xclasp; \ | |
| 2768 | \ | |
| 2769 | xclasp = (JSExtendedClass *) clasp; \ | |
| 2770 | SAVE_SP(fp); \ | |
| 2771 | ok = xclasp->equality(cx, obj2, rval, &cond); \ | |
| 2772 | if (!ok) \ | |
| 2773 | goto out; \ | |
| 2774 | cond = cond OP JS_TRUE; \ | |
| 2775 | } else | |
| 2776 | #else | |
| 2777 | #define XML_EQUALITY_OP(OP) /* nothing */ | |
| 2778 | #define XML_NAME_EQUALITY_OP(OP) /* nothing */ | |
| 2779 | #endif | |
| 2780 | ||
| 2781 | #define EQUALITY_OP(OP, IFNAN) \ | |
| 2782 | JS_BEGIN_MACRO \ | |
| 2783 | rval = FETCH_OPND(-1); \ | |
| 2784 | lval = FETCH_OPND(-2); \ | |
| 2785 | ltmp = JSVAL_TAG(lval); \ | |
| 2786 | rtmp = JSVAL_TAG(rval); \ | |
| 2787 | XML_EQUALITY_OP(OP) \ | |
| 2788 | if (ltmp == rtmp) { \ | |
| 2789 | if (ltmp == JSVAL_STRING) { \ | |
| 2790 | str = JSVAL_TO_STRING(lval); \ | |
| 2791 | str2 = JSVAL_TO_STRING(rval); \ | |
| 2792 | cond = js_CompareStrings(str, str2) OP 0; \ | |
| 2793 | } else if (ltmp == JSVAL_DOUBLE) { \ | |
| 2794 | d = *JSVAL_TO_DOUBLE(lval); \ | |
| 2795 | d2 = *JSVAL_TO_DOUBLE(rval); \ | |
| 2796 | cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ | |
| 2797 | } else { \ | |
| 2798 | XML_NAME_EQUALITY_OP(OP) \ | |
| 2799 | /* Handle all undefined (=>NaN) and int combinations. */ \ | |
| 2800 | cond = lval OP rval; \ | |
| 2801 | } \ | |
| 2802 | } else { \ | |
| 2803 | if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ | |
| 2804 | cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ | |
| 2805 | } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ | |
| 2806 | cond = 1 OP 0; \ | |
| 2807 | } else { \ | |
| 2808 | if (ltmp == JSVAL_OBJECT) { \ | |
| 2809 | VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); \ | |
| 2810 | lval = sp[-2]; \ | |
| 2811 | ltmp = JSVAL_TAG(lval); \ | |
| 2812 | } else if (rtmp == JSVAL_OBJECT) { \ | |
| 2813 | VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); \ | |
| 2814 | rval = sp[-1]; \ | |
| 2815 | rtmp = JSVAL_TAG(rval); \ | |
| 2816 | } \ | |
| 2817 | if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) { \ | |
| 2818 | str = JSVAL_TO_STRING(lval); \ | |
| 2819 | str2 = JSVAL_TO_STRING(rval); \ | |
| 2820 | cond = js_CompareStrings(str, str2) OP 0; \ | |
| 2821 | } else { \ | |
| 2822 | VALUE_TO_NUMBER(cx, lval, d); \ | |
| 2823 | VALUE_TO_NUMBER(cx, rval, d2); \ | |
| 2824 | cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ | |
| 2825 | } \ | |
| 2826 | } \ | |
| 2827 | } \ | |
| 2828 | sp--; \ | |
| 2829 | STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ | |
| 2830 | JS_END_MACRO | |
| 2831 | ||
| 2832 | case JSOP_EQ: | |
| 2833 | 205369 | EQUALITY_OP(==, JS_FALSE); |
| 2834 | 205369 | break; |
| 2835 | ||
| 2836 | case JSOP_NE: | |
| 2837 | 177469 | EQUALITY_OP(!=, JS_TRUE); |
| 2838 | 177469 | break; |
| 2839 | ||
| 2840 | #if !JS_BUG_FALLIBLE_EQOPS | |
| 2841 | #define NEW_EQUALITY_OP(OP) \ | |
| 2842 | JS_BEGIN_MACRO \ | |
| 2843 | rval = FETCH_OPND(-1); \ | |
| 2844 | lval = FETCH_OPND(-2); \ | |
| 2845 | cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ | |
| 2846 | sp--; \ | |
| 2847 | STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ | |
| 2848 | JS_END_MACRO | |
| 2849 | ||
| 2850 | case JSOP_NEW_EQ: | |
| 2851 | 0 | NEW_EQUALITY_OP(==); |
| 2852 | 0 | break; |
| 2853 | ||
| 2854 | case JSOP_NEW_NE: | |
| 2855 | 0 | NEW_EQUALITY_OP(!=); |
| 2856 | 0 | break; |
| 2857 | ||
| 2858 | #if JS_HAS_SWITCH_STATEMENT | |
| 2859 | case JSOP_CASE: | |
| 2860 | 0 | NEW_EQUALITY_OP(==); |
| 2861 | 0 | (void) POP(); |
| 2862 | 0 | if (cond) { |
| 2863 | 0 | len = GET_JUMP_OFFSET(pc); |
| 2864 | 0 | CHECK_BRANCH(len); |
| 2865 | } else { | |
| 2866 | 0 | PUSH(lval); |
| 2867 | } | |
| 2868 | break; | |
| 2869 | ||
| 2870 | case JSOP_CASEX: | |
| 2871 | 0 | NEW_EQUALITY_OP(==); |
| 2872 | 0 | (void) POP(); |
| 2873 | 0 | if (cond) { |
| 2874 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 2875 | 0 | CHECK_BRANCH(len); |
| 2876 | } else { | |
| 2877 | 0 | PUSH(lval); |
| 2878 | } | |
| 2879 | break; | |
| 2880 | #endif | |
| 2881 | ||
| 2882 | #endif /* !JS_BUG_FALLIBLE_EQOPS */ | |
| 2883 | ||
| 2884 | case JSOP_LT: | |
| 2885 | 6810 | RELATIONAL_OP(<); |
| 2886 | 6810 | break; |
| 2887 | ||
| 2888 | case JSOP_LE: | |
| 2889 | 0 | RELATIONAL_OP(<=); |
| 2890 | 0 | break; |
| 2891 | ||
| 2892 | case JSOP_GT: | |
| 2893 | 369 | RELATIONAL_OP(>); |
| 2894 | 369 | break; |
| 2895 | ||
| 2896 | case JSOP_GE: | |
| 2897 | 0 | RELATIONAL_OP(>=); |
| 2898 | 0 | break; |
| 2899 | ||
| 2900 | #undef EQUALITY_OP | |
| 2901 | #undef RELATIONAL_OP | |
| 2902 | ||
| 2903 | case JSOP_LSH: | |
| 2904 | 0 | SIGNED_SHIFT_OP(<<); |
| 2905 | 0 | break; |
| 2906 | ||
| 2907 | case JSOP_RSH: | |
| 2908 | 0 | SIGNED_SHIFT_OP(>>); |
| 2909 | 0 | break; |
| 2910 | ||
| 2911 | case JSOP_URSH: | |
| 2912 | { | |
| 2913 | uint32 u; | |
| 2914 | ||
| 2915 | 0 | FETCH_INT(cx, -1, j); |
| 2916 | 0 | FETCH_UINT(cx, -2, u); |
| 2917 | 0 | j &= 31; |
| 2918 | 0 | d = u >> j; |
| 2919 | 0 | sp--; |
| 2920 | 0 | STORE_NUMBER(cx, -1, d); |
| 2921 | 0 | break; |
| 2922 | } | |
| 2923 | ||
| 2924 | #undef INTEGER_OP | |
| 2925 | #undef BITWISE_OP | |
| 2926 | #undef SIGNED_SHIFT_OP | |
| 2927 | ||
| 2928 | case JSOP_ADD: | |
| 2929 | 58547 | rval = FETCH_OPND(-1); |
| 2930 | 58547 | lval = FETCH_OPND(-2); |
| 2931 | #if JS_HAS_XML_SUPPORT | |
| 2932 | 58547 | if (!JSVAL_IS_PRIMITIVE(lval) && |
| 2933 | (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && | |
| 2934 | VALUE_IS_XML(cx, rval)) { | |
| 2935 | JSXMLObjectOps *ops; | |
| 2936 | ||
| 2937 | 0 | ops = (JSXMLObjectOps *) obj2->map->ops; |
| 2938 | 0 | SAVE_SP(fp); |
| 2939 | 0 | ok = ops->concatenate(cx, obj2, rval, &rval); |
| 2940 | 0 | if (!ok) |
| 2941 | 0 | goto out; |
| 2942 | 0 | sp--; |
| 2943 | 0 | STORE_OPND(-1, rval); |
| 2944 | 0 | break; |
| 2945 | } | |
| 2946 | #endif | |
| 2947 | { | |
| 2948 | 58547 | VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]); |
| 2949 | 58547 | lval = sp[-2]; |
| 2950 | 58547 | VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]); |
| 2951 | 58547 | rval = sp[-1]; |
| 2952 | 115486 | if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) { |
| 2953 | 56939 | SAVE_SP(fp); |
| 2954 | 56939 | if (cond) { |
| 2955 | 56939 | str = JSVAL_TO_STRING(lval); |
| 2956 | 56939 | ok = (str2 = js_ValueToString(cx, rval)) != NULL; |
| 2957 | } else { | |
| 2958 | 0 | str2 = JSVAL_TO_STRING(rval); |
| 2959 | 0 | ok = (str = js_ValueToString(cx, lval)) != NULL; |
| 2960 | } | |
| 2961 | 56939 | if (!ok) |
| 2962 | 56939 | goto out; |
| 2963 | 56939 | str = js_ConcatStrings(cx, str, str2); |
| 2964 | 56939 | if (!str) { |
| 2965 | 0 | ok = JS_FALSE; |
| 2966 | 0 | goto out; |
| 2967 | } | |
| 2968 | 56939 | sp--; |
| 2969 | 56939 | STORE_OPND(-1, STRING_TO_JSVAL(str)); |
| 2970 | } else { | |
| 2971 | 1608 | VALUE_TO_NUMBER(cx, lval, d); |
| 2972 | 1608 | VALUE_TO_NUMBER(cx, rval, d2); |
| 2973 | 1608 | d += d2; |
| 2974 | 1608 | sp--; |
| 2975 | 1608 | STORE_NUMBER(cx, -1, d); |
| 2976 | } | |
| 2977 | } | |
| 2978 | break; | |
| 2979 | ||
| 2980 | #define BINARY_OP(OP) \ | |
| 2981 | JS_BEGIN_MACRO \ | |
| 2982 | FETCH_NUMBER(cx, -1, d2); \ | |
| 2983 | FETCH_NUMBER(cx, -2, d); \ | |
| 2984 | d = d OP d2; \ | |
| 2985 | sp--; \ | |
| 2986 | STORE_NUMBER(cx, -1, d); \ | |
| 2987 | JS_END_MACRO | |
| 2988 | ||
| 2989 | case JSOP_SUB: | |
| 2990 | 87 | BINARY_OP(-); |
| 2991 | 87 | break; |
| 2992 | ||
| 2993 | case JSOP_MUL: | |
| 2994 | 0 | BINARY_OP(*); |
| 2995 | 0 | break; |
| 2996 | ||
| 2997 | case JSOP_DIV: | |
| 2998 | 0 | FETCH_NUMBER(cx, -1, d2); |
| 2999 | 0 | FETCH_NUMBER(cx, -2, d); |
| 3000 | 0 | sp--; |
| 3001 | 0 | if (d2 == 0) { |
| 3002 | #if defined(XP_WIN) | |
| 3003 | /* XXX MSVC miscompiles such that (NaN == 0) */ | |
| 3004 | if (JSDOUBLE_IS_NaN(d2)) | |
| 3005 | rval = DOUBLE_TO_JSVAL(rt->jsNaN); | |
| 3006 | else | |
| 3007 | #endif | |
| 3008 | 0 | if (d == 0 || JSDOUBLE_IS_NaN(d)) |
| 3009 | 0 | rval = DOUBLE_TO_JSVAL(rt->jsNaN); |
| 3010 | 0 | else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) |
| 3011 | 0 | rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); |
| 3012 | else | |
| 3013 | 0 | rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); |
| 3014 | 0 | STORE_OPND(-1, rval); |
| 3015 | } else { | |
| 3016 | 0 | d /= d2; |
| 3017 | 0 | STORE_NUMBER(cx, -1, d); |
| 3018 | } | |
| 3019 | break; | |
| 3020 | ||
| 3021 | case JSOP_MOD: | |
| 3022 | 0 | FETCH_NUMBER(cx, -1, d2); |
| 3023 | 0 | FETCH_NUMBER(cx, -2, d); |
| 3024 | 0 | sp--; |
| 3025 | 0 | if (d2 == 0) { |
| 3026 | 0 | STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); |
| 3027 | } else { | |
| 3028 | #if defined(XP_WIN) | |
| 3029 | /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ | |
| 3030 | if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) | |
| 3031 | #endif | |
| 3032 | 0 | d = fmod(d, d2); |
| 3033 | 0 | STORE_NUMBER(cx, -1, d); |
| 3034 | } | |
| 3035 | break; | |
| 3036 | ||
| 3037 | case JSOP_NOT: | |
| 3038 | 8609 | POP_BOOLEAN(cx, rval, cond); |
| 3039 | 8609 | PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); |
| 3040 | 8609 | break; |
| 3041 | ||
| 3042 | case JSOP_BITNOT: | |
| 3043 | 0 | FETCH_INT(cx, -1, i); |
| 3044 | 0 | d = (jsdouble) ~i; |
| 3045 | 0 | STORE_NUMBER(cx, -1, d); |
| 3046 | 0 | break; |
| 3047 | ||
| 3048 | case JSOP_NEG: | |
| 3049 | 0 | FETCH_NUMBER(cx, -1, d); |
| 3050 | #ifdef HPUX | |
| 3051 | /* | |
| 3052 | * Negation of a zero doesn't produce a negative | |
| 3053 | * zero on HPUX. Perform the operation by bit | |
| 3054 | * twiddling. | |
| 3055 | */ | |
| 3056 | JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; | |
| 3057 | #else | |
| 3058 | 0 | d = -d; |
| 3059 | #endif | |
| 3060 | 0 | STORE_NUMBER(cx, -1, d); |
| 3061 | 0 | break; |
| 3062 | ||
| 3063 | case JSOP_POS: | |
| 3064 | 0 | FETCH_NUMBER(cx, -1, d); |
| 3065 | 0 | STORE_NUMBER(cx, -1, d); |
| 3066 | 0 | break; |
| 3067 | ||
| 3068 | case JSOP_NEW: | |
| 3069 | /* Get immediate argc and find the constructor function. */ | |
| 3070 | 45502 | argc = GET_ARGC(pc); |
| 3071 | ||
| 3072 | #if JS_HAS_INITIALIZERS | |
| 3073 | 60478 | do_new: |
| 3074 | #endif | |
| 3075 | 60478 | SAVE_SP(fp); |
| 3076 | 60478 | vp = sp - (2 + argc); |
| 3077 | JS_ASSERT(vp >= fp->spbase); | |
| 3078 | ||
| 3079 | 60478 | fun = NULL; |
| 3080 | 60478 | obj2 = NULL; |
| 3081 | 60478 | lval = *vp; |
| 3082 | 60478 | if (!JSVAL_IS_OBJECT(lval) || |
| 3083 | (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || | |
| 3084 | /* XXX clean up to avoid special cases above ObjectOps layer */ | |
| 3085 | OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass || | |
| 3086 | !obj2->map->ops->construct) | |
| 3087 | { | |
| 3088 | 60478 | fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT); |
| 3089 | 60478 | if (!fun) { |
| 3090 | 0 | ok = JS_FALSE; |
| 3091 | 0 | goto out; |
| 3092 | } | |
| 3093 | } | |
| 3094 | ||
| 3095 | 60478 | clasp = &js_ObjectClass; |
| 3096 | 60478 | if (!obj2) { |
| 3097 | 0 | proto = parent = NULL; |
| 3098 | 0 | fun = NULL; |
| 3099 | } else { | |
| 3100 | /* | |
| 3101 | * Get the constructor prototype object for this function. | |
| 3102 | * Use the nominal |this| parameter slot, vp[1], as a local | |
| 3103 | * root to protect this prototype, in case it has no other | |
| 3104 | * strong refs. | |
| 3105 | */ | |
| 3106 | 60478 | ok = OBJ_GET_PROPERTY(cx, obj2, |
| 3107 | ATOM_TO_JSID(rt->atomState | |
| 3108 | .classPrototypeAtom), | |
| 3109 | &vp[1]); | |
| 3110 | 60478 | if (!ok) |
| 3111 | 60478 | goto out; |
| 3112 | 60478 | rval = vp[1]; |
| 3113 | 60478 | proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL; |
| 3114 | 60478 | parent = OBJ_GET_PARENT(cx, obj2); |
| 3115 | ||
| 3116 | 60478 | if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) { |
| 3117 | 60478 | funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp; |
| 3118 | 60478 | if (funclasp) |
| 3119 | 60478 | clasp = funclasp; |
| 3120 | } | |
| 3121 | } | |
| 3122 | 60478 | obj = js_NewObject(cx, clasp, proto, parent); |
| 3123 | 60478 | if (!obj) { |
| 3124 | 0 | ok = JS_FALSE; |
| 3125 | 0 | goto out; |
| 3126 | } | |
| 3127 | ||
| 3128 | /* Now we have an object with a constructor method; call it. */ | |
| 3129 | 60478 | vp[1] = OBJECT_TO_JSVAL(obj); |
| 3130 | 60478 | ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT); |
| 3131 | 60478 | RESTORE_SP(fp); |
| 3132 | 60478 | LOAD_BRANCH_CALLBACK(cx); |
| 3133 | 60478 | LOAD_INTERRUPT_HANDLER(rt); |
| 3134 | 60478 | if (!ok) { |
| 3135 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 3136 | 0 | goto out; |
| 3137 | } | |
| 3138 | ||
| 3139 | /* Check the return value and update obj from it. */ | |
| 3140 | 60478 | rval = *vp; |
| 3141 | 60478 | if (JSVAL_IS_PRIMITIVE(rval)) { |
| 3142 | 0 | if (fun || !JS_VERSION_IS_ECMA(cx)) { |
| 3143 | 0 | *vp = OBJECT_TO_JSVAL(obj); |
| 3144 | 0 | break; |
| 3145 | } | |
| 3146 | /* native [[Construct]] returning primitive is error */ | |
| 3147 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 3148 | JSMSG_BAD_NEW_RESULT, | |
| 3149 | js_ValueToPrintableString(cx, rval)); | |
| 3150 | 0 | ok = JS_FALSE; |
| 3151 | 0 | goto out; |
| 3152 | } | |
| 3153 | 60478 | obj = JSVAL_TO_OBJECT(rval); |
| 3154 | JS_RUNTIME_METER(rt, constructs); | |
| 3155 | 60478 | break; |
| 3156 | ||
| 3157 | case JSOP_DELNAME: | |
| 3158 | 0 | atom = GET_ATOM(cx, script, pc); |
| 3159 | 0 | id = ATOM_TO_JSID(atom); |
| 3160 | ||
| 3161 | 0 | SAVE_SP(fp); |
| 3162 | 0 | ok = js_FindProperty(cx, id, &obj, &obj2, &prop); |
| 3163 | 0 | if (!ok) |
| 3164 | 0 | goto out; |
| 3165 | ||
| 3166 | /* ECMA says to return true if name is undefined or inherited. */ | |
| 3167 | 0 | rval = JSVAL_TRUE; |
| 3168 | 0 | if (prop) { |
| 3169 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 3170 | 0 | ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); |
| 3171 | 0 | if (!ok) |
| 3172 | 0 | goto out; |
| 3173 | } | |
| 3174 | 0 | PUSH_OPND(rval); |
| 3175 | 0 | break; |
| 3176 | ||
| 3177 | case JSOP_DELPROP: | |
| 3178 | 0 | atom = GET_ATOM(cx, script, pc); |
| 3179 | 0 | id = ATOM_TO_JSID(atom); |
| 3180 | 0 | PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); |
| 3181 | 0 | STORE_OPND(-1, rval); |
| 3182 | 0 | break; |
| 3183 | ||
| 3184 | case JSOP_DELELEM: | |
| 3185 | 0 | ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); |
| 3186 | 0 | sp--; |
| 3187 | 0 | STORE_OPND(-1, rval); |
| 3188 | 0 | break; |
| 3189 | ||
| 3190 | case JSOP_TYPEOF: | |
| 3191 | 0 | rval = FETCH_OPND(-1); |
| 3192 | 0 | SAVE_SP(fp); |
| 3193 | 0 | type = JS_TypeOfValue(cx, rval); |
| 3194 | 0 | atom = rt->atomState.typeAtoms[type]; |
| 3195 | 0 | STORE_OPND(-1, ATOM_KEY(atom)); |
| 3196 | 0 | break; |
| 3197 | ||
| 3198 | case JSOP_VOID: | |
| 3199 | 0 | (void) POP_OPND(); |
| 3200 | 0 | PUSH_OPND(JSVAL_VOID); |
| 3201 | 0 | break; |
| 3202 | ||
| 3203 | case JSOP_INCNAME: | |
| 3204 | case JSOP_DECNAME: | |
| 3205 | case JSOP_NAMEINC: | |
| 3206 | case JSOP_NAMEDEC: | |
| 3207 | 0 | atom = GET_ATOM(cx, script, pc); |
| 3208 | 0 | id = ATOM_TO_JSID(atom); |
| 3209 | ||
| 3210 | 0 | SAVE_SP(fp); |
| 3211 | 0 | ok = js_FindProperty(cx, id, &obj, &obj2, &prop); |
| 3212 | 0 | if (!ok) |
| 3213 | 0 | goto out; |
| 3214 | 0 | if (!prop) |
| 3215 | 0 | goto atom_not_defined; |
| 3216 | ||
| 3217 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 3218 | 0 | lval = OBJECT_TO_JSVAL(obj); |
| 3219 | 0 | i = 0; |
| 3220 | 0 | goto do_incop; |
| 3221 | ||
| 3222 | case JSOP_INCPROP: | |
| 3223 | case JSOP_DECPROP: | |
| 3224 | case JSOP_PROPINC: | |
| 3225 | case JSOP_PROPDEC: | |
| 3226 | 0 | atom = GET_ATOM(cx, script, pc); |
| 3227 | 0 | id = ATOM_TO_JSID(atom); |
| 3228 | 0 | lval = FETCH_OPND(-1); |
| 3229 | 0 | i = -1; |
| 3230 | 0 | goto do_incop; |
| 3231 | ||
| 3232 | case JSOP_INCELEM: | |
| 3233 | case JSOP_DECELEM: | |
| 3234 | case JSOP_ELEMINC: | |
| 3235 | case JSOP_ELEMDEC: | |
| 3236 | 75 | FETCH_ELEMENT_ID(-1, id); |
| 3237 | 75 | lval = FETCH_OPND(-2); |
| 3238 | 75 | i = -2; |
| 3239 | ||
| 3240 | 75 | do_incop: |
| 3241 | 75 | VALUE_TO_OBJECT(cx, lval, obj); |
| 3242 | 75 | if (i < 0) |
| 3243 | 75 | STORE_OPND(i, OBJECT_TO_JSVAL(obj)); |
| 3244 | 75 | CHECK_ELEMENT_ID(obj, id); |
| 3245 | ||
| 3246 | /* The operand must contain a number. */ | |
| 3247 | 75 | SAVE_SP(fp); |
| 3248 | 75 | CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); |
| 3249 | 75 | if (!ok) |
| 3250 | 75 | goto out; |
| 3251 | ||
| 3252 | /* The expression result goes in rtmp, the updated value in rval. */ | |
| 3253 | 75 | if (JSVAL_IS_INT(rval) && |
| 3254 | rval != INT_TO_JSVAL(JSVAL_INT_MIN) && | |
| 3255 | rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { | |
| 3256 | 75 | if (cs->format & JOF_POST) { |
| 3257 | 75 | rtmp = rval; |
| 3258 | 75 | (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); |
| 3259 | } else { | |
| 3260 | 0 | (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); |
| 3261 | 0 | rtmp = rval; |
| 3262 | } | |
| 3263 | } else { | |
| 3264 | ||
| 3265 | /* | |
| 3266 | * Initially, rval contains the value to increment or decrement, which is not | |
| 3267 | * yet converted. As above, the expression result goes in rtmp, the updated | |
| 3268 | * value goes in rval. Our caller must set vp to point at a GC-rooted jsval | |
| 3269 | * in which we home rtmp, to protect it from GC in case the unconverted rval | |
| 3270 | * is not a number. | |
| 3271 | */ | |
| 3272 | #define NONINT_INCREMENT_OP_MIDDLE() \ | |
| 3273 | JS_BEGIN_MACRO \ | |
| 3274 | VALUE_TO_NUMBER(cx, rval, d); \ | |
| 3275 | if (cs->format & JOF_POST) { \ | |
| 3276 | rtmp = rval; \ | |
| 3277 | if (!JSVAL_IS_NUMBER(rtmp)) { \ | |
| 3278 | ok = js_NewNumberValue(cx, d, &rtmp); \ | |
| 3279 | if (!ok) \ | |
| 3280 | goto out; \ | |
| 3281 | *vp = rtmp; \ | |
| 3282 | } \ | |
| 3283 | (cs->format & JOF_INC) ? d++ : d--; \ | |
| 3284 | ok = js_NewNumberValue(cx, d, &rval); \ | |
| 3285 | } else { \ | |
| 3286 | (cs->format & JOF_INC) ? ++d : --d; \ | |
| 3287 | ok = js_NewNumberValue(cx, d, &rval); \ | |
| 3288 | rtmp = rval; \ | |
| 3289 | } \ | |
| 3290 | if (!ok) \ | |
| 3291 | goto out; \ | |
| 3292 | JS_END_MACRO | |
| 3293 | ||
| 3294 | 0 | if (cs->format & JOF_POST) { |
| 3295 | /* | |
| 3296 | * We must push early to protect the postfix increment | |
| 3297 | * or decrement result, if converted to a jsdouble from | |
| 3298 | * a non-number value, from GC nesting in the setter. | |
| 3299 | */ | |
| 3300 | 0 | vp = sp; |
| 3301 | 0 | PUSH(JSVAL_VOID); |
| 3302 | 0 | SAVE_SP(fp); |
| 3303 | 0 | --i; |
| 3304 | } | |
| 3305 | #ifdef __GNUC__ | |
| 3306 | 0 | else vp = NULL; /* suppress bogus gcc warnings */ |
| 3307 | #endif | |
| 3308 | ||
| 3309 | 0 | NONINT_INCREMENT_OP_MIDDLE(); |
| 3310 | } | |
| 3311 | ||
| 3312 | 75 | fp->flags |= JSFRAME_ASSIGNING; |
| 3313 | 75 | CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); |
| 3314 | 75 | fp->flags &= ~JSFRAME_ASSIGNING; |
| 3315 | 75 | if (!ok) |
| 3316 | 75 | goto out; |
| 3317 | 75 | sp += i; |
| 3318 | 75 | PUSH_OPND(rtmp); |
| 3319 | 75 | break; |
| 3320 | ||
| 3321 | /* | |
| 3322 | * NB: This macro can't use JS_BEGIN_MACRO/JS_END_MACRO around its body because | |
| 3323 | * it must break from the switch case that calls it, not from the do...while(0) | |
| 3324 | * loop created by the JS_BEGIN/END_MACRO brackets. | |
| 3325 | */ | |
| 3326 | #define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX) \ | |
| 3327 | slot = SLOT; \ | |
| 3328 | JS_ASSERT(slot < fp->fun->COUNT); \ | |
| 3329 | vp = fp->BASE + slot; \ | |
| 3330 | rval = *vp; \ | |
| 3331 | if (JSVAL_IS_INT(rval) && \ | |
| 3332 | rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ | |
| 3333 | PRE = rval; \ | |
| 3334 | rval OP 2; \ | |
| 3335 | *vp = rval; \ | |
| 3336 | PUSH_OPND(PRE); \ | |
| 3337 | break; \ | |
| 3338 | } \ | |
| 3339 | goto do_nonint_fast_incop; | |
| 3340 | ||
| 3341 | case JSOP_INCARG: | |
| 3342 | 0 | FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); |
| 3343 | case JSOP_DECARG: | |
| 3344 | 0 | FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); |
| 3345 | case JSOP_ARGINC: | |
| 3346 | 0 | FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); |
| 3347 | case JSOP_ARGDEC: | |
| 3348 | 0 | FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); |
| 3349 | ||
| 3350 | case JSOP_INCVAR: | |
| 3351 | 3306 | FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX); |
| 3352 | case JSOP_DECVAR: | |
| 3353 | 0 | FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN); |
| 3354 | case JSOP_VARINC: | |
| 3355 | 10 | FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX); |
| 3356 | case JSOP_VARDEC: | |
| 3357 | 0 | FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN); |
| 3358 | ||
| 3359 | #undef FAST_INCREMENT_OP | |
| 3360 | ||
| 3361 | 0 | do_nonint_fast_incop: |
| 3362 | 0 | NONINT_INCREMENT_OP_MIDDLE(); |
| 3363 | 0 | *vp = rval; |
| 3364 | 0 | PUSH_OPND(rtmp); |
| 3365 | 0 | break; |
| 3366 | ||
| 3367 | #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX) \ | |
| 3368 | slot = GET_VARNO(pc); \ | |
| 3369 | JS_ASSERT(slot < fp->nvars); \ | |
| 3370 | lval = fp->vars[slot]; \ | |
| 3371 | if (JSVAL_IS_NULL(lval)) { \ | |
| 3372 | op = SLOWOP; \ | |
| 3373 | goto do_op; \ | |
| 3374 | } \ | |
| 3375 | slot = JSVAL_TO_INT(lval); \ | |
| 3376 | obj = fp->varobj; \ | |
| 3377 | rval = OBJ_GET_SLOT(cx, obj, slot); \ | |
| 3378 | if (JSVAL_IS_INT(rval) && \ | |
| 3379 | rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ | |
| 3380 | PRE = rval; \ | |
| 3381 | rval OP 2; \ | |
| 3382 | OBJ_SET_SLOT(cx, obj, slot, rval); \ | |
| 3383 | PUSH_OPND(PRE); \ | |
| 3384 | break; \ | |
| 3385 | } \ | |
| 3386 | goto do_nonint_fast_global_incop; | |
| 3387 | ||
| 3388 | case JSOP_INCGVAR: | |
| 3389 | 0 | FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); |
| 3390 | case JSOP_DECGVAR: | |
| 3391 | 0 | FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); |
| 3392 | case JSOP_GVARINC: | |
| 3393 | 0 | FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); |
| 3394 | case JSOP_GVARDEC: | |
| 3395 | 0 | FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); |
| 3396 | ||
| 3397 | #undef FAST_GLOBAL_INCREMENT_OP | |
| 3398 | ||
| 3399 | 0 | do_nonint_fast_global_incop: |
| 3400 | 0 | vp = sp++; |
| 3401 | 0 | SAVE_SP(fp); |
| 3402 | 0 | NONINT_INCREMENT_OP_MIDDLE(); |
| 3403 | 0 | OBJ_SET_SLOT(cx, obj, slot, rval); |
| 3404 | 0 | STORE_OPND(-1, rtmp); |
| 3405 | 0 | break; |
| 3406 | ||
| 3407 | case JSOP_GETPROP: | |
| 3408 | /* Get an immediate atom naming the property. */ | |
| 3409 | 546396 | atom = GET_ATOM(cx, script, pc); |
| 3410 | 546396 | id = ATOM_TO_JSID(atom); |
| 3411 | 546396 | PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); |
| 3412 | 546396 | STORE_OPND(-1, rval); |
| 3413 | 546396 | break; |
| 3414 | ||
| 3415 | case JSOP_SETPROP: | |
| 3416 | /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ | |
| 3417 | 7306 | rval = FETCH_OPND(-1); |
| 3418 | ||
| 3419 | /* Get an immediate atom naming the property. */ | |
| 3420 | 7306 | atom = GET_ATOM(cx, script, pc); |
| 3421 | 7306 | id = ATOM_TO_JSID(atom); |
| 3422 | 7306 | PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); |
| 3423 | 7306 | sp--; |
| 3424 | 7306 | STORE_OPND(-1, rval); |
| 3425 | 7306 | obj = NULL; |
| 3426 | 7306 | break; |
| 3427 | ||
| 3428 | case JSOP_GETELEM: | |
| 3429 | 186484 | ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); |
| 3430 | 186484 | sp--; |
| 3431 | 186484 | STORE_OPND(-1, rval); |
| 3432 | 186484 | break; |
| 3433 | ||
| 3434 | case JSOP_SETELEM: | |
| 3435 | 90410 | rval = FETCH_OPND(-1); |
| 3436 | 90410 | ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); |
| 3437 | 90410 | sp -= 2; |
| 3438 | 90410 | STORE_OPND(-1, rval); |
| 3439 | 90410 | obj = NULL; |
| 3440 | 90410 | break; |
| 3441 | ||
| 3442 | case JSOP_ENUMELEM: | |
| 3443 | /* Funky: the value to set is under the [obj, id] pair. */ | |
| 3444 | 0 | FETCH_ELEMENT_ID(-1, id); |
| 3445 | 0 | FETCH_OBJECT(cx, -2, lval, obj); |
| 3446 | 0 | CHECK_ELEMENT_ID(obj, id); |
| 3447 | 0 | rval = FETCH_OPND(-3); |
| 3448 | 0 | SAVE_SP(fp); |
| 3449 | 0 | ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); |
| 3450 | 0 | if (!ok) |
| 3451 | 0 | goto out; |
| 3452 | 0 | sp -= 3; |
| 3453 | 0 | break; |
| 3454 | ||
| 3455 | /* | |
| 3456 | * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the | |
| 3457 | * arguments object until it is truly needed. JSOP_ARGSUB optimizes away | |
| 3458 | * arguments objects when the only uses of the 'arguments' parameter are to | |
| 3459 | * fetch individual actual parameters. But if such a use were then invoked, | |
| 3460 | * e.g., arguments[i](), the 'this' parameter would and must bind to the | |
| 3461 | * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. | |
| 3462 | */ | |
| 3463 | #define LAZY_ARGS_THISP ((JSObject *) 1) | |
| 3464 | ||
| 3465 | case JSOP_PUSHOBJ: | |
| 3466 | 593122 | if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { |
| 3467 | 0 | ok = JS_FALSE; |
| 3468 | 0 | goto out; |
| 3469 | } | |
| 3470 | 593122 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 3471 | 593122 | break; |
| 3472 | ||
| 3473 | case JSOP_CALL: | |
| 3474 | case JSOP_EVAL: | |
| 3475 | 532644 | argc = GET_ARGC(pc); |
| 3476 | 532644 | vp = sp - (argc + 2); |
| 3477 | 532644 | lval = *vp; |
| 3478 | 532644 | SAVE_SP(fp); |
| 3479 | ||
| 3480 | 532644 | if (JSVAL_IS_FUNCTION(cx, lval) && |
| 3481 | (obj = JSVAL_TO_OBJECT(lval), | |
| 3482 | fun = (JSFunction *) JS_GetPrivate(cx, obj), | |
| 3483 | fun->interpreted && | |
| 3484 | !(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) && | |
| 3485 | argc >= (uintN)(fun->nargs + fun->extra))) | |
| 3486 | /* inline_call: */ | |
| 3487 | { | |
| 3488 | uintN nframeslots, nvars; | |
| 3489 | void *newmark; | |
| 3490 | JSInlineFrame *newifp; | |
| 3491 | JSInterpreterHook hook; | |
| 3492 | ||
| 3493 | /* Restrict recursion of lightweight functions. */ | |
| 3494 | 122435 | if (inlineCallCount == MAX_INLINE_CALL_COUNT) { |
| 3495 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 3496 | JSMSG_OVER_RECURSED); | |
| 3497 | 0 | ok = JS_FALSE; |
| 3498 | 0 | goto out; |
| 3499 | } | |
| 3500 | ||
| 3501 | #if JS_HAS_JIT | |
| 3502 | /* ZZZbe should do this only if interpreted often enough. */ | |
| 3503 | ok = jsjit_Compile(cx, fun); | |
| 3504 | if (!ok) | |
| 3505 | goto out; | |
| 3506 | #endif | |
| 3507 | ||
| 3508 | /* Compute the number of stack slots needed for fun. */ | |
| 3509 | 122435 | nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1) |
| 3510 | / sizeof(jsval); | |
| 3511 | 122435 | nvars = fun->nvars; |
| 3512 | 122435 | script = fun->u.script; |
| 3513 | 122435 | depth = (jsint) script->depth; |
| 3514 | ||
| 3515 | /* Allocate the frame and space for vars and operands. */ | |
| 3516 | 122435 | newsp = js_AllocRawStack(cx, nframeslots + nvars + 2 * depth, |
| 3517 | &newmark); | |
| 3518 | 122435 | if (!newsp) { |
| 3519 | 0 | ok = JS_FALSE; |
| 3520 | 0 | goto bad_inline_call; |
| 3521 | } | |
| 3522 | 122435 | newifp = (JSInlineFrame *) newsp; |
| 3523 | 122435 | newsp += nframeslots; |
| 3524 | ||
| 3525 | /* Initialize the stack frame. */ | |
| 3526 | 122435 | memset(newifp, 0, sizeof(JSInlineFrame)); |
| 3527 | 122435 | newifp->frame.script = script; |
| 3528 | 122435 | newifp->frame.fun = fun; |
| 3529 | 122435 | newifp->frame.argc = argc; |
| 3530 | 122435 | newifp->frame.argv = vp + 2; |
| 3531 | 122435 | newifp->frame.rval = JSVAL_VOID; |
| 3532 | 122435 | newifp->frame.nvars = nvars; |
| 3533 | 122435 | newifp->frame.vars = newsp; |
| 3534 | 122435 | newifp->frame.down = fp; |
| 3535 | 122435 | newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj); |
| 3536 | 122435 | newifp->mark = newmark; |
| 3537 | ||
| 3538 | /* Compute the 'this' parameter now that argv is set. */ | |
| 3539 | 122435 | ok = js_ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame); |
| 3540 | 122435 | if (!ok) { |
| 3541 | 0 | js_FreeRawStack(cx, newmark); |
| 3542 | 0 | goto bad_inline_call; |
| 3543 | } | |
| 3544 | #ifdef DUMP_CALL_TABLE | |
| 3545 | LogCall(cx, *vp, argc, vp + 2); | |
| 3546 | #endif | |
| 3547 | ||
| 3548 | /* Push void to initialize local variables. */ | |
| 3549 | 122435 | sp = newsp; |
| 3550 | 516592 | while (nvars--) |
| 3551 | 271722 | PUSH(JSVAL_VOID); |
| 3552 | 122435 | sp += depth; |
| 3553 | 122435 | newifp->frame.spbase = sp; |
| 3554 | 122435 | SAVE_SP(&newifp->frame); |
| 3555 | ||
| 3556 | /* Call the debugger hook if present. */ | |
| 3557 | 122435 | hook = cx->runtime->callHook; |
| 3558 | 122435 | if (hook) { |
| 3559 | 0 | newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, |
| 3560 | cx->runtime->callHookData); | |
| 3561 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 3562 | } | |
| 3563 | ||
| 3564 | /* Switch to new version if currentVersion wasn't overridden. */ | |
| 3565 | 122435 | newifp->callerVersion = cx->version; |
| 3566 | 122435 | if (cx->version == currentVersion) { |
| 3567 | 122435 | currentVersion = script->version; |
| 3568 | 122435 | if (currentVersion != cx->version) |
| 3569 | 0 | js_SetVersion(cx, currentVersion); |
| 3570 | } | |
| 3571 | ||
| 3572 | /* Push the frame and set interpreter registers. */ | |
| 3573 | 122435 | cx->fp = fp = &newifp->frame; |
| 3574 | 122435 | pc = script->code; |
| 3575 | 122435 | endpc = pc + script->length; |
| 3576 | 122435 | inlineCallCount++; |
| 3577 | JS_RUNTIME_METER(rt, inlineCalls); | |
| 3578 | 122435 | continue; |
| 3579 | ||
| 3580 | 0 | bad_inline_call: |
| 3581 | 0 | script = fp->script; |
| 3582 | 0 | depth = (jsint) script->depth; |
| 3583 | 0 | goto out; |
| 3584 | } | |
| 3585 | ||
| 3586 | 410209 | ok = js_Invoke(cx, argc, 0); |
| 3587 | 410209 | RESTORE_SP(fp); |
| 3588 | 410209 | LOAD_BRANCH_CALLBACK(cx); |
| 3589 | 410209 | LOAD_INTERRUPT_HANDLER(rt); |
| 3590 | 410209 | if (!ok) |
| 3591 | 410209 | goto out; |
| 3592 | JS_RUNTIME_METER(rt, nonInlineCalls); | |
| 3593 | #if JS_HAS_LVALUE_RETURN | |
| 3594 | 410209 | if (cx->rval2set) { |
| 3595 | /* | |
| 3596 | * Sneaky: use the stack depth we didn't claim in our budget, | |
| 3597 | * but that we know is there on account of [fun, this] already | |
| 3598 | * having been pushed, at a minimum (if no args). Those two | |
| 3599 | * slots have been popped and [rval] has been pushed, which | |
| 3600 | * leaves one more slot for rval2 before we might overflow. | |
| 3601 | * | |
| 3602 | * NB: rval2 must be the property identifier, and rval the | |
| 3603 | * object from which to get the property. The pair form an | |
| 3604 | * ECMA "reference type", which can be used on the right- or | |
| 3605 | * left-hand side of assignment ops. Only native methods can | |
| 3606 | * return reference types. See JSOP_SETCALL just below for | |
| 3607 | * the left-hand-side case. | |
| 3608 | */ | |
| 3609 | 0 | PUSH_OPND(cx->rval2); |
| 3610 | 0 | cx->rval2set = JS_FALSE; |
| 3611 | 0 | ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); |
| 3612 | 0 | sp--; |
| 3613 | 0 | STORE_OPND(-1, rval); |
| 3614 | } | |
| 3615 | #endif | |
| 3616 | 410209 | obj = NULL; |
| 3617 | 410209 | break; |
| 3618 | ||
| 3619 | #if JS_HAS_LVALUE_RETURN | |
| 3620 | case JSOP_SETCALL: | |
| 3621 | 0 | argc = GET_ARGC(pc); |
| 3622 | 0 | SAVE_SP(fp); |
| 3623 | 0 | ok = js_Invoke(cx, argc, 0); |
| 3624 | 0 | RESTORE_SP(fp); |
| 3625 | 0 | LOAD_BRANCH_CALLBACK(cx); |
| 3626 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 3627 | 0 | if (!ok) |
| 3628 | 0 | goto out; |
| 3629 | 0 | if (!cx->rval2set) { |
| 3630 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 3631 | JSMSG_BAD_LEFTSIDE_OF_ASS); | |
| 3632 | 0 | ok = JS_FALSE; |
| 3633 | 0 | goto out; |
| 3634 | } | |
| 3635 | 0 | PUSH_OPND(cx->rval2); |
| 3636 | 0 | cx->rval2set = JS_FALSE; |
| 3637 | 0 | obj = NULL; |
| 3638 | 0 | break; |
| 3639 | #endif | |
| 3640 | ||
| 3641 | case JSOP_NAME: | |
| 3642 | 1325959 | atom = GET_ATOM(cx, script, pc); |
| 3643 | 1325959 | id = ATOM_TO_JSID(atom); |
| 3644 | ||
| 3645 | 1325959 | SAVE_SP(fp); |
| 3646 | 1325959 | ok = js_FindProperty(cx, id, &obj, &obj2, &prop); |
| 3647 | 1325959 | if (!ok) |
| 3648 | 1325959 | goto out; |
| 3649 | 1325959 | if (!prop) { |
| 3650 | /* Kludge to allow (typeof foo == "undefined") tests. */ | |
| 3651 | 0 | for (pc2 = pc + len; pc2 < endpc; pc2++) { |
| 3652 | 0 | op2 = (JSOp)*pc2; |
| 3653 | 0 | if (op2 == JSOP_TYPEOF) { |
| 3654 | 0 | PUSH_OPND(JSVAL_VOID); |
| 3655 | 0 | goto advance_pc; |
| 3656 | } | |
| 3657 | 0 | if (op2 != JSOP_GROUP) |
| 3658 | 0 | break; |
| 3659 | } | |
| 3660 | goto atom_not_defined; | |
| 3661 | } | |
| 3662 | ||
| 3663 | /* Take the slow path if prop was not found in a native object. */ | |
| 3664 | 1325959 | if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { |
| 3665 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 3666 | 0 | ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); |
| 3667 | 0 | if (!ok) |
| 3668 | 0 | goto out; |
| 3669 | 0 | PUSH_OPND(rval); |
| 3670 | 0 | break; |
| 3671 | } | |
| 3672 | ||
| 3673 | /* Get and push the obj[id] property's value. */ | |
| 3674 | 1325959 | sprop = (JSScopeProperty *)prop; |
| 3675 | 1325959 | slot = (uintN)sprop->slot; |
| 3676 | 1325959 | rval = (slot != SPROP_INVALID_SLOT) |
| 3677 | ? LOCKED_OBJ_GET_SLOT(obj2, slot) | |
| 3678 | : JSVAL_VOID; | |
| 3679 | JS_UNLOCK_OBJ(cx, obj2); | |
| 3680 | 1325959 | ok = SPROP_GET(cx, sprop, obj, obj2, &rval); |
| 3681 | JS_LOCK_OBJ(cx, obj2); | |
| 3682 | 1325959 | if (!ok) { |
| 3683 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 3684 | goto out; | |
| 3685 | } | |
| 3686 | 1325959 | if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) |
| 3687 | 1325959 | LOCKED_OBJ_SET_SLOT(obj2, slot, rval); |
| 3688 | 1325959 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 3689 | 1325959 | PUSH_OPND(rval); |
| 3690 | 1325959 | break; |
| 3691 | ||
| 3692 | case JSOP_UINT16: | |
| 3693 | 163811 | i = (jsint) GET_ATOM_INDEX(pc); |
| 3694 | 163811 | rval = INT_TO_JSVAL(i); |
| 3695 | 163811 | PUSH_OPND(rval); |
| 3696 | 163811 | obj = NULL; |
| 3697 | 163811 | break; |
| 3698 | ||
| 3699 | case JSOP_UINT24: | |
| 3700 | 0 | i = (jsint) GET_LITERAL_INDEX(pc); |
| 3701 | 0 | rval = INT_TO_JSVAL(i); |
| 3702 | 0 | PUSH_OPND(rval); |
| 3703 | 0 | break; |
| 3704 | ||
| 3705 | case JSOP_LITERAL: | |
| 3706 | 0 | atomIndex = GET_LITERAL_INDEX(pc); |
| 3707 | 0 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); |
| 3708 | 0 | PUSH_OPND(ATOM_KEY(atom)); |
| 3709 | 0 | obj = NULL; |
| 3710 | 0 | break; |
| 3711 | ||
| 3712 | case JSOP_FINDNAME: | |
| 3713 | 0 | atomIndex = GET_LITERAL_INDEX(pc); |
| 3714 | 0 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); |
| 3715 | 0 | SAVE_SP(fp); |
| 3716 | 0 | obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); |
| 3717 | 0 | if (!obj) { |
| 3718 | 0 | ok = JS_FALSE; |
| 3719 | 0 | goto out; |
| 3720 | } | |
| 3721 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 3722 | 0 | PUSH_OPND(ATOM_KEY(atom)); |
| 3723 | 0 | break; |
| 3724 | ||
| 3725 | case JSOP_LITOPX: | |
| 3726 | 0 | atomIndex = GET_LITERAL_INDEX(pc); |
| 3727 | 0 | op = pc[1 + LITERAL_INDEX_LEN]; |
| 3728 | 0 | switch (op) { |
| 3729 | case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; | |
| 3730 | case JSOP_BINDNAME: goto do_JSOP_BINDNAME; | |
| 3731 | case JSOP_CLOSURE: goto do_JSOP_CLOSURE; | |
| 3732 | case JSOP_DEFCONST: goto do_JSOP_DEFCONST; | |
| 3733 | case JSOP_DEFFUN: goto do_JSOP_DEFFUN; | |
| 3734 | case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; | |
| 3735 | case JSOP_DEFVAR: goto do_JSOP_DEFVAR; | |
| 3736 | #if JS_HAS_EXPORT_IMPORT | |
| 3737 | case JSOP_EXPORTNAME: goto do_JSOP_EXPORTNAME; | |
| 3738 | #endif | |
| 3739 | #if JS_HAS_XML_SUPPORT | |
| 3740 | case JSOP_GETMETHOD: goto do_JSOP_GETMETHOD; | |
| 3741 | case JSOP_SETMETHOD: goto do_JSOP_SETMETHOD; | |
| 3742 | #endif | |
| 3743 | case JSOP_INITCATCHVAR: goto do_JSOP_INITCATCHVAR; | |
| 3744 | case JSOP_NAMEDFUNOBJ: goto do_JSOP_NAMEDFUNOBJ; | |
| 3745 | case JSOP_NUMBER: goto do_JSOP_NUMBER; | |
| 3746 | case JSOP_OBJECT: goto do_JSOP_OBJECT; | |
| 3747 | #if JS_HAS_XML_SUPPORT | |
| 3748 | case JSOP_QNAMECONST: goto do_JSOP_QNAMECONST; | |
| 3749 | case JSOP_QNAMEPART: goto do_JSOP_QNAMEPART; | |
| 3750 | #endif | |
| 3751 | case JSOP_REGEXP: goto do_JSOP_REGEXP; | |
| 3752 | case JSOP_SETCONST: goto do_JSOP_SETCONST; | |
| 3753 | case JSOP_STRING: goto do_JSOP_STRING; | |
| 3754 | #if JS_HAS_XML_SUPPORT | |
| 3755 | case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; | |
| 3756 | case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; | |
| 3757 | case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; | |
| 3758 | case JSOP_XMLPI: goto do_JSOP_XMLPI; | |
| 3759 | #endif | |
| 3760 | default: JS_ASSERT(0); | |
| 3761 | } | |
| 3762 | /* NOTREACHED */ | |
| 3763 | break; | |
| 3764 | ||
| 3765 | case JSOP_NUMBER: | |
| 3766 | case JSOP_STRING: | |
| 3767 | case JSOP_OBJECT: | |
| 3768 | 321457 | atomIndex = GET_ATOM_INDEX(pc); |
| 3769 | ||
| 3770 | 642914 | do_JSOP_NUMBER: |
| 3771 | 642914 | do_JSOP_STRING: |
| 3772 | 321457 | do_JSOP_OBJECT: |
| 3773 | 321457 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); |
| 3774 | 321457 | PUSH_OPND(ATOM_KEY(atom)); |
| 3775 | 321457 | obj = NULL; |
| 3776 | 321457 | break; |
| 3777 | ||
| 3778 | 0 | BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) |
| 3779 | { | |
| 3780 | JSRegExp *re; | |
| 3781 | JSObject *funobj; | |
| 3782 | ||
| 3783 | /* | |
| 3784 | * Push a regexp object for the atom mapped by the bytecode at pc, | |
| 3785 | * cloning the literal's regexp object if necessary, to simulate in | |
| 3786 | * the pre-compile/execute-later case what ECMA specifies for the | |
| 3787 | * compile-and-go case: that scanning each regexp literal creates | |
| 3788 | * a single corresponding RegExp object. | |
| 3789 | * | |
| 3790 | * To support pre-compilation transparently, we must handle the | |
| 3791 | * case where a regexp object literal is used in a different global | |
| 3792 | * at execution time from the global with which it was scanned at | |
| 3793 | * compile time. We do this by re-wrapping the JSRegExp private | |
| 3794 | * data struct with a cloned object having the right prototype and | |
| 3795 | * parent, and having its own lastIndex property value storage. | |
| 3796 | * | |
| 3797 | * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone | |
| 3798 | * literal objects, we don't want to pay a script prolog execution | |
| 3799 | * price for all regexp literals in a script (many may not be used | |
| 3800 | * by a particular execution of that script, depending on control | |
| 3801 | * flow), so we initialize lazily here. | |
| 3802 | * | |
| 3803 | * XXX This code is specific to regular expression objects. If we | |
| 3804 | * need a similar op for other kinds of object literals, we should | |
| 3805 | * push cloning down under JSObjectOps and reuse code here. | |
| 3806 | */ | |
| 3807 | JS_ASSERT(ATOM_IS_OBJECT(atom)); | |
| 3808 | 0 | obj = ATOM_TO_OBJECT(atom); |
| 3809 | JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); | |
| 3810 | ||
| 3811 | 0 | re = (JSRegExp *) JS_GetPrivate(cx, obj); |
| 3812 | 0 | slot = re->cloneIndex; |
| 3813 | 0 | if (fp->fun) { |
| 3814 | /* | |
| 3815 | * We're in function code, not global or eval code (in eval | |
| 3816 | * code, JSOP_REGEXP is never emitted). The code generator | |
| 3817 | * recorded in fp->fun->nregexps the number of re->cloneIndex | |
| 3818 | * slots that it reserved in the cloned funobj. | |
| 3819 | */ | |
| 3820 | 0 | funobj = JSVAL_TO_OBJECT(fp->argv[-2]); |
| 3821 | 0 | slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass); |
| 3822 | 0 | if (!JS_GetReservedSlot(cx, funobj, slot, &rval)) |
| 3823 | 0 | return JS_FALSE; |
| 3824 | 0 | if (JSVAL_IS_VOID(rval)) |
| 3825 | 0 | rval = JSVAL_NULL; |
| 3826 | } else { | |
| 3827 | /* | |
| 3828 | * We're in global code. The code generator already arranged | |
| 3829 | * via script->numGlobalVars to reserve a global variable slot | |
| 3830 | * at cloneIndex. All global variable slots are initialized | |
| 3831 | * to null, not void, for faster testing in JSOP_*GVAR cases. | |
| 3832 | */ | |
| 3833 | 0 | rval = fp->vars[slot]; |
| 3834 | #ifdef __GNUC__ | |
| 3835 | 0 | funobj = NULL; /* suppress bogus gcc warnings */ |
| 3836 | #endif | |
| 3837 | } | |
| 3838 | ||
| 3839 | 0 | if (JSVAL_IS_NULL(rval)) { |
| 3840 | /* Compute the current global object in obj2. */ | |
| 3841 | 0 | obj2 = fp->scopeChain; |
| 3842 | 0 | while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL) |
| 3843 | 0 | obj2 = parent; |
| 3844 | ||
| 3845 | /* | |
| 3846 | * We must home sp here, because either js_CloneRegExpObject | |
| 3847 | * or JS_SetReservedSlot could nest a last-ditch GC. We home | |
| 3848 | * pc as well, in case js_CloneRegExpObject has to lookup the | |
| 3849 | * "RegExp" class in the global object, which could entail a | |
| 3850 | * JSNewResolveOp call. | |
| 3851 | */ | |
| 3852 | 0 | SAVE_SP(fp); |
| 3853 | ||
| 3854 | /* | |
| 3855 | * If obj's parent is not obj2, we must clone obj so that it | |
| 3856 | * has the right parent, and therefore, the right prototype. | |
| 3857 | * | |
| 3858 | * Yes, this means we assume that the correct RegExp.prototype | |
| 3859 | * to which regexp instances (including literals) delegate can | |
| 3860 | * be distinguished solely by the instance's parent, which was | |
| 3861 | * set to the parent of the RegExp constructor function object | |
| 3862 | * when the instance was created. In other words, | |
| 3863 | * | |
| 3864 | * (/x/.__parent__ == RegExp.__parent__) implies | |
| 3865 | * (/x/.__proto__ == RegExp.prototype) | |
| 3866 | * | |
| 3867 | * (unless you assign a different object to RegExp.prototype | |
| 3868 | * at runtime, in which case, ECMA doesn't specify operation, | |
| 3869 | * and you get what you deserve). | |
| 3870 | * | |
| 3871 | * This same coupling between instance parent and constructor | |
| 3872 | * parent turns up everywhere (see jsobj.c's FindConstructor, | |
| 3873 | * js_ConstructObject, and js_NewObject). It's fundamental to | |
| 3874 | * the design of the language when you consider multiple global | |
| 3875 | * objects and separate compilation and execution, even though | |
| 3876 | * it is not specified fully in ECMA. | |
| 3877 | */ | |
| 3878 | 0 | if (OBJ_GET_PARENT(cx, obj) != obj2) { |
| 3879 | 0 | obj = js_CloneRegExpObject(cx, obj, obj2); |
| 3880 | 0 | if (!obj) { |
| 3881 | 0 | ok = JS_FALSE; |
| 3882 | 0 | goto out; |
| 3883 | } | |
| 3884 | } | |
| 3885 | 0 | rval = OBJECT_TO_JSVAL(obj); |
| 3886 | ||
| 3887 | /* Store the regexp object value in its cloneIndex slot. */ | |
| 3888 | 0 | if (fp->fun) { |
| 3889 | 0 | if (!JS_SetReservedSlot(cx, funobj, slot, rval)) |
| 3890 | 0 | return JS_FALSE; |
| 3891 | } else { | |
| 3892 | 0 | fp->vars[slot] = rval; |
| 3893 | } | |
| 3894 | } | |
| 3895 | ||
| 3896 | 0 | PUSH_OPND(rval); |
| 3897 | 0 | obj = NULL; |
| 3898 | } | |
| 3899 | 0 | END_LITOPX_CASE |
| 3900 | ||
| 3901 | case JSOP_ZERO: | |
| 3902 | 64859 | PUSH_OPND(JSVAL_ZERO); |
| 3903 | 64859 | obj = NULL; |
| 3904 | 64859 | break; |
| 3905 | ||
| 3906 | case JSOP_ONE: | |
| 3907 | 44152 | PUSH_OPND(JSVAL_ONE); |
| 3908 | 44152 | obj = NULL; |
| 3909 | 44152 | break; |
| 3910 | ||
| 3911 | case JSOP_NULL: | |
| 3912 | 98 | PUSH_OPND(JSVAL_NULL); |
| 3913 | 98 | obj = NULL; |
| 3914 | 98 | break; |
| 3915 | ||
| 3916 | case JSOP_THIS: | |
| 3917 | 0 | obj = fp->thisp; |
| 3918 | 0 | clasp = OBJ_GET_CLASS(cx, obj); |
| 3919 | 0 | if (clasp->flags & JSCLASS_IS_EXTENDED) { |
| 3920 | JSExtendedClass *xclasp; | |
| 3921 | ||
| 3922 | 0 | xclasp = (JSExtendedClass *) clasp; |
| 3923 | 0 | if (xclasp->outerObject) { |
| 3924 | 0 | obj = xclasp->outerObject(cx, obj); |
| 3925 | 0 | if (!obj) { |
| 3926 | 0 | ok = JS_FALSE; |
| 3927 | 0 | goto out; |
| 3928 | } | |
| 3929 | } | |
| 3930 | } | |
| 3931 | ||
| 3932 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 3933 | 0 | obj = NULL; |
| 3934 | 0 | break; |
| 3935 | ||
| 3936 | case JSOP_FALSE: | |
| 3937 | 39860 | PUSH_OPND(JSVAL_FALSE); |
| 3938 | 39860 | obj = NULL; |
| 3939 | 39860 | break; |
| 3940 | ||
| 3941 | case JSOP_TRUE: | |
| 3942 | 24610 | PUSH_OPND(JSVAL_TRUE); |
| 3943 | 24610 | obj = NULL; |
| 3944 | 24610 | break; |
| 3945 | ||
| 3946 | #if JS_HAS_SWITCH_STATEMENT | |
| 3947 | case JSOP_TABLESWITCH: | |
| 3948 | 0 | pc2 = pc; |
| 3949 | 0 | len = GET_JUMP_OFFSET(pc2); |
| 3950 | ||
| 3951 | /* | |
| 3952 | * ECMAv2 forbids conversion of discriminant, so we will skip to | |
| 3953 | * the default case if the discriminant isn't already an int jsval. | |
| 3954 | * (This opcode is emitted only for dense jsint-domain switches.) | |
| 3955 | */ | |
| 3956 | 0 | if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || |
| 3957 | (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { | |
| 3958 | 0 | rval = POP_OPND(); |
| 3959 | 0 | if (!JSVAL_IS_INT(rval)) |
| 3960 | break; | |
| 3961 | 0 | i = JSVAL_TO_INT(rval); |
| 3962 | } else { | |
| 3963 | 0 | FETCH_INT(cx, -1, i); |
| 3964 | 0 | sp--; |
| 3965 | } | |
| 3966 | ||
| 3967 | 0 | pc2 += JUMP_OFFSET_LEN; |
| 3968 | 0 | low = GET_JUMP_OFFSET(pc2); |
| 3969 | 0 | pc2 += JUMP_OFFSET_LEN; |
| 3970 | 0 | high = GET_JUMP_OFFSET(pc2); |
| 3971 | ||
| 3972 | 0 | i -= low; |
| 3973 | 0 | if ((jsuint)i < (jsuint)(high - low + 1)) { |
| 3974 | 0 | pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; |
| 3975 | 0 | off = (jsint) GET_JUMP_OFFSET(pc2); |
| 3976 | 0 | if (off) |
| 3977 | 0 | len = off; |
| 3978 | } | |
| 3979 | break; | |
| 3980 | ||
| 3981 | case JSOP_LOOKUPSWITCH: | |
| 3982 | 0 | lval = POP_OPND(); |
| 3983 | 0 | pc2 = pc; |
| 3984 | 0 | len = GET_JUMP_OFFSET(pc2); |
| 3985 | ||
| 3986 | 0 | if (!JSVAL_IS_NUMBER(lval) && |
| 3987 | !JSVAL_IS_STRING(lval) && | |
| 3988 | !JSVAL_IS_BOOLEAN(lval)) { | |
| 3989 | 0 | goto advance_pc; |
| 3990 | } | |
| 3991 | ||
| 3992 | 0 | pc2 += JUMP_OFFSET_LEN; |
| 3993 | 0 | npairs = (jsint) GET_ATOM_INDEX(pc2); |
| 3994 | 0 | pc2 += ATOM_INDEX_LEN; |
| 3995 | ||
| 3996 | #define SEARCH_PAIRS(MATCH_CODE) \ | |
| 3997 | while (npairs) { \ | |
| 3998 | atom = GET_ATOM(cx, script, pc2); \ | |
| 3999 | rval = ATOM_KEY(atom); \ | |
| 4000 | MATCH_CODE \ | |
| 4001 | if (match) { \ | |
| 4002 | pc2 += ATOM_INDEX_LEN; \ | |
| 4003 | len = GET_JUMP_OFFSET(pc2); \ | |
| 4004 | goto advance_pc; \ | |
| 4005 | } \ | |
| 4006 | pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ | |
| 4007 | npairs--; \ | |
| 4008 | } | |
| 4009 | 0 | if (JSVAL_IS_STRING(lval)) { |
| 4010 | 0 | str = JSVAL_TO_STRING(lval); |
| 4011 | 0 | SEARCH_PAIRS( |
| 4012 | match = (JSVAL_IS_STRING(rval) && | |
| 4013 | ((str2 = JSVAL_TO_STRING(rval)) == str || | |
| 4014 | !js_CompareStrings(str2, str))); | |
| 4015 | ) | |
| 4016 | 0 | } else if (JSVAL_IS_DOUBLE(lval)) { |
| 4017 | 0 | d = *JSVAL_TO_DOUBLE(lval); |
| 4018 | 0 | SEARCH_PAIRS( |
| 4019 | match = (JSVAL_IS_DOUBLE(rval) && | |
| 4020 | *JSVAL_TO_DOUBLE(rval) == d); | |
| 4021 | ) | |
| 4022 | } else { | |
| 4023 | 0 | SEARCH_PAIRS( |
| 4024 | match = (lval == rval); | |
| 4025 | ) | |
| 4026 | } | |
| 4027 | #undef SEARCH_PAIRS | |
| 4028 | break; | |
| 4029 | ||
| 4030 | case JSOP_TABLESWITCHX: | |
| 4031 | 0 | pc2 = pc; |
| 4032 | 0 | len = GET_JUMPX_OFFSET(pc2); |
| 4033 | ||
| 4034 | /* | |
| 4035 | * ECMAv2 forbids conversion of discriminant, so we will skip to | |
| 4036 | * the default case if the discriminant isn't already an int jsval. | |
| 4037 | * (This opcode is emitted only for dense jsint-domain switches.) | |
| 4038 | */ | |
| 4039 | 0 | if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || |
| 4040 | (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { | |
| 4041 | 0 | rval = POP_OPND(); |
| 4042 | 0 | if (!JSVAL_IS_INT(rval)) |
| 4043 | break; | |
| 4044 | 0 | i = JSVAL_TO_INT(rval); |
| 4045 | } else { | |
| 4046 | 0 | FETCH_INT(cx, -1, i); |
| 4047 | 0 | sp--; |
| 4048 | } | |
| 4049 | ||
| 4050 | 0 | pc2 += JUMPX_OFFSET_LEN; |
| 4051 | 0 | low = GET_JUMP_OFFSET(pc2); |
| 4052 | 0 | pc2 += JUMP_OFFSET_LEN; |
| 4053 | 0 | high = GET_JUMP_OFFSET(pc2); |
| 4054 | ||
| 4055 | 0 | i -= low; |
| 4056 | 0 | if ((jsuint)i < (jsuint)(high - low + 1)) { |
| 4057 | 0 | pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; |
| 4058 | 0 | off = (jsint) GET_JUMPX_OFFSET(pc2); |
| 4059 | 0 | if (off) |
| 4060 | 0 | len = off; |
| 4061 | } | |
| 4062 | break; | |
| 4063 | ||
| 4064 | case JSOP_LOOKUPSWITCHX: | |
| 4065 | 0 | lval = POP_OPND(); |
| 4066 | 0 | pc2 = pc; |
| 4067 | 0 | len = GET_JUMPX_OFFSET(pc2); |
| 4068 | ||
| 4069 | 0 | if (!JSVAL_IS_NUMBER(lval) && |
| 4070 | !JSVAL_IS_STRING(lval) && | |
| 4071 | !JSVAL_IS_BOOLEAN(lval)) { | |
| 4072 | 0 | goto advance_pc; |
| 4073 | } | |
| 4074 | ||
| 4075 | 0 | pc2 += JUMPX_OFFSET_LEN; |
| 4076 | 0 | npairs = (jsint) GET_ATOM_INDEX(pc2); |
| 4077 | 0 | pc2 += ATOM_INDEX_LEN; |
| 4078 | ||
| 4079 | #define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ | |
| 4080 | while (npairs) { \ | |
| 4081 | atom = GET_ATOM(cx, script, pc2); \ | |
| 4082 | rval = ATOM_KEY(atom); \ | |
| 4083 | MATCH_CODE \ | |
| 4084 | if (match) { \ | |
| 4085 | pc2 += ATOM_INDEX_LEN; \ | |
| 4086 | len = GET_JUMPX_OFFSET(pc2); \ | |
| 4087 | goto advance_pc; \ | |
| 4088 | } \ | |
| 4089 | pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ | |
| 4090 | npairs--; \ | |
| 4091 | } | |
| 4092 | 0 | if (JSVAL_IS_STRING(lval)) { |
| 4093 | 0 | str = JSVAL_TO_STRING(lval); |
| 4094 | 0 | SEARCH_EXTENDED_PAIRS( |
| 4095 | match = (JSVAL_IS_STRING(rval) && | |
| 4096 | ((str2 = JSVAL_TO_STRING(rval)) == str || | |
| 4097 | !js_CompareStrings(str2, str))); | |
| 4098 | ) | |
| 4099 | 0 | } else if (JSVAL_IS_DOUBLE(lval)) { |
| 4100 | 0 | d = *JSVAL_TO_DOUBLE(lval); |
| 4101 | 0 | SEARCH_EXTENDED_PAIRS( |
| 4102 | match = (JSVAL_IS_DOUBLE(rval) && | |
| 4103 | *JSVAL_TO_DOUBLE(rval) == d); | |
| 4104 | ) | |
| 4105 | } else { | |
| 4106 | 0 | SEARCH_EXTENDED_PAIRS( |
| 4107 | match = (lval == rval); | |
| 4108 | ) | |
| 4109 | } | |
| 4110 | #undef SEARCH_EXTENDED_PAIRS | |
| 4111 | break; | |
| 4112 | ||
| 4113 | case JSOP_CONDSWITCH: | |
| 4114 | break; | |
| 4115 | ||
| 4116 | #endif /* JS_HAS_SWITCH_STATEMENT */ | |
| 4117 | ||
| 4118 | #if JS_HAS_EXPORT_IMPORT | |
| 4119 | case JSOP_EXPORTALL: | |
| 4120 | 0 | SAVE_SP(fp); |
| 4121 | 0 | obj = fp->varobj; |
| 4122 | 0 | ida = JS_Enumerate(cx, obj); |
| 4123 | 0 | if (!ida) { |
| 4124 | 0 | ok = JS_FALSE; |
| 4125 | } else { | |
| 4126 | 0 | for (i = 0, j = ida->length; i < j; i++) { |
| 4127 | 0 | id = ida->vector[i]; |
| 4128 | 0 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); |
| 4129 | 0 | if (!ok) |
| 4130 | 0 | break; |
| 4131 | 0 | if (!prop) |
| 4132 | 0 | continue; |
| 4133 | 0 | ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); |
| 4134 | 0 | if (ok) { |
| 4135 | 0 | attrs |= JSPROP_EXPORTED; |
| 4136 | 0 | ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); |
| 4137 | } | |
| 4138 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 4139 | 0 | if (!ok) |
| 4140 | 0 | break; |
| 4141 | } | |
| 4142 | 0 | JS_DestroyIdArray(cx, ida); |
| 4143 | } | |
| 4144 | break; | |
| 4145 | ||
| 4146 | 0 | BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) |
| 4147 | 0 | id = ATOM_TO_JSID(atom); |
| 4148 | 0 | obj = fp->varobj; |
| 4149 | 0 | SAVE_SP(fp); |
| 4150 | 0 | ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); |
| 4151 | 0 | if (!ok) |
| 4152 | 0 | goto out; |
| 4153 | 0 | if (!prop) { |
| 4154 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, |
| 4155 | JSPROP_EXPORTED, NULL); | |
| 4156 | } else { | |
| 4157 | 0 | ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); |
| 4158 | 0 | if (ok) { |
| 4159 | 0 | attrs |= JSPROP_EXPORTED; |
| 4160 | 0 | ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); |
| 4161 | } | |
| 4162 | 0 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 4163 | } | |
| 4164 | 0 | if (!ok) |
| 4165 | goto out; | |
| 4166 | END_LITOPX_CASE | |
| 4167 | ||
| 4168 | case JSOP_IMPORTALL: | |
| 4169 | 0 | id = (jsid) JSVAL_VOID; |
| 4170 | 0 | PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); |
| 4171 | 0 | sp--; |
| 4172 | 0 | break; |
| 4173 | ||
| 4174 | case JSOP_IMPORTPROP: | |
| 4175 | /* Get an immediate atom naming the property. */ | |
| 4176 | 0 | atom = GET_ATOM(cx, script, pc); |
| 4177 | 0 | id = ATOM_TO_JSID(atom); |
| 4178 | 0 | PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); |
| 4179 | 0 | sp--; |
| 4180 | 0 | break; |
| 4181 | ||
| 4182 | case JSOP_IMPORTELEM: | |
| 4183 | 0 | ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); |
| 4184 | 0 | sp -= 2; |
| 4185 | 0 | break; |
| 4186 | #endif /* JS_HAS_EXPORT_IMPORT */ | |
| 4187 | ||
| 4188 | case JSOP_TRAP: | |
| 4189 | 0 | switch (JS_HandleTrap(cx, script, pc, &rval)) { |
| 4190 | case JSTRAP_ERROR: | |
| 4191 | 0 | ok = JS_FALSE; |
| 4192 | 0 | goto out; |
| 4193 | case JSTRAP_CONTINUE: | |
| 4194 | JS_ASSERT(JSVAL_IS_INT(rval)); | |
| 4195 | 0 | op = (JSOp) JSVAL_TO_INT(rval); |
| 4196 | JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); | |
| 4197 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 4198 | 0 | goto do_op; |
| 4199 | case JSTRAP_RETURN: | |
| 4200 | 0 | fp->rval = rval; |
| 4201 | 0 | goto out; |
| 4202 | #if JS_HAS_EXCEPTIONS | |
| 4203 | case JSTRAP_THROW: | |
| 4204 | 0 | cx->throwing = JS_TRUE; |
| 4205 | 0 | cx->exception = rval; |
| 4206 | 0 | ok = JS_FALSE; |
| 4207 | 0 | goto out; |
| 4208 | #endif /* JS_HAS_EXCEPTIONS */ | |
| 4209 | default:; | |
| 4210 | } | |
| 4211 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 4212 | 0 | break; |
| 4213 | ||
| 4214 | case JSOP_ARGUMENTS: | |
| 4215 | 0 | SAVE_SP(fp); |
| 4216 | 0 | ok = js_GetArgsValue(cx, fp, &rval); |
| 4217 | 0 | if (!ok) |
| 4218 | 0 | goto out; |
| 4219 | 0 | PUSH_OPND(rval); |
| 4220 | 0 | break; |
| 4221 | ||
| 4222 | case JSOP_ARGSUB: | |
| 4223 | 0 | id = INT_TO_JSID(GET_ARGNO(pc)); |
| 4224 | 0 | SAVE_SP(fp); |
| 4225 | 0 | ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); |
| 4226 | 0 | if (!ok) |
| 4227 | 0 | goto out; |
| 4228 | 0 | if (!obj) { |
| 4229 | /* | |
| 4230 | * If arguments was not overridden by eval('arguments = ...'), | |
| 4231 | * set obj to the magic cookie respected by JSOP_PUSHOBJ, just | |
| 4232 | * in case this bytecode is part of an 'arguments[i](j, k)' or | |
| 4233 | * similar such invocation sequence, where the function that | |
| 4234 | * is invoked expects its 'this' parameter to be the caller's | |
| 4235 | * arguments object. | |
| 4236 | */ | |
| 4237 | 0 | obj = LAZY_ARGS_THISP; |
| 4238 | } | |
| 4239 | 0 | PUSH_OPND(rval); |
| 4240 | 0 | break; |
| 4241 | ||
| 4242 | #undef LAZY_ARGS_THISP | |
| 4243 | ||
| 4244 | case JSOP_ARGCNT: | |
| 4245 | 0 | id = ATOM_TO_JSID(rt->atomState.lengthAtom); |
| 4246 | 0 | SAVE_SP(fp); |
| 4247 | 0 | ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); |
| 4248 | 0 | if (!ok) |
| 4249 | 0 | goto out; |
| 4250 | 0 | PUSH_OPND(rval); |
| 4251 | 0 | break; |
| 4252 | ||
| 4253 | case JSOP_GETARG: | |
| 4254 | 548463 | slot = GET_ARGNO(pc); |
| 4255 | JS_ASSERT(slot < fp->fun->nargs); | |
| 4256 | 548463 | PUSH_OPND(fp->argv[slot]); |
| 4257 | 548463 | obj = NULL; |
| 4258 | 548463 | break; |
| 4259 | ||
| 4260 | case JSOP_SETARG: | |
| 4261 | 1920 | slot = GET_ARGNO(pc); |
| 4262 | JS_ASSERT(slot < fp->fun->nargs); | |
| 4263 | 1920 | vp = &fp->argv[slot]; |
| 4264 | 1920 | GC_POKE(cx, *vp); |
| 4265 | 1920 | *vp = FETCH_OPND(-1); |
| 4266 | 1920 | obj = NULL; |
| 4267 | 1920 | break; |
| 4268 | ||
| 4269 | case JSOP_GETVAR: | |
| 4270 | 433785 | slot = GET_VARNO(pc); |
| 4271 | JS_ASSERT(slot < fp->fun->nvars); | |
| 4272 | 433785 | PUSH_OPND(fp->vars[slot]); |
| 4273 | 433785 | obj = NULL; |
| 4274 | 433785 | break; |
| 4275 | ||
| 4276 | case JSOP_SETVAR: | |
| 4277 | 177182 | slot = GET_VARNO(pc); |
| 4278 | JS_ASSERT(slot < fp->fun->nvars); | |
| 4279 | 177182 | vp = &fp->vars[slot]; |
| 4280 | 177182 | GC_POKE(cx, *vp); |
| 4281 | 177182 | *vp = FETCH_OPND(-1); |
| 4282 | 177182 | obj = NULL; |
| 4283 | 177182 | break; |
| 4284 | ||
| 4285 | case JSOP_GETGVAR: | |
| 4286 | 383 | slot = GET_VARNO(pc); |
| 4287 | JS_ASSERT(slot < fp->nvars); | |
| 4288 | 383 | lval = fp->vars[slot]; |
| 4289 | 383 | if (JSVAL_IS_NULL(lval)) { |
| 4290 | 0 | op = JSOP_NAME; |
| 4291 | 0 | goto do_op; |
| 4292 | } | |
| 4293 | 383 | slot = JSVAL_TO_INT(lval); |
| 4294 | 383 | obj = fp->varobj; |
| 4295 | 383 | rval = OBJ_GET_SLOT(cx, obj, slot); |
| 4296 | 383 | PUSH_OPND(rval); |
| 4297 | 383 | break; |
| 4298 | ||
| 4299 | case JSOP_SETGVAR: | |
| 4300 | 67 | slot = GET_VARNO(pc); |
| 4301 | JS_ASSERT(slot < fp->nvars); | |
| 4302 | 67 | rval = FETCH_OPND(-1); |
| 4303 | 67 | lval = fp->vars[slot]; |
| 4304 | 67 | obj = fp->varobj; |
| 4305 | 67 | if (JSVAL_IS_NULL(lval)) { |
| 4306 | /* | |
| 4307 | * Inline-clone and specialize JSOP_SETNAME code here because | |
| 4308 | * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] | |
| 4309 | * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. | |
| 4310 | */ | |
| 4311 | 0 | atom = GET_ATOM(cx, script, pc); |
| 4312 | 0 | id = ATOM_TO_JSID(atom); |
| 4313 | 0 | SAVE_SP(fp); |
| 4314 | 0 | CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); |
| 4315 | 0 | if (!ok) |
| 4316 | 0 | goto out; |
| 4317 | 0 | STORE_OPND(-1, rval); |
| 4318 | } else { | |
| 4319 | 67 | slot = JSVAL_TO_INT(lval); |
| 4320 | 67 | GC_POKE(cx, obj->slots[slot]); |
| 4321 | 67 | OBJ_SET_SLOT(cx, obj, slot, rval); |
| 4322 | } | |
| 4323 | 67 | obj = NULL; |
| 4324 | 67 | break; |
| 4325 | ||
| 4326 | case JSOP_DEFCONST: | |
| 4327 | case JSOP_DEFVAR: | |
| 4328 | 4617 | atomIndex = GET_ATOM_INDEX(pc); |
| 4329 | ||
| 4330 | 4617 | do_JSOP_DEFCONST: |
| 4331 | 4617 | do_JSOP_DEFVAR: |
| 4332 | 4617 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); |
| 4333 | 4617 | obj = fp->varobj; |
| 4334 | 4617 | attrs = JSPROP_ENUMERATE; |
| 4335 | 4617 | if (!(fp->flags & JSFRAME_EVAL)) |
| 4336 | 4617 | attrs |= JSPROP_PERMANENT; |
| 4337 | 4617 | if (op == JSOP_DEFCONST) |
| 4338 | 0 | attrs |= JSPROP_READONLY; |
| 4339 | ||
| 4340 | /* Lookup id in order to check for redeclaration problems. */ | |
| 4341 | 4617 | id = ATOM_TO_JSID(atom); |
| 4342 | 4617 | SAVE_SP(fp); |
| 4343 | 4617 | ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop); |
| 4344 | 4617 | if (!ok) |
| 4345 | 4617 | goto out; |
| 4346 | ||
| 4347 | /* Bind a variable only if it's not yet defined. */ | |
| 4348 | 4617 | if (!prop) { |
| 4349 | 1093 | ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, |
| 4350 | attrs, &prop); | |
| 4351 | 1093 | if (!ok) |
| 4352 | 1093 | goto out; |
| 4353 | JS_ASSERT(prop); | |
| 4354 | 1093 | obj2 = obj; |
| 4355 | } | |
| 4356 | ||
| 4357 | /* | |
| 4358 | * Try to optimize a property we either just created, or found | |
| 4359 | * directly in the global object, that is permanent, has a slot, | |
| 4360 | * and has stub getter and setter, into a "fast global" accessed | |
| 4361 | * by the JSOP_*GVAR opcodes. | |
| 4362 | */ | |
| 4363 | 4617 | if (atomIndex < script->numGlobalVars && |
| 4364 | (attrs & JSPROP_PERMANENT) && | |
| 4365 | obj2 == obj && | |
| 4366 | OBJ_IS_NATIVE(obj)) { | |
| 4367 | 170 | sprop = (JSScopeProperty *) prop; |
| 4368 | 170 | if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) && |
| 4369 | SPROP_HAS_STUB_GETTER(sprop) && | |
| 4370 | SPROP_HAS_STUB_SETTER(sprop)) { | |
| 4371 | /* | |
| 4372 | * Fast globals use fp->vars to map the global name's | |
| 4373 | * atomIndex to the permanent fp->varobj slot number, | |
| 4374 | * tagged as a jsval. The atomIndex for the global's | |
| 4375 | * name literal is identical to its fp->vars index. | |
| 4376 | */ | |
| 4377 | 170 | fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); |
| 4378 | } | |
| 4379 | } | |
| 4380 | ||
| 4381 | 4617 | OBJ_DROP_PROPERTY(cx, obj2, prop); |
| 4382 | break; | |
| 4383 | ||
| 4384 | 3774 | BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) |
| 4385 | { | |
| 4386 | uintN flags; | |
| 4387 | ||
| 4388 | 3774 | atomIndex = GET_ATOM_INDEX(pc); |
| 4389 | 3774 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); |
| 4390 | 3774 | obj = ATOM_TO_OBJECT(atom); |
| 4391 | 3774 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 4392 | 3774 | id = ATOM_TO_JSID(fun->atom); |
| 4393 | ||
| 4394 | /* | |
| 4395 | * We must be at top-level (either outermost block that forms a | |
| 4396 | * function's body, or a global) scope, not inside an expression | |
| 4397 | * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE) | |
| 4398 | * in the same compilation unit (ECMA Program). | |
| 4399 | * | |
| 4400 | * However, we could be in a Program being eval'd from inside a | |
| 4401 | * with statement, so we need to distinguish scope chain head from | |
| 4402 | * variables object. Hence the obj2 vs. parent distinction below. | |
| 4403 | * First we make sure the function object we're defining has the | |
| 4404 | * right scope chain. Then we define its name in fp->varobj. | |
| 4405 | * | |
| 4406 | * If static link is not current scope, clone fun's object to link | |
| 4407 | * to the current scope via parent. This clause exists to enable | |
| 4408 | * sharing of compiled functions among multiple equivalent scopes, | |
| 4409 | * splitting the cost of compilation evenly among the scopes and | |
| 4410 | * amortizing it over a number of executions. Examples include XUL | |
| 4411 | * scripts and event handlers shared among Mozilla chrome windows, | |
| 4412 | * and server-side JS user-defined functions shared among requests. | |
| 4413 | * | |
| 4414 | * NB: The Script object exposes compile and exec in the language, | |
| 4415 | * such that this clause introduces an incompatible change from old | |
| 4416 | * JS versions that supported Script. Such a JS version supported | |
| 4417 | * executing a script that defined and called functions scoped by | |
| 4418 | * the compile-time static link, not by the exec-time scope chain. | |
| 4419 | * | |
| 4420 | * We sacrifice compatibility, breaking such scripts, in order to | |
| 4421 | * promote compile-cost sharing and amortizing, and because Script | |
| 4422 | * is not and will not be standardized. | |
| 4423 | */ | |
| 4424 | 3774 | obj2 = fp->scopeChain; |
| 4425 | 3774 | if (OBJ_GET_PARENT(cx, obj) != obj2) { |
| 4426 | 0 | obj = js_CloneFunctionObject(cx, obj, obj2); |
| 4427 | 0 | if (!obj) { |
| 4428 | 0 | ok = JS_FALSE; |
| 4429 | 0 | goto out; |
| 4430 | } | |
| 4431 | } | |
| 4432 | ||
| 4433 | /* | |
| 4434 | * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All | |
| 4435 | * paths from here must flow through the "Restore fp->scopeChain" | |
| 4436 | * code below the OBJ_DEFINE_PROPERTY call. | |
| 4437 | */ | |
| 4438 | 3774 | fp->scopeChain = obj; |
| 4439 | 3774 | rval = OBJECT_TO_JSVAL(obj); |
| 4440 | ||
| 4441 | /* | |
| 4442 | * ECMA requires functions defined when entering Global code to be | |
| 4443 | * permanent, and functions defined when entering Eval code to be | |
| 4444 | * impermanent. | |
| 4445 | */ | |
| 4446 | 3774 | attrs = JSPROP_ENUMERATE; |
| 4447 | 3774 | if (!(fp->flags & JSFRAME_EVAL)) |
| 4448 | 3774 | attrs |= JSPROP_PERMANENT; |
| 4449 | ||
| 4450 | /* | |
| 4451 | * Load function flags that are also property attributes. Getters | |
| 4452 | * and setters do not need a slot, their value is stored elsewhere | |
| 4453 | * in the property itself, not in obj->slots. | |
| 4454 | */ | |
| 4455 | 3774 | flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); |
| 4456 | 3774 | if (flags) { |
| 4457 | 0 | attrs |= flags | JSPROP_SHARED; |
| 4458 | 0 | rval = JSVAL_VOID; |
| 4459 | } | |
| 4460 | ||
| 4461 | /* | |
| 4462 | * Check for a const property of the same name -- or any kind | |
| 4463 | * of property if executing with the strict option. We check | |
| 4464 | * here at runtime as well as at compile-time, to handle eval | |
| 4465 | * as well as multiple HTML script tags. | |
| 4466 | */ | |
| 4467 | 3774 | parent = fp->varobj; |
| 4468 | 3774 | SAVE_SP(fp); |
| 4469 | 3774 | ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL); |
| 4470 | 3774 | if (ok) { |
| 4471 | 3774 | ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval, |
| 4472 | (flags & JSFUN_GETTER) | |
| 4473 | ? (JSPropertyOp) obj | |
| 4474 | : NULL, | |
| 4475 | (flags & JSFUN_SETTER) | |
| 4476 | ? (JSPropertyOp) obj | |
| 4477 | : NULL, | |
| 4478 | attrs, | |
| 4479 | &prop); | |
| 4480 | } | |
| 4481 | ||
| 4482 | /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ | |
| 4483 | 3774 | fp->scopeChain = obj2; |
| 4484 | 3774 | if (!ok) |
| 4485 | 3774 | goto out; |
| 4486 | ||
| 4487 | #if 0 | |
| 4488 | if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) && | |
| 4489 | script->numGlobalVars) { | |
| 4490 | /* | |
| 4491 | * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals | |
| 4492 | * use fp->vars to map the global function name's atomIndex to | |
| 4493 | * its permanent fp->varobj slot number, tagged as a jsval. | |
| 4494 | */ | |
| 4495 | sprop = (JSScopeProperty *) prop; | |
| 4496 | fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); | |
| 4497 | } | |
| 4498 | #endif | |
| 4499 | 3774 | OBJ_DROP_PROPERTY(cx, parent, prop); |
| 4500 | } | |
| 4501 | END_LITOPX_CASE | |
| 4502 | ||
| 4503 | #if JS_HAS_LEXICAL_CLOSURE | |
| 4504 | 0 | BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) |
| 4505 | /* | |
| 4506 | * Define a local function (i.e., one nested at the top level of | |
| 4507 | * another function), parented by the current scope chain, and | |
| 4508 | * stored in a local variable slot that the compiler allocated. | |
| 4509 | * This is an optimization over JSOP_DEFFUN that avoids requiring | |
| 4510 | * a call object for the outer function's activation. | |
| 4511 | */ | |
| 4512 | 0 | slot = GET_VARNO(pc); |
| 4513 | 0 | atom = js_GetAtom(cx, &script->atomMap, atomIndex); |
| 4514 | 0 | obj = ATOM_TO_OBJECT(atom); |
| 4515 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 4516 | ||
| 4517 | 0 | parent = fp->scopeChain; |
| 4518 | 0 | if (OBJ_GET_PARENT(cx, obj) != parent) { |
| 4519 | 0 | SAVE_SP(fp); |
| 4520 | 0 | obj = js_CloneFunctionObject(cx, obj, parent); |
| 4521 | 0 | if (!obj) { |
| 4522 | 0 | ok = JS_FALSE; |
| 4523 | 0 | goto out; |
| 4524 | } | |
| 4525 | } | |
| 4526 | 0 | fp->vars[slot] = OBJECT_TO_JSVAL(obj); |
| 4527 | 0 | END_LITOPX_CASE |
| 4528 | ||
| 4529 | 0 | BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) |
| 4530 | /* Push the specified function object literal. */ | |
| 4531 | 0 | obj = ATOM_TO_OBJECT(atom); |
| 4532 | ||
| 4533 | /* If re-parenting, push a clone of the function object. */ | |
| 4534 | 0 | parent = fp->scopeChain; |
| 4535 | 0 | if (OBJ_GET_PARENT(cx, obj) != parent) { |
| 4536 | 0 | SAVE_SP(fp); |
| 4537 | 0 | obj = js_CloneFunctionObject(cx, obj, parent); |
| 4538 | 0 | if (!obj) { |
| 4539 | 0 | ok = JS_FALSE; |
| 4540 | 0 | goto out; |
| 4541 | } | |
| 4542 | } | |
| 4543 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 4544 | 0 | obj = NULL; |
| 4545 | 0 | END_LITOPX_CASE |
| 4546 | ||
| 4547 | 0 | BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) |
| 4548 | /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ | |
| 4549 | 0 | rval = ATOM_KEY(atom); |
| 4550 | JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval)); | |
| 4551 | ||
| 4552 | /* | |
| 4553 | * 1. Create a new object as if by the expression new Object(). | |
| 4554 | * 2. Add Result(1) to the front of the scope chain. | |
| 4555 | * | |
| 4556 | * Step 2 is achieved by making the new object's parent be the | |
| 4557 | * current scope chain, and then making the new object the parent | |
| 4558 | * of the Function object clone. | |
| 4559 | */ | |
| 4560 | 0 | SAVE_SP(fp); |
| 4561 | 0 | obj2 = fp->scopeChain; |
| 4562 | 0 | parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2); |
| 4563 | 0 | if (!parent) { |
| 4564 | 0 | ok = JS_FALSE; |
| 4565 | 0 | goto out; |
| 4566 | } | |
| 4567 | ||
| 4568 | /* | |
| 4569 | * 3. Create a new Function object as specified in section 13.2 | |
| 4570 | * with [parameters and body specified by the function expression | |
| 4571 | * that was parsed by the compiler into a Function object, and | |
| 4572 | * saved in the script's atom map]. | |
| 4573 | * | |
| 4574 | * Protect parent from GC after js_CloneFunctionObject calls into | |
| 4575 | * js_NewObject, which displaces the newborn object root in cx by | |
| 4576 | * allocating the clone, then runs a last-ditch GC while trying | |
| 4577 | * to allocate the clone's slots vector. Another, multi-threaded | |
| 4578 | * path: js_CloneFunctionObject => js_NewObject => OBJ_GET_CLASS | |
| 4579 | * which may suspend the current request in ClaimScope, with the | |
| 4580 | * newborn displaced as in the first scenario. | |
| 4581 | */ | |
| 4582 | 0 | fp->scopeChain = parent; |
| 4583 | 0 | obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent); |
| 4584 | 0 | if (!obj) { |
| 4585 | 0 | ok = JS_FALSE; |
| 4586 | 0 | goto out; |
| 4587 | } | |
| 4588 | ||
| 4589 | /* | |
| 4590 | * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All | |
| 4591 | * paths from here must flow through the "Restore fp->scopeChain" | |
| 4592 | * code below the OBJ_DEFINE_PROPERTY call. | |
| 4593 | */ | |
| 4594 | 0 | fp->scopeChain = obj; |
| 4595 | 0 | rval = OBJECT_TO_JSVAL(obj); |
| 4596 | ||
| 4597 | /* | |
| 4598 | * 4. Create a property in the object Result(1). The property's | |
| 4599 | * name is [fun->atom, the identifier parsed by the compiler], | |
| 4600 | * value is Result(3), and attributes are { DontDelete, ReadOnly }. | |
| 4601 | */ | |
| 4602 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 4603 | 0 | attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); |
| 4604 | 0 | if (attrs) { |
| 4605 | 0 | attrs |= JSPROP_SHARED; |
| 4606 | 0 | rval = JSVAL_VOID; |
| 4607 | } | |
| 4608 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, |
| 4609 | (attrs & JSFUN_GETTER) | |
| 4610 | ? (JSPropertyOp) obj | |
| 4611 | : NULL, | |
| 4612 | (attrs & JSFUN_SETTER) | |
| 4613 | ? (JSPropertyOp) obj | |
| 4614 | : NULL, | |
| 4615 | attrs | | |
| 4616 | JSPROP_ENUMERATE | JSPROP_PERMANENT | | |
| 4617 | JSPROP_READONLY, | |
| 4618 | NULL); | |
| 4619 | ||
| 4620 | /* Restore fp->scopeChain now that obj is defined in parent. */ | |
| 4621 | 0 | fp->scopeChain = obj2; |
| 4622 | 0 | if (!ok) { |
| 4623 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 4624 | 0 | goto out; |
| 4625 | } | |
| 4626 | ||
| 4627 | /* | |
| 4628 | * 5. Remove Result(1) from the front of the scope chain [no-op]. | |
| 4629 | * 6. Return Result(3). | |
| 4630 | */ | |
| 4631 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 4632 | 0 | obj = NULL; |
| 4633 | 0 | END_LITOPX_CASE |
| 4634 | ||
| 4635 | 0 | BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) |
| 4636 | /* | |
| 4637 | * ECMA ed. 3 extension: a named function expression in a compound | |
| 4638 | * statement (not at the top statement level of global code, or at | |
| 4639 | * the top level of a function body). | |
| 4640 | * | |
| 4641 | * Get immediate operand atom, which is a function object literal. | |
| 4642 | * From it, get the function to close. | |
| 4643 | */ | |
| 4644 | JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom))); | |
| 4645 | 0 | obj = ATOM_TO_OBJECT(atom); |
| 4646 | ||
| 4647 | /* | |
| 4648 | * Clone the function object with the current scope chain as the | |
| 4649 | * clone's parent. The original function object is the prototype | |
| 4650 | * of the clone. Do this only if re-parenting; the compiler may | |
| 4651 | * have seen the right parent already and created a sufficiently | |
| 4652 | * well-scoped function object. | |
| 4653 | */ | |
| 4654 | 0 | SAVE_SP(fp); |
| 4655 | 0 | obj2 = fp->scopeChain; |
| 4656 | 0 | if (OBJ_GET_PARENT(cx, obj) != obj2) { |
| 4657 | 0 | obj = js_CloneFunctionObject(cx, obj, obj2); |
| 4658 | 0 | if (!obj) { |
| 4659 | 0 | ok = JS_FALSE; |
| 4660 | 0 | goto out; |
| 4661 | } | |
| 4662 | } | |
| 4663 | ||
| 4664 | /* | |
| 4665 | * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All | |
| 4666 | * paths from here must flow through the "Restore fp->scopeChain" | |
| 4667 | * code below the OBJ_DEFINE_PROPERTY call. | |
| 4668 | */ | |
| 4669 | 0 | fp->scopeChain = obj; |
| 4670 | 0 | rval = OBJECT_TO_JSVAL(obj); |
| 4671 | ||
| 4672 | /* | |
| 4673 | * Make a property in fp->varobj with id fun->atom and value obj, | |
| 4674 | * unless fun is a getter or setter (in which case, obj is cast to | |
| 4675 | * a JSPropertyOp and passed accordingly). | |
| 4676 | */ | |
| 4677 | 0 | fun = (JSFunction *) JS_GetPrivate(cx, obj); |
| 4678 | 0 | attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER); |
| 4679 | 0 | if (attrs) { |
| 4680 | 0 | attrs |= JSPROP_SHARED; |
| 4681 | 0 | rval = JSVAL_VOID; |
| 4682 | } | |
| 4683 | 0 | parent = fp->varobj; |
| 4684 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval, |
| 4685 | (attrs & JSFUN_GETTER) | |
| 4686 | ? (JSPropertyOp) obj | |
| 4687 | : NULL, | |
| 4688 | (attrs & JSFUN_SETTER) | |
| 4689 | ? (JSPropertyOp) obj | |
| 4690 | : NULL, | |
| 4691 | attrs | JSPROP_ENUMERATE | |
| 4692 | | JSPROP_PERMANENT, | |
| 4693 | &prop); | |
| 4694 | ||
| 4695 | /* Restore fp->scopeChain now that obj is defined in fp->varobj. */ | |
| 4696 | 0 | fp->scopeChain = obj2; |
| 4697 | 0 | if (!ok) { |
| 4698 | 0 | cx->newborn[GCX_OBJECT] = NULL; |
| 4699 | 0 | goto out; |
| 4700 | } | |
| 4701 | ||
| 4702 | #if 0 | |
| 4703 | if (attrs == 0 && script->numGlobalVars) { | |
| 4704 | /* | |
| 4705 | * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals | |
| 4706 | * use fp->vars to map the global function name's atomIndex to | |
| 4707 | * its permanent fp->varobj slot number, tagged as a jsval. | |
| 4708 | */ | |
| 4709 | sprop = (JSScopeProperty *) prop; | |
| 4710 | fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); | |
| 4711 | } | |
| 4712 | #endif | |
| 4713 | 0 | OBJ_DROP_PROPERTY(cx, parent, prop); |
| 4714 | END_LITOPX_CASE | |
| 4715 | #endif /* JS_HAS_LEXICAL_CLOSURE */ | |
| 4716 | ||
| 4717 | #if JS_HAS_GETTER_SETTER | |
| 4718 | case JSOP_GETTER: | |
| 4719 | case JSOP_SETTER: | |
| 4720 | JS_ASSERT(len == 1); | |
| 4721 | 0 | op2 = (JSOp) *++pc; |
| 4722 | 0 | cs = &js_CodeSpec[op2]; |
| 4723 | 0 | len = cs->length; |
| 4724 | 0 | switch (op2) { |
| 4725 | case JSOP_SETNAME: | |
| 4726 | case JSOP_SETPROP: | |
| 4727 | 0 | atom = GET_ATOM(cx, script, pc); |
| 4728 | 0 | id = ATOM_TO_JSID(atom); |
| 4729 | 0 | rval = FETCH_OPND(-1); |
| 4730 | 0 | i = -1; |
| 4731 | 0 | goto gs_pop_lval; |
| 4732 | ||
| 4733 | case JSOP_SETELEM: | |
| 4734 | 0 | rval = FETCH_OPND(-1); |
| 4735 | 0 | FETCH_ELEMENT_ID(-2, id); |
| 4736 | 0 | i = -2; |
| 4737 | 0 | gs_pop_lval: |
| 4738 | 0 | FETCH_OBJECT(cx, i - 1, lval, obj); |
| 4739 | 0 | break; |
| 4740 | ||
| 4741 | #if JS_HAS_INITIALIZERS | |
| 4742 | case JSOP_INITPROP: | |
| 4743 | JS_ASSERT(sp - fp->spbase >= 2); | |
| 4744 | 0 | rval = FETCH_OPND(-1); |
| 4745 | 0 | i = -1; |
| 4746 | 0 | atom = GET_ATOM(cx, script, pc); |
| 4747 | 0 | id = ATOM_TO_JSID(atom); |
| 4748 | 0 | goto gs_get_lval; |
| 4749 | ||
| 4750 | case JSOP_INITELEM: | |
| 4751 | JS_ASSERT(sp - fp->spbase >= 3); | |
| 4752 | 0 | rval = FETCH_OPND(-1); |
| 4753 | 0 | FETCH_ELEMENT_ID(-2, id); |
| 4754 | 0 | i = -2; |
| 4755 | 0 | gs_get_lval: |
| 4756 | 0 | lval = FETCH_OPND(i-1); |
| 4757 | JS_ASSERT(JSVAL_IS_OBJECT(lval)); | |
| 4758 | 0 | obj = JSVAL_TO_OBJECT(lval); |
| 4759 | break; | |
| 4760 | #endif /* JS_HAS_INITIALIZERS */ | |
| 4761 | ||
| 4762 | default: | |
| 4763 | JS_ASSERT(0); | |
| 4764 | } | |
| 4765 | ||
| 4766 | /* Ensure that id has a type suitable for use with obj. */ | |
| 4767 | 0 | CHECK_ELEMENT_ID(obj, id); |
| 4768 | ||
| 4769 | 0 | SAVE_SP(fp); |
| 4770 | 0 | if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) { |
| 4771 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 4772 | JSMSG_BAD_GETTER_OR_SETTER, | |
| 4773 | (op == JSOP_GETTER) | |
| 4774 | ? js_getter_str | |
| 4775 | : js_setter_str); | |
| 4776 | 0 | ok = JS_FALSE; |
| 4777 | 0 | goto out; |
| 4778 | } | |
| 4779 | ||
| 4780 | /* | |
| 4781 | * Getters and setters are just like watchpoints from an access | |
| 4782 | * control point of view. | |
| 4783 | */ | |
| 4784 | 0 | ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); |
| 4785 | 0 | if (!ok) |
| 4786 | 0 | goto out; |
| 4787 | ||
| 4788 | 0 | if (op == JSOP_GETTER) { |
| 4789 | 0 | getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); |
| 4790 | 0 | setter = NULL; |
| 4791 | 0 | attrs = JSPROP_GETTER; |
| 4792 | } else { | |
| 4793 | 0 | getter = NULL; |
| 4794 | 0 | setter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); |
| 4795 | 0 | attrs = JSPROP_SETTER; |
| 4796 | } | |
| 4797 | 0 | attrs |= JSPROP_ENUMERATE | JSPROP_SHARED; |
| 4798 | ||
| 4799 | /* Check for a readonly or permanent property of the same name. */ | |
| 4800 | 0 | ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL); |
| 4801 | 0 | if (!ok) |
| 4802 | 0 | goto out; |
| 4803 | ||
| 4804 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, |
| 4805 | attrs, NULL); | |
| 4806 | 0 | if (!ok) |
| 4807 | 0 | goto out; |
| 4808 | ||
| 4809 | 0 | obj = NULL; |
| 4810 | 0 | sp += i; |
| 4811 | 0 | if (cs->ndefs) |
| 4812 | 0 | STORE_OPND(-1, rval); |
| 4813 | break; | |
| 4814 | #endif /* JS_HAS_GETTER_SETTER */ | |
| 4815 | ||
| 4816 | #if JS_HAS_INITIALIZERS | |
| 4817 | case JSOP_NEWINIT: | |
| 4818 | 14976 | argc = 0; |
| 4819 | 14976 | fp->sharpDepth++; |
| 4820 | 14976 | goto do_new; |
| 4821 | ||
| 4822 | case JSOP_ENDINIT: | |
| 4823 | 14976 | if (--fp->sharpDepth == 0) |
| 4824 | 14976 | fp->sharpArray = NULL; |
| 4825 | ||
| 4826 | /* Re-set the newborn root to the top of this object tree. */ | |
| 4827 | JS_ASSERT(sp - fp->spbase >= 1); | |
| 4828 | 14976 | lval = FETCH_OPND(-1); |
| 4829 | JS_ASSERT(JSVAL_IS_OBJECT(lval)); | |
| 4830 | 14976 | cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); |
| 4831 | 14976 | break; |
| 4832 | ||
| 4833 | case JSOP_INITPROP: | |
| 4834 | /* Pop the property's value into rval. */ | |
| 4835 | JS_ASSERT(sp - fp->spbase >= 2); | |
| 4836 | 0 | rval = FETCH_OPND(-1); |
| 4837 | ||
| 4838 | /* Get the immediate property name into id. */ | |
| 4839 | 0 | atom = GET_ATOM(cx, script, pc); |
| 4840 | 0 | id = ATOM_TO_JSID(atom); |
| 4841 | 0 | i = -1; |
| 4842 | 0 | goto do_init; |
| 4843 | ||
| 4844 | case JSOP_INITELEM: | |
| 4845 | /* Pop the element's value into rval. */ | |
| 4846 | JS_ASSERT(sp - fp->spbase >= 3); | |
| 4847 | 88418 | rval = FETCH_OPND(-1); |
| 4848 | ||
| 4849 | /* Pop and conditionally atomize the element id. */ | |
| 4850 | 88418 | FETCH_ELEMENT_ID(-2, id); |
| 4851 | 88418 | i = -2; |
| 4852 | ||
| 4853 | 88418 | do_init: |
| 4854 | /* Find the object being initialized at top of stack. */ | |
| 4855 | 88418 | lval = FETCH_OPND(i-1); |
| 4856 | JS_ASSERT(JSVAL_IS_OBJECT(lval)); | |
| 4857 | 88418 | obj = JSVAL_TO_OBJECT(lval); |
| 4858 | ||
| 4859 | /* Ensure that id has a type suitable for use with obj. */ | |
| 4860 | 88418 | CHECK_ELEMENT_ID(obj, id); |
| 4861 | ||
| 4862 | /* Set the property named by obj[id] to rval. */ | |
| 4863 | 88418 | SAVE_SP(fp); |
| 4864 | 88418 | ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); |
| 4865 | 88418 | if (!ok) |
| 4866 | 88418 | goto out; |
| 4867 | 88418 | sp += i; |
| 4868 | 88418 | break; |
| 4869 | ||
| 4870 | #if JS_HAS_SHARP_VARS | |
| 4871 | case JSOP_DEFSHARP: | |
| 4872 | 0 | SAVE_SP(fp); |
| 4873 | 0 | obj = fp->sharpArray; |
| 4874 | 0 | if (!obj) { |
| 4875 | 0 | obj = js_NewArrayObject(cx, 0, NULL); |
| 4876 | 0 | if (!obj) { |
| 4877 | 0 | ok = JS_FALSE; |
| 4878 | 0 | goto out; |
| 4879 | } | |
| 4880 | 0 | fp->sharpArray = obj; |
| 4881 | } | |
| 4882 | 0 | i = (jsint) GET_ATOM_INDEX(pc); |
| 4883 | 0 | id = INT_TO_JSID(i); |
| 4884 | 0 | rval = FETCH_OPND(-1); |
| 4885 | 0 | if (JSVAL_IS_PRIMITIVE(rval)) { |
| 4886 | char numBuf[12]; | |
| 4887 | 0 | JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); |
| 4888 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 4889 | JSMSG_BAD_SHARP_DEF, numBuf); | |
| 4890 | 0 | ok = JS_FALSE; |
| 4891 | 0 | goto out; |
| 4892 | } | |
| 4893 | 0 | ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); |
| 4894 | 0 | if (!ok) |
| 4895 | goto out; | |
| 4896 | break; | |
| 4897 | ||
| 4898 | case JSOP_USESHARP: | |
| 4899 | 0 | i = (jsint) GET_ATOM_INDEX(pc); |
| 4900 | 0 | id = INT_TO_JSID(i); |
| 4901 | 0 | obj = fp->sharpArray; |
| 4902 | 0 | if (!obj) { |
| 4903 | 0 | rval = JSVAL_VOID; |
| 4904 | } else { | |
| 4905 | 0 | SAVE_SP(fp); |
| 4906 | 0 | ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); |
| 4907 | 0 | if (!ok) |
| 4908 | 0 | goto out; |
| 4909 | } | |
| 4910 | 0 | if (!JSVAL_IS_OBJECT(rval)) { |
| 4911 | char numBuf[12]; | |
| 4912 | 0 | JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); |
| 4913 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 4914 | JSMSG_BAD_SHARP_USE, numBuf); | |
| 4915 | 0 | ok = JS_FALSE; |
| 4916 | 0 | goto out; |
| 4917 | } | |
| 4918 | 0 | PUSH_OPND(rval); |
| 4919 | 0 | break; |
| 4920 | #endif /* JS_HAS_SHARP_VARS */ | |
| 4921 | #endif /* JS_HAS_INITIALIZERS */ | |
| 4922 | ||
| 4923 | #if JS_HAS_EXCEPTIONS | |
| 4924 | /* No-ops for ease of decompilation and jit'ing. */ | |
| 4925 | case JSOP_TRY: | |
| 4926 | case JSOP_FINALLY: | |
| 4927 | break; | |
| 4928 | ||
| 4929 | /* Reset the stack to the given depth. */ | |
| 4930 | case JSOP_SETSP: | |
| 4931 | 0 | i = (jsint) GET_ATOM_INDEX(pc); |
| 4932 | JS_ASSERT(i >= 0); | |
| 4933 | 0 | sp = fp->spbase + i; |
| 4934 | ||
| 4935 | 0 | obj = fp->scopeChain; |
| 4936 | 0 | while (OBJ_GET_CLASS(cx, obj) == &js_WithClass && |
| 4937 | JS_GetPrivate(cx, obj) == fp && | |
| 4938 | OBJ_BLOCK_DEPTH(cx, obj) >= i) { | |
| 4939 | 0 | obj = OBJ_GET_PARENT(cx, obj); |
| 4940 | } | |
| 4941 | 0 | fp->scopeChain = obj; |
| 4942 | 0 | break; |
| 4943 | ||
| 4944 | case JSOP_GOSUB: | |
| 4945 | JS_ASSERT(cx->exception != JSVAL_HOLE); | |
| 4946 | 0 | if (!cx->throwing) { |
| 4947 | 0 | lval = JSVAL_HOLE; |
| 4948 | } else { | |
| 4949 | 0 | lval = cx->exception; |
| 4950 | 0 | cx->throwing = JS_FALSE; |
| 4951 | } | |
| 4952 | 0 | PUSH(lval); |
| 4953 | 0 | i = PTRDIFF(pc, script->main, jsbytecode) + len; |
| 4954 | 0 | len = GET_JUMP_OFFSET(pc); |
| 4955 | 0 | PUSH(INT_TO_JSVAL(i)); |
| 4956 | 0 | break; |
| 4957 | ||
| 4958 | case JSOP_GOSUBX: | |
| 4959 | JS_ASSERT(cx->exception != JSVAL_HOLE); | |
| 4960 | 0 | lval = cx->throwing ? cx->exception : JSVAL_HOLE; |
| 4961 | 0 | PUSH(lval); |
| 4962 | 0 | i = PTRDIFF(pc, script->main, jsbytecode) + len; |
| 4963 | 0 | len = GET_JUMPX_OFFSET(pc); |
| 4964 | 0 | PUSH(INT_TO_JSVAL(i)); |
| 4965 | 0 | break; |
| 4966 | ||
| 4967 | case JSOP_RETSUB: | |
| 4968 | 0 | rval = POP(); |
| 4969 | JS_ASSERT(JSVAL_IS_INT(rval)); | |
| 4970 | 0 | lval = POP(); |
| 4971 | 0 | if (lval != JSVAL_HOLE) { |
| 4972 | /* | |
| 4973 | * Exception was pending during finally, throw it *before* we | |
| 4974 | * adjust pc, because pc indexes into script->trynotes. This | |
| 4975 | * turns out not to be necessary, but it seems clearer. And | |
| 4976 | * it points out a FIXME: 350509, due to Igor Bukanov. | |
| 4977 | */ | |
| 4978 | 0 | cx->throwing = JS_TRUE; |
| 4979 | 0 | cx->exception = lval; |
| 4980 | 0 | ok = JS_FALSE; |
| 4981 | 0 | goto out; |
| 4982 | } | |
| 4983 | 0 | i = JSVAL_TO_INT(rval); |
| 4984 | 0 | pc = script->main + i; |
| 4985 | 0 | len = 0; |
| 4986 | 0 | break; |
| 4987 | ||
| 4988 | case JSOP_EXCEPTION: | |
| 4989 | 0 | PUSH(cx->exception); |
| 4990 | 0 | cx->throwing = JS_FALSE; |
| 4991 | 0 | break; |
| 4992 | ||
| 4993 | case JSOP_THROWING: | |
| 4994 | JS_ASSERT(!cx->throwing); | |
| 4995 | 0 | cx->throwing = JS_TRUE; |
| 4996 | 0 | break; |
| 4997 | ||
| 4998 | case JSOP_THROW: | |
| 4999 | 0 | cx->throwing = JS_TRUE; |
| 5000 | 0 | cx->exception = POP_OPND(); |
| 5001 | 0 | ok = JS_FALSE; |
| 5002 | /* let the code at out try to catch the exception. */ | |
| 5003 | 0 | goto out; |
| 5004 | ||
| 5005 | 0 | BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0) |
| 5006 | /* Load the value into rval, while keeping it live on stack. */ | |
| 5007 | JS_ASSERT(sp - fp->spbase >= 2); | |
| 5008 | 0 | rval = FETCH_OPND(-1); |
| 5009 | ||
| 5010 | /* Get the immediate catch variable name into id. */ | |
| 5011 | 0 | id = ATOM_TO_JSID(atom); |
| 5012 | ||
| 5013 | /* Find the object being initialized at top of stack. */ | |
| 5014 | 0 | lval = FETCH_OPND(-2); |
| 5015 | JS_ASSERT(JSVAL_IS_OBJECT(lval)); | |
| 5016 | 0 | obj = JSVAL_TO_OBJECT(lval); |
| 5017 | ||
| 5018 | 0 | SAVE_SP(fp); |
| 5019 | ||
| 5020 | /* | |
| 5021 | * It's possible for an evil script to substitute a random object | |
| 5022 | * for the new object. Check to make sure that we don't override a | |
| 5023 | * readonly property with the below OBJ_DEFINE_PROPERTY. | |
| 5024 | */ | |
| 5025 | 0 | ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs); |
| 5026 | 0 | if (!ok) |
| 5027 | 0 | goto out; |
| 5028 | 0 | if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT | |
| 5029 | JSPROP_GETTER | JSPROP_SETTER))) { | |
| 5030 | /* Define obj[id] to contain rval and to be permanent. */ | |
| 5031 | 0 | ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, |
| 5032 | JSPROP_PERMANENT, NULL); | |
| 5033 | 0 | if (!ok) |
| 5034 | 0 | goto out; |
| 5035 | } | |
| 5036 | ||
| 5037 | /* Now that we're done with rval, pop it. */ | |
| 5038 | 0 | sp--; |
| 5039 | 0 | END_LITOPX_CASE |
| 5040 | #endif /* JS_HAS_EXCEPTIONS */ | |
| 5041 | ||
| 5042 | #if JS_HAS_INSTANCEOF | |
| 5043 | case JSOP_INSTANCEOF: | |
| 5044 | 441 | rval = FETCH_OPND(-1); |
| 5045 | 441 | if (JSVAL_IS_PRIMITIVE(rval) || |
| 5046 | !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { | |
| 5047 | 0 | SAVE_SP(fp); |
| 5048 | 0 | str = js_DecompileValueGenerator(cx, -1, rval, NULL); |
| 5049 | 0 | if (str) { |
| 5050 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 5051 | JSMSG_BAD_INSTANCEOF_RHS, | |
| 5052 | JS_GetStringBytes(str)); | |
| 5053 | } | |
| 5054 | 0 | ok = JS_FALSE; |
| 5055 | 0 | goto out; |
| 5056 | } | |
| 5057 | 441 | lval = FETCH_OPND(-2); |
| 5058 | 441 | cond = JS_FALSE; |
| 5059 | 441 | SAVE_SP(fp); |
| 5060 | 441 | ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); |
| 5061 | 441 | if (!ok) |
| 5062 | 441 | goto out; |
| 5063 | 441 | sp--; |
| 5064 | 441 | STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); |
| 5065 | 441 | break; |
| 5066 | #endif /* JS_HAS_INSTANCEOF */ | |
| 5067 | ||
| 5068 | #if JS_HAS_DEBUGGER_KEYWORD | |
| 5069 | case JSOP_DEBUGGER: | |
| 5070 | { | |
| 5071 | 0 | JSTrapHandler handler = rt->debuggerHandler; |
| 5072 | 0 | if (handler) { |
| 5073 | 0 | SAVE_SP(fp); |
| 5074 | switch (handler(cx, script, pc, &rval, | |
| 5075 | 0 | rt->debuggerHandlerData)) { |
| 5076 | case JSTRAP_ERROR: | |
| 5077 | 0 | ok = JS_FALSE; |
| 5078 | 0 | goto out; |
| 5079 | case JSTRAP_CONTINUE: | |
| 5080 | break; | |
| 5081 | case JSTRAP_RETURN: | |
| 5082 | 0 | fp->rval = rval; |
| 5083 | 0 | goto out; |
| 5084 | #if JS_HAS_EXCEPTIONS | |
| 5085 | case JSTRAP_THROW: | |
| 5086 | 0 | cx->throwing = JS_TRUE; |
| 5087 | 0 | cx->exception = rval; |
| 5088 | 0 | ok = JS_FALSE; |
| 5089 | 0 | goto out; |
| 5090 | #endif /* JS_HAS_EXCEPTIONS */ | |
| 5091 | default:; | |
| 5092 | } | |
| 5093 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 5094 | } | |
| 5095 | break; | |
| 5096 | } | |
| 5097 | #endif /* JS_HAS_DEBUGGER_KEYWORD */ | |
| 5098 | ||
| 5099 | #if JS_HAS_XML_SUPPORT | |
| 5100 | case JSOP_DEFXMLNS: | |
| 5101 | 0 | rval = POP(); |
| 5102 | 0 | SAVE_SP(fp); |
| 5103 | 0 | ok = js_SetDefaultXMLNamespace(cx, rval); |
| 5104 | 0 | if (!ok) |
| 5105 | goto out; | |
| 5106 | break; | |
| 5107 | ||
| 5108 | case JSOP_ANYNAME: | |
| 5109 | 0 | SAVE_SP(fp); |
| 5110 | 0 | ok = js_GetAnyName(cx, &rval); |
| 5111 | 0 | if (!ok) |
| 5112 | 0 | goto out; |
| 5113 | 0 | PUSH_OPND(rval); |
| 5114 | 0 | break; |
| 5115 | ||
| 5116 | 0 | BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) |
| 5117 | 0 | PUSH_OPND(ATOM_KEY(atom)); |
| 5118 | 0 | END_LITOPX_CASE |
| 5119 | ||
| 5120 | 0 | BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) |
| 5121 | 0 | rval = ATOM_KEY(atom); |
| 5122 | 0 | lval = FETCH_OPND(-1); |
| 5123 | 0 | SAVE_SP(fp); |
| 5124 | 0 | obj = js_ConstructXMLQNameObject(cx, lval, rval); |
| 5125 | 0 | if (!obj) { |
| 5126 | 0 | ok = JS_FALSE; |
| 5127 | 0 | goto out; |
| 5128 | } | |
| 5129 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 5130 | 0 | END_LITOPX_CASE |
| 5131 | ||
| 5132 | case JSOP_QNAME: | |
| 5133 | 0 | rval = FETCH_OPND(-1); |
| 5134 | 0 | lval = FETCH_OPND(-2); |
| 5135 | 0 | SAVE_SP(fp); |
| 5136 | 0 | obj = js_ConstructXMLQNameObject(cx, lval, rval); |
| 5137 | 0 | if (!obj) { |
| 5138 | 0 | ok = JS_FALSE; |
| 5139 | 0 | goto out; |
| 5140 | } | |
| 5141 | 0 | sp--; |
| 5142 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 5143 | 0 | break; |
| 5144 | ||
| 5145 | case JSOP_TOATTRNAME: | |
| 5146 | 0 | rval = FETCH_OPND(-1); |
| 5147 | 0 | SAVE_SP(fp); |
| 5148 | 0 | ok = js_ToAttributeName(cx, &rval); |
| 5149 | 0 | if (!ok) |
| 5150 | 0 | goto out; |
| 5151 | 0 | STORE_OPND(-1, rval); |
| 5152 | 0 | break; |
| 5153 | ||
| 5154 | case JSOP_TOATTRVAL: | |
| 5155 | 0 | rval = FETCH_OPND(-1); |
| 5156 | JS_ASSERT(JSVAL_IS_STRING(rval)); | |
| 5157 | 0 | SAVE_SP(fp); |
| 5158 | 0 | str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); |
| 5159 | 0 | if (!str) { |
| 5160 | 0 | ok = JS_FALSE; |
| 5161 | 0 | goto out; |
| 5162 | } | |
| 5163 | 0 | STORE_OPND(-1, STRING_TO_JSVAL(str)); |
| 5164 | 0 | break; |
| 5165 | ||
| 5166 | case JSOP_ADDATTRNAME: | |
| 5167 | case JSOP_ADDATTRVAL: | |
| 5168 | 0 | rval = FETCH_OPND(-1); |
| 5169 | 0 | lval = FETCH_OPND(-2); |
| 5170 | 0 | str = JSVAL_TO_STRING(lval); |
| 5171 | 0 | str2 = JSVAL_TO_STRING(rval); |
| 5172 | 0 | SAVE_SP(fp); |
| 5173 | 0 | str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); |
| 5174 | 0 | if (!str) { |
| 5175 | 0 | ok = JS_FALSE; |
| 5176 | 0 | goto out; |
| 5177 | } | |
| 5178 | 0 | sp--; |
| 5179 | 0 | STORE_OPND(-1, STRING_TO_JSVAL(str)); |
| 5180 | 0 | break; |
| 5181 | ||
| 5182 | case JSOP_BINDXMLNAME: | |
| 5183 | 0 | lval = FETCH_OPND(-1); |
| 5184 | 0 | SAVE_SP(fp); |
| 5185 | 0 | ok = js_FindXMLProperty(cx, lval, &obj, &rval); |
| 5186 | 0 | if (!ok) |
| 5187 | 0 | goto out; |
| 5188 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 5189 | 0 | PUSH_OPND(rval); |
| 5190 | 0 | break; |
| 5191 | ||
| 5192 | case JSOP_SETXMLNAME: | |
| 5193 | 0 | obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); |
| 5194 | 0 | lval = FETCH_OPND(-2); |
| 5195 | 0 | rval = FETCH_OPND(-1); |
| 5196 | 0 | SAVE_SP(fp); |
| 5197 | 0 | ok = js_SetXMLProperty(cx, obj, lval, &rval); |
| 5198 | 0 | if (!ok) |
| 5199 | 0 | goto out; |
| 5200 | 0 | sp -= 2; |
| 5201 | 0 | STORE_OPND(-1, rval); |
| 5202 | 0 | obj = NULL; |
| 5203 | 0 | break; |
| 5204 | ||
| 5205 | case JSOP_XMLNAME: | |
| 5206 | 0 | lval = FETCH_OPND(-1); |
| 5207 | 0 | SAVE_SP(fp); |
| 5208 | 0 | ok = js_FindXMLProperty(cx, lval, &obj, &rval); |
| 5209 | 0 | if (!ok) |
| 5210 | 0 | goto out; |
| 5211 | 0 | ok = js_GetXMLProperty(cx, obj, rval, &rval); |
| 5212 | 0 | if (!ok) |
| 5213 | 0 | goto out; |
| 5214 | 0 | STORE_OPND(-1, rval); |
| 5215 | 0 | break; |
| 5216 | ||
| 5217 | case JSOP_DESCENDANTS: | |
| 5218 | case JSOP_DELDESC: | |
| 5219 | 0 | FETCH_OBJECT(cx, -2, lval, obj); |
| 5220 | 0 | rval = FETCH_OPND(-1); |
| 5221 | 0 | SAVE_SP(fp); |
| 5222 | 0 | ok = js_GetXMLDescendants(cx, obj, rval, &rval); |
| 5223 | 0 | if (!ok) |
| 5224 | 0 | goto out; |
| 5225 | ||
| 5226 | 0 | if (op == JSOP_DELDESC) { |
| 5227 | 0 | sp[-1] = rval; /* set local root */ |
| 5228 | 0 | ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); |
| 5229 | 0 | if (!ok) |
| 5230 | 0 | goto out; |
| 5231 | 0 | rval = JSVAL_TRUE; /* always succeed */ |
| 5232 | } | |
| 5233 | ||
| 5234 | 0 | sp--; |
| 5235 | 0 | STORE_OPND(-1, rval); |
| 5236 | 0 | break; |
| 5237 | ||
| 5238 | case JSOP_FILTER: | |
| 5239 | 0 | FETCH_OBJECT(cx, -1, lval, obj); |
| 5240 | 0 | len = GET_JUMP_OFFSET(pc); |
| 5241 | 0 | SAVE_SP(fp); |
| 5242 | 0 | ok = js_FilterXMLList(cx, obj, pc + cs->length, &rval); |
| 5243 | 0 | if (!ok) |
| 5244 | 0 | goto out; |
| 5245 | JS_ASSERT(fp->sp == sp); | |
| 5246 | 0 | STORE_OPND(-1, rval); |
| 5247 | 0 | break; |
| 5248 | ||
| 5249 | case JSOP_ENDFILTER: | |
| 5250 | 0 | *result = POP_OPND(); |
| 5251 | 0 | goto out; |
| 5252 | ||
| 5253 | case JSOP_STARTXML: | |
| 5254 | case JSOP_STARTXMLEXPR: | |
| 5255 | break; | |
| 5256 | ||
| 5257 | case JSOP_TOXML: | |
| 5258 | 0 | rval = FETCH_OPND(-1); |
| 5259 | 0 | SAVE_SP(fp); |
| 5260 | 0 | obj = js_ValueToXMLObject(cx, rval); |
| 5261 | 0 | if (!obj) { |
| 5262 | 0 | ok = JS_FALSE; |
| 5263 | 0 | goto out; |
| 5264 | } | |
| 5265 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 5266 | 0 | break; |
| 5267 | ||
| 5268 | case JSOP_TOXMLLIST: | |
| 5269 | 0 | rval = FETCH_OPND(-1); |
| 5270 | 0 | SAVE_SP(fp); |
| 5271 | 0 | obj = js_ValueToXMLListObject(cx, rval); |
| 5272 | 0 | if (!obj) { |
| 5273 | 0 | ok = JS_FALSE; |
| 5274 | 0 | goto out; |
| 5275 | } | |
| 5276 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 5277 | 0 | break; |
| 5278 | ||
| 5279 | case JSOP_XMLTAGEXPR: | |
| 5280 | 0 | rval = FETCH_OPND(-1); |
| 5281 | 0 | SAVE_SP(fp); |
| 5282 | 0 | str = js_ValueToString(cx, rval); |
| 5283 | 0 | if (!str) { |
| 5284 | 0 | ok = JS_FALSE; |
| 5285 | 0 | goto out; |
| 5286 | } | |
| 5287 | 0 | STORE_OPND(-1, STRING_TO_JSVAL(str)); |
| 5288 | 0 | break; |
| 5289 | ||
| 5290 | case JSOP_XMLELTEXPR: | |
| 5291 | 0 | rval = FETCH_OPND(-1); |
| 5292 | 0 | SAVE_SP(fp); |
| 5293 | 0 | if (VALUE_IS_XML(cx, rval)) { |
| 5294 | 0 | str = js_ValueToXMLString(cx, rval); |
| 5295 | } else { | |
| 5296 | 0 | str = js_ValueToString(cx, rval); |
| 5297 | 0 | if (str) |
| 5298 | 0 | str = js_EscapeElementValue(cx, str); |
| 5299 | } | |
| 5300 | 0 | if (!str) { |
| 5301 | 0 | ok = JS_FALSE; |
| 5302 | 0 | goto out; |
| 5303 | } | |
| 5304 | 0 | STORE_OPND(-1, STRING_TO_JSVAL(str)); |
| 5305 | 0 | break; |
| 5306 | ||
| 5307 | 0 | BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) |
| 5308 | 0 | SAVE_SP(fp); |
| 5309 | 0 | obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); |
| 5310 | 0 | if (!obj) { |
| 5311 | 0 | ok = JS_FALSE; |
| 5312 | 0 | goto out; |
| 5313 | } | |
| 5314 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 5315 | 0 | obj = NULL; |
| 5316 | 0 | END_LITOPX_CASE |
| 5317 | ||
| 5318 | 0 | BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) |
| 5319 | 0 | str = ATOM_TO_STRING(atom); |
| 5320 | 0 | obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); |
| 5321 | 0 | if (!obj) { |
| 5322 | 0 | ok = JS_FALSE; |
| 5323 | 0 | goto out; |
| 5324 | } | |
| 5325 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 5326 | 0 | END_LITOPX_CASE |
| 5327 | ||
| 5328 | 0 | BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) |
| 5329 | 0 | str = ATOM_TO_STRING(atom); |
| 5330 | 0 | obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); |
| 5331 | 0 | if (!obj) { |
| 5332 | 0 | ok = JS_FALSE; |
| 5333 | 0 | goto out; |
| 5334 | } | |
| 5335 | 0 | PUSH_OPND(OBJECT_TO_JSVAL(obj)); |
| 5336 | 0 | END_LITOPX_CASE |
| 5337 | ||
| 5338 | 0 | BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) |
| 5339 | 0 | str = ATOM_TO_STRING(atom); |
| 5340 | 0 | rval = FETCH_OPND(-1); |
| 5341 | 0 | str2 = JSVAL_TO_STRING(rval); |
| 5342 | 0 | SAVE_SP(fp); |
| 5343 | 0 | obj = js_NewXMLSpecialObject(cx, |
| 5344 | JSXML_CLASS_PROCESSING_INSTRUCTION, | |
| 5345 | str, str2); | |
| 5346 | 0 | if (!obj) { |
| 5347 | 0 | ok = JS_FALSE; |
| 5348 | 0 | goto out; |
| 5349 | } | |
| 5350 | 0 | STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); |
| 5351 | 0 | END_LITOPX_CASE |
| 5352 | ||
| 5353 | 57327 | BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) |
| 5354 | /* Get an immediate atom naming the property. */ | |
| 5355 | 57327 | id = ATOM_TO_JSID(atom); |
| 5356 | 57327 | FETCH_OBJECT(cx, -1, lval, obj); |
| 5357 | 57327 | SAVE_SP(fp); |
| 5358 | ||
| 5359 | /* Special-case XML object method lookup, per ECMA-357. */ | |
| 5360 | 57327 | if (OBJECT_IS_XML(cx, obj)) { |
| 5361 | JSXMLObjectOps *ops; | |
| 5362 | ||
| 5363 | 0 | ops = (JSXMLObjectOps *) obj->map->ops; |
| 5364 | 0 | obj = ops->getMethod(cx, obj, id, &rval); |
| 5365 | 0 | if (!obj) |
| 5366 | 0 | ok = JS_FALSE; |
| 5367 | } else { | |
| 5368 | 57327 | CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); |
| 5369 | } | |
| 5370 | 57327 | if (!ok) |
| 5371 | 57327 | goto out; |
| 5372 | 57327 | STORE_OPND(-1, rval); |
| 5373 | 57327 | END_LITOPX_CASE |
| 5374 | ||
| 5375 | 0 | BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) |
| 5376 | /* Get an immediate atom naming the property. */ | |
| 5377 | 0 | id = ATOM_TO_JSID(atom); |
| 5378 | 0 | rval = FETCH_OPND(-1); |
| 5379 | 0 | FETCH_OBJECT(cx, -2, lval, obj); |
| 5380 | 0 | SAVE_SP(fp); |
| 5381 | ||
| 5382 | /* Special-case XML object method lookup, per ECMA-357. */ | |
| 5383 | 0 | if (OBJECT_IS_XML(cx, obj)) { |
| 5384 | JSXMLObjectOps *ops; | |
| 5385 | ||
| 5386 | 0 | ops = (JSXMLObjectOps *) obj->map->ops; |
| 5387 | 0 | ok = ops->setMethod(cx, obj, id, &rval); |
| 5388 | } else { | |
| 5389 | 0 | CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); |
| 5390 | } | |
| 5391 | 0 | if (!ok) |
| 5392 | 0 | goto out; |
| 5393 | 0 | --sp; |
| 5394 | 0 | STORE_OPND(-1, rval); |
| 5395 | 0 | obj = NULL; |
| 5396 | 0 | END_LITOPX_CASE |
| 5397 | ||
| 5398 | case JSOP_GETFUNNS: | |
| 5399 | 0 | ok = js_GetFunctionNamespace(cx, &rval); |
| 5400 | 0 | if (!ok) |
| 5401 | 0 | goto out; |
| 5402 | 0 | PUSH_OPND(rval); |
| 5403 | 0 | break; |
| 5404 | ||
| 5405 | case JSOP_FOREACH: | |
| 5406 | 0 | foreach = JS_TRUE; |
| 5407 | 0 | break; |
| 5408 | #endif /* JS_HAS_XML_SUPPORT */ | |
| 5409 | ||
| 5410 | default: { | |
| 5411 | char numBuf[12]; | |
| 5412 | 0 | JS_snprintf(numBuf, sizeof numBuf, "%d", op); |
| 5413 | 0 | JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, |
| 5414 | JSMSG_BAD_BYTECODE, numBuf); | |
| 5415 | 0 | ok = JS_FALSE; |
| 5416 | 0 | goto out; |
| 5417 | } | |
| 5418 | } | |
| 5419 | ||
| 5420 | 7456733 | advance_pc: |
| 5421 | 7456733 | pc += len; |
| 5422 | ||
| 5423 | #ifdef DEBUG | |
| 5424 | if (tracefp) { | |
| 5425 | intN ndefs, n; | |
| 5426 | jsval *siter; | |
| 5427 | ||
| 5428 | ndefs = cs->ndefs; | |
| 5429 | if (ndefs) { | |
| 5430 | SAVE_SP(fp); | |
| 5431 | if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) | |
| 5432 | --ndefs; | |
| 5433 | for (n = -ndefs; n < 0; n++) { | |
| 5434 | str = js_DecompileValueGenerator(cx, n, sp[n], NULL); | |
| 5435 | if (str) { | |
| 5436 | fprintf(tracefp, "%s %s", | |
| 5437 | (n == -ndefs) ? " output:" : ",", | |
| 5438 | JS_GetStringBytes(str)); | |
| 5439 | } | |
| 5440 | } | |
| 5441 | fprintf(tracefp, " @ %d\n", sp - fp->spbase); | |
| 5442 | } | |
| 5443 | fprintf(tracefp, " stack: "); | |
| 5444 | for (siter = fp->spbase; siter < sp; siter++) { | |
| 5445 | str = js_ValueToSource(cx, *siter); | |
| 5446 | fprintf(tracefp, "%s ", | |
| 5447 | str ? JS_GetStringBytes(str) : "<null>"); | |
| 5448 | } | |
| 5449 | fputc('\n', tracefp); | |
| 5450 | } | |
| 5451 | #endif | |
| 5452 | } | |
| 5453 | 31698 | out: |
| 5454 | ||
| 5455 | #if JS_HAS_EXCEPTIONS | |
| 5456 | 31698 | if (!ok) { |
| 5457 | /* | |
| 5458 | * Has an exception been raised? Also insist that we are in the | |
| 5459 | * interpreter activation that pushed fp's operand stack, to avoid | |
| 5460 | * catching exceptions within XML filtering predicate expressions, | |
| 5461 | * such as the one from tests/e4x/Regress/regress-301596.js: | |
| 5462 | * | |
| 5463 | * try { | |
| 5464 | * <xml/>.(@a == 1); | |
| 5465 | * throw 5; | |
| 5466 | * } catch (e) { | |
| 5467 | * } | |
| 5468 | * | |
| 5469 | * The inner interpreter activation executing the predicate bytecode | |
| 5470 | * will throw "reference to undefined XML name @a" (or 5, in older | |
| 5471 | * versions that followed the first edition of ECMA-357 and evaluated | |
| 5472 | * unbound identifiers to undefined), and the exception must not be | |
| 5473 | * caught until control unwinds to the outer interpreter activation. | |
| 5474 | * | |
| 5475 | * Otherwise, the wrong stack depth will be restored by JSOP_SETSP, | |
| 5476 | * and the catch will move into the filtering predicate expression, | |
| 5477 | * leading to double catch execution if it rethrows. | |
| 5478 | * | |
| 5479 | * XXX This assumes the null mark case implies XML filtering predicate | |
| 5480 | * expression execution! | |
| 5481 | * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 | |
| 5482 | */ | |
| 5483 | 0 | if (cx->throwing && JS_LIKELY(mark != NULL)) { |
| 5484 | /* | |
| 5485 | * Call debugger throw hook if set (XXX thread safety?). | |
| 5486 | */ | |
| 5487 | 0 | JSTrapHandler handler = rt->throwHook; |
| 5488 | 0 | if (handler) { |
| 5489 | 0 | SAVE_SP(fp); |
| 5490 | 0 | switch (handler(cx, script, pc, &rval, rt->throwHookData)) { |
| 5491 | case JSTRAP_ERROR: | |
| 5492 | 0 | cx->throwing = JS_FALSE; |
| 5493 | 0 | goto no_catch; |
| 5494 | case JSTRAP_RETURN: | |
| 5495 | 0 | ok = JS_TRUE; |
| 5496 | 0 | cx->throwing = JS_FALSE; |
| 5497 | 0 | fp->rval = rval; |
| 5498 | 0 | goto no_catch; |
| 5499 | case JSTRAP_THROW: | |
| 5500 | 0 | cx->exception = rval; |
| 5501 | case JSTRAP_CONTINUE: | |
| 5502 | default:; | |
| 5503 | } | |
| 5504 | 0 | LOAD_INTERRUPT_HANDLER(rt); |
| 5505 | } | |
| 5506 | ||
| 5507 | /* | |
| 5508 | * Look for a try block in script that can catch this exception. | |
| 5509 | */ | |
| 5510 | 0 | SCRIPT_FIND_CATCH_START(script, pc, pc); |
| 5511 | 0 | if (pc) { |
| 5512 | /* Don't clear cx->throwing to save cx->exception from GC. */ | |
| 5513 | 0 | len = 0; |
| 5514 | 0 | ok = JS_TRUE; |
| 5515 | #if JS_HAS_XML_SUPPORT | |
| 5516 | 0 | foreach = JS_FALSE; |
| 5517 | #endif | |
| 5518 | 0 | goto advance_pc; |
| 5519 | } | |
| 5520 | } | |
| 5521 | 31698 | no_catch:; |
| 5522 | } | |
| 5523 | #endif | |
| 5524 | ||
| 5525 | /* | |
| 5526 | * Check whether control fell off the end of a lightweight function, or an | |
| 5527 | * exception thrown under such a function was not caught by it. If so, go | |
| 5528 | * to the inline code under JSOP_RETURN. | |
| 5529 | */ | |
| 5530 | 31698 | if (inlineCallCount) { |
| 5531 | #if JS_HAS_XML_SUPPORT | |
| 5532 | 30230 | foreach = JS_FALSE; |
| 5533 | #endif | |
| 5534 | 30230 | goto inline_return; |
| 5535 | } | |
| 5536 | ||
| 5537 | /* | |
| 5538 | * Reset sp before freeing stack slots, because our caller may GC soon. | |
| 5539 | * Clear spbase to indicate that we've popped the 2 * depth operand slots. | |
| 5540 | * Restore the previous frame's execution state. | |
| 5541 | */ | |
| 5542 | 1468 | if (JS_LIKELY(mark != NULL)) { |
| 5543 | 1468 | fp->sp = fp->spbase; |
| 5544 | 1468 | fp->spbase = NULL; |
| 5545 | 1468 | js_FreeRawStack(cx, mark); |
| 5546 | } else { | |
| 5547 | 0 | SAVE_SP(fp); |
| 5548 | } | |
| 5549 | ||
| 5550 | 1468 | out2: |
| 5551 | 1468 | if (cx->version == currentVersion && currentVersion != originalVersion) |
| 5552 | 0 | js_SetVersion(cx, originalVersion); |
| 5553 | 1468 | cx->interpLevel--; |
| 5554 | 1468 | return ok; |
| 5555 | ||
| 5556 | 0 | atom_not_defined: |
| 5557 | { | |
| 5558 | 0 | const char *printable = js_AtomToPrintableString(cx, atom); |
| 5559 | 0 | if (printable) |
| 5560 | 0 | js_ReportIsNotDefined(cx, printable); |
| 5561 | 0 | ok = JS_FALSE; |
| 5562 | 0 | goto out; |
| 5563 | } |