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 ***** */
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"
70 #if JS_HAS_JIT
71 #include "jsjit.h"
72 #endif
74 #if JS_HAS_XML_SUPPORT
75 #include "jsxml.h"
76 #endif
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
95 void
96 js_FlushPropertyCache(JSContext *cx)
97 {
98 JSPropertyCache *cache;
100 cache = &cx->runtime->propertyCache;
101 if (cache->empty) {
102 ASSERT_CACHE_IS_EMPTY(cache);
103 return;
104 }
105 memset(cache->table, 0, sizeof cache->table);
106 cache->empty = JS_TRUE;
107 #ifdef JS_PROPERTY_CACHE_METERING
108 cache->flushes++;
109 #endif
110 }
112 void
113 js_DisablePropertyCache(JSContext *cx)
114 {
115 JS_ASSERT(!cx->runtime->propertyCache.disabled);
116 cx->runtime->propertyCache.disabled = JS_TRUE;
117 }
119 void
120 js_EnablePropertyCache(JSContext *cx)
121 {
122 JS_ASSERT(cx->runtime->propertyCache.disabled);
123 ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache);
124 cx->runtime->propertyCache.disabled = JS_FALSE;
125 }
127 /*
128 * Class for for/in loop property iterator objects.
129 */
130 #define JSSLOT_ITER_STATE JSSLOT_PRIVATE
132 static void
133 prop_iterator_finalize(JSContext *cx, JSObject *obj)
134 {
135 jsval iter_state;
136 jsval iteratee;
138 /* Protect against stillborn iterators. */
139 iter_state = obj->slots[JSSLOT_ITER_STATE];
140 iteratee = obj->slots[JSSLOT_PARENT];
141 if (!JSVAL_IS_NULL(iter_state) && !JSVAL_IS_PRIMITIVE(iteratee)) {
142 OBJ_ENUMERATE(cx, JSVAL_TO_OBJECT(iteratee), JSENUMERATE_DESTROY,
143 &iter_state, NULL);
144 }
145 js_RemoveRoot(cx->runtime, &obj->slots[JSSLOT_PARENT]);
146 }
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 };
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)
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])
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
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
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
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
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
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
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
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
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
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
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
326 JS_FRIEND_API(jsval *)
327 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
328 {
329 jsval *sp;
331 if (markp)
332 *markp = JS_ARENA_MARK(&cx->stackPool);
333 JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
334 if (!sp) {
335 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 return sp;
341 }
343 JS_FRIEND_API(void)
344 js_FreeRawStack(JSContext *cx, void *mark)
345 {
346 JS_ARENA_RELEASE(&cx->stackPool, mark);
347 }
349 JS_FRIEND_API(jsval *)
350 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
351 {
352 jsval *sp, *vp, *end;
353 JSArena *a;
354 JSStackHeader *sh;
355 JSStackFrame *fp;
357 /* Callers don't check for zero nslots: we do to avoid empty segments. */
358 if (nslots == 0) {
359 *markp = NULL;
360 return JS_ARENA_MARK(&cx->stackPool);
361 }
363 /* Allocate 2 extra slots for the stack segment header we'll likely need. */
364 sp = js_AllocRawStack(cx, 2 + nslots, markp);
365 if (!sp)
366 return NULL;
368 /* Try to avoid another header if we can piggyback on the last segment. */
369 a = cx->stackPool.current;
370 sh = cx->stackHeaders;
371 if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
372 /* Extend the last stack segment, give back the 2 header slots. */
373 sh->nslots += nslots;
374 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 fp = cx->fp;
383 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 end = fp->spbase + fp->script->depth;
390 for (vp = fp->sp; vp < end; vp++)
391 *vp = JSVAL_VOID;
392 }
394 /* Allocate and push a stack segment header from the 2 extra slots. */
395 sh = (JSStackHeader *)sp;
396 sh->nslots = nslots;
397 sh->down = cx->stackHeaders;
398 cx->stackHeaders = sh;
399 sp += 2;
400 }
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 memset(sp, 0, nslots * sizeof(jsval));
408 return sp;
409 }
411 JS_FRIEND_API(void)
412 js_FreeStack(JSContext *cx, void *mark)
413 {
414 JSStackHeader *sh;
415 jsuword slotdiff;
417 /* Check for zero nslots allocation special case. */
418 if (!mark)
419 return;
421 /* We can assert because js_FreeStack always balances js_AllocStack. */
422 sh = cx->stackHeaders;
423 JS_ASSERT(sh);
425 /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
426 slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
427 if (slotdiff < (jsuword)sh->nslots)
428 sh->nslots = slotdiff;
429 else
430 cx->stackHeaders = sh->down;
432 /* Release the stackPool space allocated since mark was set. */
433 JS_ARENA_RELEASE(&cx->stackPool, mark);
434 }
436 JSBool
437 js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
438 {
439 return JS_TRUE;
440 }
442 JSBool
443 js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
444 {
445 return JS_TRUE;
446 }
448 JSBool
449 js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
450 {
451 return JS_TRUE;
452 }
454 JSBool
455 js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
456 {
457 return JS_TRUE;
458 }
460 JSBool
461 js_ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
462 {
463 if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
464 /* Some objects (e.g., With) delegate 'this' to another object. */
465 thisp = OBJ_THIS_OBJECT(cx, thisp);
466 if (!thisp)
467 return JS_FALSE;
469 /* Default return value for a constructor is the new object. */
470 if (fp->flags & JSFRAME_CONSTRUCTING)
471 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 if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) ||
491 !OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2]))) {
492 thisp = cx->globalObject;
493 } else {
494 jsid id;
495 jsval v;
496 uintN attrs;
498 /* Walk up the parent chain. */
499 thisp = JSVAL_TO_OBJECT(fp->argv[-2]);
500 id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
501 for (;;) {
502 if (!OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs))
503 return JS_FALSE;
504 if (JSVAL_IS_VOID(v))
505 v = OBJ_GET_SLOT(cx, thisp, JSSLOT_PARENT);
506 if (JSVAL_IS_NULL(v))
507 break;
508 thisp = JSVAL_TO_OBJECT(v);
509 }
510 }
511 }
512 fp->thisp = thisp;
513 fp->argv[-1] = OBJECT_TO_JSVAL(thisp);
514 return JS_TRUE;
515 }
517 #ifdef DUMP_CALL_TABLE
519 #include "jsclist.h"
520 #include "jshash.h"
521 #include "jsdtoa.h"
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;
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)
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;
552 typedef struct CallEntry {
553 JSHashEntry entry;
554 CallKey key;
555 CallValue value;
556 char name[32]; /* function name copy */
557 } CallEntry;
559 static void *
560 AllocCallTable(void *pool, size_t size)
561 {
562 return malloc(size);
563 }
565 static void
566 FreeCallTable(void *pool, void *item)
567 {
568 free(item);
569 }
571 static JSHashEntry *
572 AllocCallEntry(void *pool, const void *key)
573 {
574 return (JSHashEntry*) calloc(1, sizeof(CallEntry));
575 }
577 static void
578 FreeCallEntry(void *pool, JSHashEntry *he, uintN flag)
579 {
580 JS_ASSERT(flag == HT_FREE_ENTRY);
581 free(he);
582 }
584 static JSHashAllocOps callTableAllocOps = {
585 AllocCallTable, FreeCallTable,
586 AllocCallEntry, FreeCallEntry
587 };
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;
595 if (ck->filename) {
596 hash = (hash << 4) ^ JS_HashString(ck->filename);
597 hash = (hash << 4) ^ ck->lineno;
598 }
599 return hash;
600 }
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;
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 }
614 JSHashTable *js_CallTable;
615 size_t js_LogCallToSourceLimit;
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;
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 }
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);
644 argc = JS_MIN(ce->value.maxargc, 8);
645 for (i = 0; i < argc; i++) {
646 ai = &ce->value.argInfo[i];
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 }
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 }
684 return HT_ENUMERATE_NEXT;
685 }
687 void
688 js_DumpCallTable(JSContext *cx)
689 {
690 char name[24];
691 FILE *fp;
692 static uintN dumpCount;
694 if (!js_CallTable)
695 return;
697 JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7);
698 dumpCount++;
699 fp = fopen(name, "w");
700 if (!fp)
701 return;
703 JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp);
704 fclose(fp);
705 }
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;
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 }
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);
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 }
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];
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 }
796 /* Move avc to the back of the LRU list. */
797 JS_REMOVE_LINK(&avc->lruLink);
798 JS_APPEND_LINK(&avc->lruLink, &ai->lruList);
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 }
846 #endif /* DUMP_CALL_TABLE */
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 {
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;
872 /* Mark the top of stack and load frequently-used registers. */
873 mark = JS_ARENA_MARK(&cx->stackPool);
874 fp = cx->fp;
875 sp = fp->sp;
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 vp = sp - (2 + argc);
885 v = *vp;
886 frame.rval = JSVAL_VOID;
887 frame.flags = flags;
888 thisp = JSVAL_TO_OBJECT(vp[1]);
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 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;
910 if (!fp->script || (flags & JSINVOKE_INTERNAL))
911 goto bad;
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 frame.argv = vp + 2;
927 ok = js_ComputeThis(cx, thisp, &frame);
928 if (!ok)
929 goto out2;
930 thisp = frame.thisp;
932 id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
933 if (OBJECT_IS_XML(cx, thisp)) {
934 JSXMLObjectOps *ops;
936 ops = (JSXMLObjectOps *) thisp->map->ops;
937 thisp = ops->getMethod(cx, thisp, id, &v);
938 if (!thisp) {
939 ok = JS_FALSE;
940 goto out2;
941 }
942 vp[1] = OBJECT_TO_JSVAL(thisp);
943 } else {
944 ok = OBJ_GET_PROPERTY(cx, thisp, id, &v);
945 }
946 if (!ok)
947 goto out2;
948 if (JSVAL_IS_PRIMITIVE(v))
949 goto bad;
951 pc = (jsbytecode *) vp[-(intN)fp->script->depth];
952 switch ((JSOp) *pc) {
953 case JSOP_NAME:
954 case JSOP_GETPROP:
955 #if JS_HAS_XML_SUPPORT
956 case JSOP_GETMETHOD:
957 #endif
958 atomIndex = GET_ATOM_INDEX(pc);
959 atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex);
960 argsobj = js_NewArrayObject(cx, argc, vp + 2);
961 if (!argsobj) {
962 ok = JS_FALSE;
963 goto out2;
964 }
966 sp = vp + 4;
967 if (argc < 2) {
968 a = cx->stackPool.current;
969 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 newsp = js_AllocRawStack(cx, 4, NULL);
977 if (!newsp) {
978 ok = JS_FALSE;
979 goto out2;
980 }
981 newsp[1] = OBJECT_TO_JSVAL(thisp);
982 sp = newsp + 4;
983 } 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 a->avail = (jsuword)sp;
992 }
993 }
995 sp[-4] = v;
996 JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp));
997 sp[-2] = ATOM_KEY(atom);
998 sp[-1] = OBJECT_TO_JSVAL(argsobj);
999 fp->sp = sp;
1000 argc = 2;
1001 break;
1003 default:
1004 goto bad;
1005 }
1006 #else
1007 goto bad;
1008 #endif
1009 }
1011 funobj = JSVAL_TO_OBJECT(v);
1012 parent = OBJ_GET_PARENT(cx, funobj);
1013 clasp = OBJ_GET_CLASS(cx, funobj);
1014 if (clasp != &js_FunctionClass) {
1015 /* Function is inlined, all other classes use object ops. */
1016 ops = funobj->map->ops;
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 if (JS_VERSION_IS_1_2(cx) ||
1028 ((ops == &js_ObjectOps) ? clasp->call : ops->call)) {
1029 ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1030 if (!ok)
1031 goto out2;
1033 if (JSVAL_IS_FUNCTION(cx, v)) {
1034 /* Make vp refer to funobj to keep it available as argv[-2]. */
1035 *vp = v;
1036 funobj = JSVAL_TO_OBJECT(v);
1037 parent = OBJ_GET_PARENT(cx, funobj);
1038 goto have_fun;
1039 }
1040 }
1041 fun = NULL;
1042 script = NULL;
1043 nslots = nvars = 0;
1045 /* Try a call or construct native object op. */
1046 native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call;
1047 if (!native)
1048 goto bad;
1049 } else {
1050 have_fun:
1051 /* Get private data and set derived locals from it. */
1052 fun = (JSFunction *) JS_GetPrivate(cx, funobj);
1053 if (fun->interpreted) {
1054 native = NULL;
1055 script = fun->u.script;
1056 } else {
1057 native = fun->u.native;
1058 script = NULL;
1059 }
1060 nslots = (fun->nargs > argc) ? fun->nargs - argc : 0;
1061 nslots += fun->extra;
1062 nvars = fun->nvars;
1064 /* Handle bound method special case. */
1065 if (fun->flags & JSFUN_BOUND_METHOD)
1066 thisp = parent;
1067 }
1069 /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */
1070 frame.varobj = NULL;
1071 frame.callobj = frame.argsobj = NULL;
1072 frame.script = script;
1073 frame.fun = fun;
1074 frame.argc = argc;
1075 frame.argv = sp - argc;
1076 frame.nvars = nvars;
1077 frame.vars = sp;
1078 frame.down = fp;
1079 frame.annotation = NULL;
1080 frame.scopeChain = NULL; /* set below for real, after cx->fp is set */
1081 frame.pc = NULL;
1082 frame.spbase = NULL;
1083 frame.sharpDepth = 0;
1084 frame.sharpArray = NULL;
1085 frame.dormantNext = NULL;
1086 frame.xmlNamespace = NULL;
1088 /* Compute the 'this' parameter and store it in frame as frame.thisp. */
1089 ok = js_ComputeThis(cx, thisp, &frame);
1090 if (!ok)
1091 goto out2;
1093 /* From here on, control must flow through label out: to return. */
1094 cx->fp = &frame;
1096 /* Init these now in case we goto out before first hook call. */
1097 hook = cx->runtime->callHook;
1098 hookData = NULL;
1100 /* Check for argument slots required by the function. */
1101 if (nslots) {
1102 /* All arguments must be contiguous, so we may have to copy actuals. */
1103 nalloc = nslots;
1104 limit = (jsval *) cx->stackPool.current->limit;
1105 if (sp + nslots > limit) {
1106 /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */
1107 nalloc += 2 + argc;
1108 } else {
1109 /* Take advantage of surplus slots in the caller's frame depth. */
1110 JS_ASSERT((jsval *)mark >= sp);
1111 surplus = (jsval *)mark - sp;
1112 nalloc -= surplus;
1113 }
1115 /* Check whether we have enough space in the caller's frame. */
1116 if ((intN)nalloc > 0) {
1117 /* Need space for actuals plus missing formals minus surplus. */
1118 newsp = js_AllocRawStack(cx, nalloc, NULL);
1119 if (!newsp) {
1120 ok = JS_FALSE;
1121 goto out;
1122 }
1124 /* If we couldn't allocate contiguous args, copy actuals now. */
1125 if (newsp != mark) {
1126 JS_ASSERT(sp + nslots > limit);
1127 JS_ASSERT(2 + argc + nslots == nalloc);
1128 *newsp++ = vp[0];
1129 *newsp++ = vp[1];
1130 if (argc)
1131 memcpy(newsp, frame.argv, argc * sizeof(jsval));
1132 frame.argv = newsp;
1133 sp = frame.vars = newsp + argc;
1134 }
1135 }
1137 /* Advance frame.vars to make room for the missing args. */
1138 frame.vars += nslots;
1140 /* Push void to initialize missing args. */
1141 do {
1142 PUSH(JSVAL_VOID);
1143 } while (--nslots != 0);
1144 }
1145 JS_ASSERT(nslots == 0);
1147 /* Now allocate stack space for local variables. */
1148 if (nvars) {
1149 JS_ASSERT((jsval *)cx->stackPool.current->avail >= frame.vars);
1150 surplus = (jsval *)cx->stackPool.current->avail - frame.vars;
1151 if (surplus < nvars) {
1152 newsp = js_AllocRawStack(cx, nvars, NULL);
1153 if (!newsp) {
1154 ok = JS_FALSE;
1155 goto out;
1156 }
1157 if (newsp != sp) {
1158 /* NB: Discontinuity between argv and vars. */
1159 sp = frame.vars = newsp;
1160 }
1161 }
1163 /* Push void to initialize local variables. */
1164 do {
1165 PUSH(JSVAL_VOID);
1166 } while (--nvars != 0);
1167 }
1168 JS_ASSERT(nvars == 0);
1170 /* Store the current sp in frame before calling fun. */
1171 SAVE_SP(&frame);
1173 /* call the hook if present */
1174 if (hook && (native || script))
1175 hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData);
1177 /* Call the function, either a native method or an interpreted script. */
1178 if (native) {
1179 #if JS_HAS_LVALUE_RETURN
1180 /* Set by JS_SetCallReturnValue2, used to return reference types. */
1181 cx->rval2set = JS_FALSE;
1182 #endif
1184 /* If native, use caller varobj and scopeChain for eval. */
1185 frame.varobj = fp->varobj;
1186 frame.scopeChain = fp->scopeChain;
1187 ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1188 JS_RUNTIME_METER(cx->runtime, nativeCalls);
1189 } 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 frame.scopeChain = parent;
1195 if (fun->flags & JSFUN_HEAVYWEIGHT) {
1196 #if JS_HAS_CALL_OBJECT
1197 /* Scope with a call object parented by the callee's parent. */
1198 if (!js_GetCallObject(cx, &frame, parent)) {
1199 ok = JS_FALSE;
1200 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 ok = js_Interpret(cx, script->code, &v);
1208 } else {
1209 /* fun might be onerror trying to report a syntax error in itself. */
1210 frame.scopeChain = NULL;
1211 ok = JS_TRUE;
1212 }
1214 out:
1215 if (hookData) {
1216 hook = cx->runtime->callHook;
1217 if (hook)
1218 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 if (frame.callobj)
1223 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 if (frame.argsobj)
1228 ok &= js_PutArgsObject(cx, &frame);
1229 #endif
1231 /* Restore cx->fp now that we're done releasing frame objects. */
1232 cx->fp = fp;
1234 out2:
1235 /* Pop everything we may have allocated off the stack. */
1236 JS_ARENA_RELEASE(&cx->stackPool, mark);
1238 /* Store the return value and restore sp just above it. */
1239 *vp = frame.rval;
1240 fp->sp = vp + 1;
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 if (fp->script && !(flags & JSINVOKE_INTERNAL))
1248 vp[-(intN)fp->script->depth] = (jsval)fp->pc;
1249 return ok;
1251 bad:
1252 js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT);
1253 ok = JS_FALSE;
1254 goto out2;
1255 }
1257 JSBool
1258 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1259 uintN argc, jsval *argv, jsval *rval)
1260 {
1261 JSStackFrame *fp, *oldfp, frame;
1262 jsval *oldsp, *sp;
1263 void *mark;
1264 uintN i;
1265 JSBool ok;
1267 fp = oldfp = cx->fp;
1268 if (!fp) {
1269 memset(&frame, 0, sizeof frame);
1270 cx->fp = fp = &frame;
1271 }
1272 oldsp = fp->sp;
1273 sp = js_AllocStack(cx, 2 + argc, &mark);
1274 if (!sp) {
1275 ok = JS_FALSE;
1276 goto out;
1277 }
1279 PUSH(fval);
1280 PUSH(OBJECT_TO_JSVAL(obj));
1281 for (i = 0; i < argc; i++)
1282 PUSH(argv[i]);
1283 SAVE_SP(fp);
1284 ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL);
1285 if (ok) {
1286 RESTORE_SP(fp);
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 *rval = POP_OPND();
1296 if (JSVAL_IS_GCTHING(*rval)) {
1297 if (cx->localRootStack) {
1298 if (js_PushLocalRoot(cx, cx->localRootStack, *rval) < 0)
1299 ok = JS_FALSE;
1300 } else {
1301 cx->lastInternalResult = *rval;
1302 }
1303 }
1304 }
1306 js_FreeStack(cx, mark);
1307 out:
1308 fp->sp = oldsp;
1309 if (oldfp != fp)
1310 cx->fp = oldfp;
1312 return ok;
1313 }
1315 JSBool
1316 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1317 JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1318 {
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 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 return JS_FALSE;
1341 }
1343 return js_InternalCall(cx, obj, fval, argc, argv, rval);
1344 }
1346 JSBool
1347 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1348 JSStackFrame *down, uintN flags, jsval *result)
1349 {
1350 JSInterpreterHook hook;
1351 void *hookData, *mark;
1352 JSStackFrame *oldfp, frame;
1353 JSObject *obj, *tmp;
1354 JSBool ok;
1356 hook = cx->runtime->executeHook;
1357 hookData = mark = NULL;
1358 oldfp = cx->fp;
1359 frame.callobj = frame.argsobj = NULL;
1360 frame.script = script;
1361 if (down) {
1362 /* Propagate arg/var state for eval and the debugger API. */
1363 frame.varobj = down->varobj;
1364 frame.fun = down->fun;
1365 frame.thisp = down->thisp;
1366 frame.argc = down->argc;
1367 frame.argv = down->argv;
1368 frame.nvars = down->nvars;
1369 frame.vars = down->vars;
1370 frame.annotation = down->annotation;
1371 frame.sharpArray = down->sharpArray;
1372 } else {
1373 obj = chain;
1374 if (cx->options & JSOPTION_VAROBJFIX) {
1375 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1376 obj = tmp;
1377 }
1378 frame.varobj = obj;
1379 frame.fun = NULL;
1380 frame.thisp = chain;
1381 frame.argc = 0;
1382 frame.argv = NULL;
1383 frame.nvars = script->numGlobalVars;
1384 if (frame.nvars) {
1385 frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
1386 if (!frame.vars)
1387 return JS_FALSE;
1388 memset(frame.vars, 0, frame.nvars * sizeof(jsval));
1389 } else {
1390 frame.vars = NULL;
1391 }
1392 frame.annotation = NULL;
1393 frame.sharpArray = NULL;
1394 }
1395 frame.rval = JSVAL_VOID;
1396 frame.down = down;
1397 frame.scopeChain = chain;
1398 frame.pc = NULL;
1399 frame.sp = oldfp ? oldfp->sp : NULL;
1400 frame.spbase = NULL;
1401 frame.sharpDepth = 0;
1402 frame.flags = flags;
1403 frame.dormantNext = NULL;
1404 frame.xmlNamespace = NULL;
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 if (oldfp && oldfp != down) {
1420 JS_ASSERT(!oldfp->dormantNext);
1421 oldfp->dormantNext = cx->dormantFrameChain;
1422 cx->dormantFrameChain = oldfp;
1423 }
1425 cx->fp = &frame;
1426 if (hook)
1427 hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData);
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 ok = js_Interpret(cx, script->code, &frame.rval);
1434 *result = frame.rval;
1436 if (hookData) {
1437 hook = cx->runtime->executeHook;
1438 if (hook)
1439 hook(cx, &frame, JS_FALSE, &ok, hookData);
1440 }
1441 if (mark)
1442 js_FreeRawStack(cx, mark);
1443 cx->fp = oldfp;
1445 if (oldfp && oldfp != down) {
1446 JS_ASSERT(cx->dormantFrameChain == oldfp);
1447 cx->dormantFrameChain = oldfp->dormantNext;
1448 oldfp->dormantNext = NULL;
1449 }
1451 return ok;
1452 }
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 {
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;
1470 if (JSVAL_IS_VOID(id)) {
1471 ida = JS_Enumerate(cx, obj);
1472 if (!ida)
1473 return JS_FALSE;
1474 ok = JS_TRUE;
1475 if (ida->length == 0)
1476 goto out;
1477 } else {
1478 ida = NULL;
1479 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1480 return JS_FALSE;
1481 if (!prop) {
1482 str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1483 ID_TO_VALUE(id), NULL);
1484 if (str)
1485 js_ReportIsNotDefined(cx, JS_GetStringBytes(str));
1486 return JS_FALSE;
1487 }
1488 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1489 OBJ_DROP_PROPERTY(cx, obj2, prop);
1490 if (!ok)
1491 return JS_FALSE;
1492 if (!(attrs & JSPROP_EXPORTED)) {
1493 str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1494 ID_TO_VALUE(id), NULL);
1495 if (str) {
1496 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1497 JSMSG_NOT_EXPORTED,
1498 JS_GetStringBytes(str));
1499 }
1500 return JS_FALSE;
1501 }
1502 }
1504 target = cx->fp->varobj;
1505 i = 0;
1506 do {
1507 if (ida) {
1508 id = ida->vector[i];
1509 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
1510 if (!ok)
1511 goto out;
1512 if (!(attrs & JSPROP_EXPORTED))
1513 continue;
1514 }
1515 ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs);
1516 if (!ok)
1517 goto out;
1518 if (JSVAL_IS_FUNCTION(cx, value)) {
1519 funobj = JSVAL_TO_OBJECT(value);
1520 closure = js_CloneFunctionObject(cx, funobj, obj);
1521 if (!closure) {
1522 ok = JS_FALSE;
1523 goto out;
1524 }
1525 value = OBJECT_TO_JSVAL(closure);
1526 }
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 if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
1538 ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
1539 if (!ok)
1540 goto out;
1541 } else {
1542 prop = NULL;
1543 }
1544 if (prop && target == obj2) {
1545 ok = OBJ_SET_PROPERTY(cx, target, id, &value);
1546 } else {
1547 ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
1548 attrs & ~(JSPROP_EXPORTED |
1549 JSPROP_GETTER |
1550 JSPROP_SETTER),
1551 NULL);
1552 }
1553 if (prop)
1554 OBJ_DROP_PROPERTY(cx, obj2, prop);
1555 if (!ok)
1556 goto out;
1557 } while (ida && ++i < ida->length);
1559 out:
1560 if (ida)
1561 JS_DestroyIdArray(cx, ida);
1562 return ok;
1563 }
1564 #endif /* JS_HAS_EXPORT_IMPORT */
1566 JSBool
1567 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1568 JSObject **objp, JSProperty **propp)
1569 {
1570 JSObject *obj2;
1571 JSProperty *prop;
1572 uintN oldAttrs, report;
1573 JSBool isFunction;
1574 jsval value;
1575 const char *type, *name;
1577 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1578 return JS_FALSE;
1579 if (propp) {
1580 *objp = obj2;
1581 *propp = prop;
1582 }
1583 if (!prop)
1584 return JS_TRUE;
1586 /* From here, return true, or goto bad on failure to drop prop. */
1587 if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs))
1588 goto bad;
1590 /* If either property is readonly, we have an error. */
1591 report = ((oldAttrs | attrs) & JSPROP_READONLY)
1592 ? JSREPORT_ERROR
1593 : JSREPORT_WARNING | JSREPORT_STRICT;
1595 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 if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1603 return JS_TRUE;
1604 if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1605 return JS_TRUE;
1606 if (!(oldAttrs & JSPROP_PERMANENT))
1607 return JS_TRUE;
1608 report = JSREPORT_ERROR;
1609 }
1611 isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1612 if (!isFunction) {
1613 if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1614 goto bad;
1615 isFunction = JSVAL_IS_FUNCTION(cx, value);
1616 }
1617 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 name = js_AtomToPrintableString(cx, JSID_TO_ATOM(id));
1627 if (!name)
1628 goto bad;
1629 return JS_ReportErrorFlagsAndNumber(cx, report,
1630 js_GetErrorMessage, NULL,
1631 JSMSG_REDECLARED_VAR,
1632 type, name);
1634 bad:
1635 if (propp) {
1636 *objp = NULL;
1637 *propp = NULL;
1638 }
1639 OBJ_DROP_PROPERTY(cx, obj2, prop);
1640 return JS_FALSE;
1641 }
1643 JSBool
1644 js_StrictlyEqual(jsval lval, jsval rval)
1645 {
1646 jsval ltag = JSVAL_TAG(lval), rtag = JSVAL_TAG(rval);
1647 jsdouble ld, rd;
1649 if (ltag == rtag) {
1650 if (ltag == JSVAL_STRING) {
1651 JSString *lstr = JSVAL_TO_STRING(lval),
1652 *rstr = JSVAL_TO_STRING(rval);
1653 return js_CompareStrings(lstr, rstr) == 0;
1654 }
1655 if (ltag == JSVAL_DOUBLE) {
1656 ld = *JSVAL_TO_DOUBLE(lval);
1657 rd = *JSVAL_TO_DOUBLE(rval);
1658 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1659 }
1660 return lval == rval;
1661 }
1662 if (ltag == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {
1663 ld = *JSVAL_TO_DOUBLE(lval);
1664 rd = JSVAL_TO_INT(rval);
1665 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1666 }
1667 if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
1668 ld = JSVAL_TO_INT(lval);
1669 rd = *JSVAL_TO_DOUBLE(rval);
1670 return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
1671 }
1672 return lval == rval;
1673 }
1675 static JSBool
1676 InternStringElementId(JSContext *cx, jsval idval, jsid *idp)
1677 {
1678 JSAtom *atom;
1680 atom = js_ValueToStringAtom(cx, idval);
1681 if (!atom)
1682 return JS_FALSE;
1683 *idp = ATOM_TO_JSID(atom);
1684 return JS_TRUE;
1685 }
1687 static JSBool
1688 InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp)
1689 {
1690 JS_ASSERT(!JSVAL_IS_INT(idval));
1692 #if JS_HAS_XML_SUPPORT
1693 if (JSVAL_IS_OBJECT(idval)) {
1694 *idp = OBJECT_JSVAL_TO_JSID(idval);
1695 return JS_TRUE;
1696 }
1697 #endif
1699 return InternStringElementId(cx, idval, idp);
1700 }
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
1713 #else
1714 #define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id))
1715 #endif
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
1725 #define MAX_INLINE_CALL_COUNT 1000
1727 JSBool
1728 js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result)
1729 {
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 JSBool foreach = JS_FALSE;
1775 #endif
1776 int stackDummy;
1778 *result = JSVAL_VOID;
1779 rt = cx->runtime;
1781 /* Set registerized frame pointer and derived script pointer. */
1782 fp = cx->fp;
1783 script = fp->script;
1785 /* Count of JS function calls that nest in this C js_Interpret frame. */
1786 inlineCallCount = 0;
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 currentVersion = script->version;
1798 originalVersion = cx->version;
1799 if (currentVersion != originalVersion)
1800 js_SetVersion(cx, currentVersion);
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)
1809 LOAD_BRANCH_CALLBACK(cx);
1810 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
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)
1829 LOAD_INTERRUPT_HANDLER(rt);
1831 /* Check for too much js_Interpret nesting, or too deep a C stack. */
1832 if (++cx->interpLevel == MAX_INTERP_LEVEL ||
1833 !JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1834 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1835 ok = JS_FALSE;
1836 goto out2;
1837 }
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 depth = (jsint) script->depth;
1845 if (JS_LIKELY(!fp->spbase)) {
1846 newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);
1847 if (!newsp) {
1848 ok = JS_FALSE;
1849 goto out2;
1850 }
1851 sp = newsp + depth;
1852 fp->spbase = sp;
1853 SAVE_SP(fp);
1854 } else {
1855 sp = fp->sp;
1856 JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval));
1857 newsp = fp->spbase - depth;
1858 mark = NULL;
1859 }
1861 endpc = script->code + script->length;
1862 while (pc < endpc) {
1863 fp->pc = pc;
1864 op = (JSOp) *pc;
1865 do_op:
1866 cs = &js_CodeSpec[op];
1867 len = cs->length;
1869 #ifdef DEBUG
1870 tracefp = (FILE *) cx->tracefp;
1871 if (tracefp) {
1872 intN nuses, n;
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
1894 if (interruptHandler) {
1895 SAVE_SP(fp);
1896 switch (interruptHandler(cx, script, pc, &rval,
1897 rt->interruptHandlerData)) {
1898 case JSTRAP_ERROR:
1899 ok = JS_FALSE;
1900 goto out;
1901 case JSTRAP_CONTINUE:
1902 break;
1903 case JSTRAP_RETURN:
1904 fp->rval = rval;
1905 goto out;
1906 #if JS_HAS_EXCEPTIONS
1907 case JSTRAP_THROW:
1908 cx->throwing = JS_TRUE;
1909 cx->exception = rval;
1910 ok = JS_FALSE;
1911 goto out;
1912 #endif /* JS_HAS_EXCEPTIONS */
1913 default:;
1914 }
1915 LOAD_INTERRUPT_HANDLER(rt);
1916 }
1918 switch (op) {
1919 case JSOP_NOP:
1920 break;
1922 case JSOP_GROUP:
1923 break;
1925 case JSOP_PUSH:
1926 PUSH_OPND(JSVAL_VOID);
1927 break;
1929 case JSOP_POP:
1930 sp--;
1931 break;
1933 case JSOP_POP2:
1934 sp -= 2;
1935 break;
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 ltmp = sp[-1];
1943 sp[-1] = sp[-2];
1944 sp[-2] = ltmp;
1945 break;
1947 case JSOP_POPV:
1948 *result = POP_OPND();
1949 break;
1951 case JSOP_ENTERWITH:
1952 FETCH_OBJECT(cx, -1, rval, obj);
1953 SAVE_SP(fp);
1954 withobj = js_NewWithObject(cx, obj, fp->scopeChain,
1955 sp - fp->spbase - 1);
1956 if (!withobj)
1957 goto out;
1958 rval = INT_TO_JSVAL(sp - fp->spbase);
1959 fp->scopeChain = withobj;
1960 STORE_OPND(-1, OBJECT_TO_JSVAL(withobj));
1961 break;
1963 case JSOP_LEAVEWITH:
1964 rval = POP_OPND();
1965 JS_ASSERT(JSVAL_IS_OBJECT(rval));
1966 withobj = JSVAL_TO_OBJECT(rval);
1967 JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1969 rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT);
1970 JS_ASSERT(JSVAL_IS_OBJECT(rval));
1971 fp->scopeChain = JSVAL_TO_OBJECT(rval);
1972 JS_SetPrivate(cx, withobj, NULL);
1973 break;
1975 case JSOP_SETRVAL:
1976 fp->rval = POP_OPND();
1977 break;
1979 case JSOP_RETURN:
1980 CHECK_BRANCH(-1);
1981 fp->rval = POP_OPND();
1982 /* FALL THROUGH */
1984 case JSOP_RETRVAL: /* fp->rval already set */
1985 if (inlineCallCount)
1986 inline_return:
1987 {
1988 JSInlineFrame *ifp = (JSInlineFrame *) fp;
1989 void *hookData = ifp->hookData;
1991 if (hookData) {
1992 JSInterpreterHook hook = cx->runtime->callHook;
1993 if (hook) {
1994 hook(cx, fp, JS_FALSE, &ok, hookData);
1995 LOAD_INTERRUPT_HANDLER(rt);
1996 }
1997 }
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 if (fp->callobj)
2007 ok &= js_PutCallObject(cx, fp);
2008 #endif
2009 #if JS_HAS_ARGS_OBJECT
2010 if (fp->argsobj)
2011 ok &= js_PutArgsObject(cx, fp);
2012 #endif
2014 /* Restore context version only if callee hasn't set version. */
2015 if (cx->version == currentVersion) {
2016 currentVersion = ifp->callerVersion;
2017 if (currentVersion != cx->version)
2018 js_SetVersion(cx, currentVersion);
2019 }
2021 /* Store the return value in the caller's operand frame. */
2022 vp = fp->argv - 2;
2023 *vp = fp->rval;
2025 /* Restore cx->fp and release the inline frame's space. */
2026 cx->fp = fp = fp->down;
2027 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
2029 /* Restore sp to point just above the return value. */
2030 fp->sp = vp + 1;
2031 RESTORE_SP(fp);
2033 /* Restore the calling script's interpreter registers. */
2034 script = fp->script;
2035 depth = (jsint) script->depth;
2036 pc = fp->pc;
2037 endpc = script->code + script->length;
2039 /* Store the generating pc for the return value. */
2040 vp[-depth] = (jsval)pc;
2042 /* Set remaining variables for 'goto advance_pc'. */
2043 op = (JSOp) *pc;
2044 cs = &js_CodeSpec[op];
2045 len = cs->length;
2047 /* Resume execution in the calling frame. */
2048 inlineCallCount--;
2049 if (ok)
2050 goto advance_pc;
2051 }
2052 goto out;
2054 #if JS_HAS_SWITCH_STATEMENT
2055 case JSOP_DEFAULT:
2056 (void) POP();
2057 /* FALL THROUGH */
2058 #endif
2059 case JSOP_GOTO:
2060 len = GET_JUMP_OFFSET(pc);
2061 CHECK_BRANCH(len);
2062 break;
2064 case JSOP_IFEQ:
2065 POP_BOOLEAN(cx, rval, cond);
2066 if (cond == JS_FALSE) {
2067 len = GET_JUMP_OFFSET(pc);
2068 CHECK_BRANCH(len);
2069 }
2070 break;
2072 case JSOP_IFNE:
2073 POP_BOOLEAN(cx, rval, cond);
2074 if (cond != JS_FALSE) {
2075 len = GET_JUMP_OFFSET(pc);
2076 CHECK_BRANCH(len);
2077 }
2078 break;
2080 case JSOP_OR:
2081 POP_BOOLEAN(cx, rval, cond);
2082 if (cond == JS_TRUE) {
2083 len = GET_JUMP_OFFSET(pc);
2084 PUSH_OPND(rval);
2085 }
2086 break;
2088 case JSOP_AND:
2089 POP_BOOLEAN(cx, rval, cond);
2090 if (cond == JS_FALSE) {
2091 len = GET_JUMP_OFFSET(pc);
2092 PUSH_OPND(rval);
2093 }
2094 break;
2097 #if JS_HAS_SWITCH_STATEMENT
2098 case JSOP_DEFAULTX:
2099 (void) POP();
2100 /* FALL THROUGH */
2101 #endif
2102 case JSOP_GOTOX:
2103 len = GET_JUMPX_OFFSET(pc);
2104 CHECK_BRANCH(len);
2105 break;
2107 case JSOP_IFEQX:
2108 POP_BOOLEAN(cx, rval, cond);
2109 if (cond == JS_FALSE) {
2110 len = GET_JUMPX_OFFSET(pc);
2111 CHECK_BRANCH(len);
2112 }
2113 break;
2115 case JSOP_IFNEX:
2116 POP_BOOLEAN(cx, rval, cond);
2117 if (cond != JS_FALSE) {
2118 len = GET_JUMPX_OFFSET(pc);
2119 CHECK_BRANCH(len);
2120 }
2121 break;
2123 case JSOP_ORX:
2124 POP_BOOLEAN(cx, rval, cond);
2125 if (cond == JS_TRUE) {
2126 len = GET_JUMPX_OFFSET(pc);
2127 PUSH_OPND(rval);
2128 }
2129 break;
2131 case JSOP_ANDX:
2132 POP_BOOLEAN(cx, rval, cond);
2133 if (cond == JS_FALSE) {
2134 len = GET_JUMPX_OFFSET(pc);
2135 PUSH_OPND(rval);
2136 }
2137 break;
2139 case JSOP_TOOBJECT:
2140 rval = FETCH_OPND(-1);
2141 if (!JSVAL_IS_PRIMITIVE(rval)) {
2142 obj = JSVAL_TO_OBJECT(rval);
2143 } else {
2144 SAVE_SP(fp);
2145 ok = js_ValueToObject(cx, rval, &obj);
2146 if (!ok)
2147 goto out;
2148 }
2149 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
2150 break;
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
2171 #if JS_HAS_IN_OPERATOR
2172 case JSOP_IN:
2173 SAVE_SP(fp);
2174 rval = FETCH_OPND(-1);
2175 if (JSVAL_IS_PRIMITIVE(rval)) {
2176 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
2177 if (str) {
2178 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2179 JSMSG_IN_NOT_OBJECT,
2180 JS_GetStringBytes(str));
2181 }
2182 ok = JS_FALSE;
2183 goto out;
2184 }
2185 obj = JSVAL_TO_OBJECT(rval);
2186 FETCH_ELEMENT_ID(-2, id);
2187 CHECK_ELEMENT_ID(obj, id);
2188 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
2189 if (!ok)
2190 goto out;
2191 sp--;
2192 STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
2193 if (prop)
2194 OBJ_DROP_PROPERTY(cx, obj2, prop);
2195 break;
2196 #endif /* JS_HAS_IN_OPERATOR */
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 lval = FETCH_OPND(-1);
2204 atom = GET_ATOM(cx, script, pc);
2205 id = ATOM_TO_JSID(atom);
2206 i = -2;
2207 goto do_forinloop;
2209 case JSOP_FORNAME:
2210 atom = GET_ATOM(cx, script, pc);
2211 id = ATOM_TO_JSID(atom);
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 SAVE_SP(fp);
2222 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2223 if (!ok)
2224 goto out;
2225 if (prop)
2226 OBJ_DROP_PROPERTY(cx, obj2, prop);
2227 lval = OBJECT_TO_JSVAL(obj);
2228 /* FALL THROUGH */
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 */
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 i = -1;
2248 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 obj = JSVAL_TO_OBJECT(sp[i]);
2255 /* If the thing to the right of 'in' has no properties, break. */
2256 if (!obj) {
2257 rval = JSVAL_FALSE;
2258 goto end_forinloop;
2259 }
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 origobj = obj;
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 vp = &sp[i - 1];
2275 rval = *vp;
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 SAVE_SP(fp);
2283 /* Is this the first iteration ? */
2284 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 propobj = js_NewObject(cx, &prop_iterator_class, NULL, NULL);
2292 if (!propobj) {
2293 ok = JS_FALSE;
2294 goto out;
2295 }
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 propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
2302 propobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL;
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 ok = js_AddRoot(cx, &propobj->slots[JSSLOT_PARENT],
2310 "propobj->parent");
2311 if (!ok)
2312 goto out;
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 *vp = OBJECT_TO_JSVAL(propobj);
2321 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 if (!ok)
2332 goto out;
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 propobj->slots[JSSLOT_ITER_STATE] = iter_state;
2342 } else {
2343 /* This is not the first iteration. Recover iterator state. */
2344 propobj = JSVAL_TO_OBJECT(rval);
2345 JS_ASSERT(OBJ_GET_CLASS(cx, propobj) == &prop_iterator_class);
2346 obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]);
2347 iter_state = propobj->slots[JSSLOT_ITER_STATE];
2348 }
2350 enum_next_property:
2351 {
2352 jsid fid;
2354 /* Get the next jsid to be enumerated and store it in fid. */
2355 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 propobj->slots[JSSLOT_ITER_STATE] = iter_state;
2366 /* No more jsids to iterate in obj? */
2367 if (iter_state == JSVAL_NULL) {
2368 /* Enumerate the properties on obj's prototype chain. */
2369 obj = OBJ_GET_PROTO(cx, obj);
2370 if (!obj) {
2371 /* End of property list -- terminate loop. */
2372 rval = JSVAL_FALSE;
2373 #if JS_HAS_XML_SUPPORT
2374 foreach = JS_FALSE;
2375 #endif
2376 goto end_forinloop;
2377 }
2379 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);
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 propobj->slots[JSSLOT_ITER_STATE] = iter_state;
2397 if (!ok)
2398 goto out;
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 propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
2406 goto enum_next_property;
2407 }
2409 /* Skip properties not owned by obj when looking from origobj. */
2410 ok = OBJ_LOOKUP_PROPERTY(cx, origobj, fid, &obj2, &prop);
2411 if (!ok)
2412 goto out;
2413 if (prop)
2414 OBJ_DROP_PROPERTY(cx, obj2, prop);
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 if (!prop)
2424 goto enum_next_property;
2425 if (obj != obj2) {
2426 cond = JS_FALSE;
2427 clasp = OBJ_GET_CLASS(cx, obj2);
2428 if (clasp->flags & JSCLASS_IS_EXTENDED) {
2429 JSExtendedClass *xclasp;
2431 xclasp = (JSExtendedClass *) clasp;
2432 cond = xclasp->outerObject &&
2433 xclasp->outerObject(cx, obj2) == obj;
2434 }
2435 if (!cond)
2436 goto enum_next_property;
2437 }
2439 #if JS_HAS_XML_SUPPORT
2440 if (foreach) {
2441 /* Clear the local foreach flag set by our prefix bytecode. */
2442 foreach = JS_FALSE;
2444 /* If obj is not XML, we must get rval given its fid. */
2445 if (!OBJECT_IS_XML(cx, obj)) {
2446 ok = OBJ_GET_PROPERTY(cx, origobj, fid, &rval);
2447 if (!ok)
2448 goto out;
2449 }
2450 } else
2451 #endif
2452 {
2453 /* Make rval a string for uniformity and compatibility. */
2454 if (JSID_IS_ATOM(fid)) {
2455 rval = ATOM_KEY(JSID_TO_ATOM(fid));
2456 }
2457 #if JS_HAS_XML_SUPPORT
2458 else if (JSID_IS_OBJECT(fid)) {
2459 str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(fid));
2460 if (!str) {
2461 ok = JS_FALSE;
2462 goto out;
2463 }
2465 rval = STRING_TO_JSVAL(str);
2466 }
2467 #endif
2468 else if (!JS_VERSION_IS_1_2(cx)) {
2469 str = js_NumberToString(cx, (jsdouble) JSID_TO_INT(fid));
2470 if (!str) {
2471 ok = JS_FALSE;
2472 goto out;
2473 }
2475 rval = STRING_TO_JSVAL(str);
2476 } else {
2477 rval = INT_JSID_TO_JSVAL(fid);
2478 }
2479 }
2481 switch (op) {
2482 case JSOP_FORARG:
2483 slot = GET_ARGNO(pc);
2484 JS_ASSERT(slot < fp->fun->nargs);
2485 fp->argv[slot] = rval;
2486 break;
2488 case JSOP_FORVAR:
2489 slot = GET_VARNO(pc);
2490 JS_ASSERT(slot < fp->fun->nvars);
2491 fp->vars[slot] = rval;
2492 break;
2494 case JSOP_FORELEM:
2495 /* FORELEM is not a SET operation, it's more like BINDNAME. */
2496 PUSH_OPND(rval);
2497 break;
2499 default:
2500 /* Convert lval to a non-null object containing id. */
2501 VALUE_TO_OBJECT(cx, lval, obj);
2502 if (i + 1 < 0)
2503 STORE_OPND(i + 1, OBJECT_TO_JSVAL(obj));
2505 /* Set the variable obj[id] to refer to rval. */
2506 fp->flags |= JSFRAME_ASSIGNING;
2507 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
2508 fp->flags &= ~JSFRAME_ASSIGNING;
2509 if (!ok)
2510 goto out;
2511 break;
2512 }
2514 /* Push true to keep looping through properties. */
2515 rval = JSVAL_TRUE;
2517 end_forinloop:
2518 sp += i + 1;
2519 PUSH_OPND(rval);
2520 break;
2521 }
2523 case JSOP_DUP:
2524 JS_ASSERT(sp > fp->spbase);
2525 rval = sp[-1];
2526 PUSH_OPND(rval);
2527 break;
2529 case JSOP_DUP2:
2530 JS_ASSERT(sp - 1 > fp->spbase);
2531 lval = FETCH_OPND(-2);
2532 rval = FETCH_OPND(-1);
2533 PUSH_OPND(lval);
2534 PUSH_OPND(rval);
2535 break;
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
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
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)
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
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
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);
2635 #define END_LITOPX_CASE \
2636 break; \
2638 BEGIN_LITOPX_CASE(JSOP_SETCONST, 0)
2639 obj = fp->varobj;
2640 rval = FETCH_OPND(-1);
2641 SAVE_SP(fp);
2642 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 if (!ok)
2648 goto out;
2649 STORE_OPND(-1, rval);
2650 END_LITOPX_CASE
2652 BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0)
2653 SAVE_SP(fp);
2654 obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom));
2655 if (!obj) {
2656 ok = JS_FALSE;
2657 goto out;
2658 }
2659 PUSH_OPND(OBJECT_TO_JSVAL(obj));
2660 END_LITOPX_CASE
2662 case JSOP_SETNAME:
2663 atom = GET_ATOM(cx, script, pc);
2664 id = ATOM_TO_JSID(atom);
2665 rval = FETCH_OPND(-1);
2666 lval = FETCH_OPND(-2);
2667 JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
2668 obj = JSVAL_TO_OBJECT(lval);
2669 SAVE_SP(fp);
2670 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
2671 if (!ok)
2672 goto out;
2673 sp--;
2674 STORE_OPND(-1, rval);
2675 obj = NULL;
2676 break;
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
2690 #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;)
2691 #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;)
2693 case JSOP_BITOR:
2694 BITWISE_OP(|);
2695 break;
2697 case JSOP_BITXOR:
2698 BITWISE_OP(^);
2699 break;
2701 case JSOP_BITAND:
2702 BITWISE_OP(&);
2703 break;
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
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
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
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
2832 case JSOP_EQ:
2833 EQUALITY_OP(==, JS_FALSE);
2834 break;
2836 case JSOP_NE:
2837 EQUALITY_OP(!=, JS_TRUE);
2838 break;
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
2850 case JSOP_NEW_EQ:
2851 NEW_EQUALITY_OP(==);
2852 break;
2854 case JSOP_NEW_NE:
2855 NEW_EQUALITY_OP(!=);
2856 break;
2858 #if JS_HAS_SWITCH_STATEMENT
2859 case JSOP_CASE:
2860 NEW_EQUALITY_OP(==);
2861 (void) POP();
2862 if (cond) {
2863 len = GET_JUMP_OFFSET(pc);
2864 CHECK_BRANCH(len);
2865 } else {
2866 PUSH(lval);
2867 }
2868 break;
2870 case JSOP_CASEX:
2871 NEW_EQUALITY_OP(==);
2872 (void) POP();
2873 if (cond) {
2874 len = GET_JUMPX_OFFSET(pc);
2875 CHECK_BRANCH(len);
2876 } else {
2877 PUSH(lval);
2878 }
2879 break;
2880 #endif
2882 #endif /* !JS_BUG_FALLIBLE_EQOPS */
2884 case JSOP_LT:
2885 RELATIONAL_OP(<);
2886 break;
2888 case JSOP_LE:
2889 RELATIONAL_OP(<=);
2890 break;
2892 case JSOP_GT:
2893 RELATIONAL_OP(>);
2894 break;
2896 case JSOP_GE:
2897 RELATIONAL_OP(>=);
2898 break;
2900 #undef EQUALITY_OP
2901 #undef RELATIONAL_OP
2903 case JSOP_LSH:
2904 SIGNED_SHIFT_OP(<<);
2905 break;
2907 case JSOP_RSH:
2908 SIGNED_SHIFT_OP(>>);
2909 break;
2911 case JSOP_URSH:
2912 {
2913 uint32 u;
2915 FETCH_INT(cx, -1, j);
2916 FETCH_UINT(cx, -2, u);
2917 j &= 31;
2918 d = u >> j;
2919 sp--;
2920 STORE_NUMBER(cx, -1, d);
2921 break;
2922 }
2924 #undef INTEGER_OP
2925 #undef BITWISE_OP
2926 #undef SIGNED_SHIFT_OP
2928 case JSOP_ADD:
2929 rval = FETCH_OPND(-1);
2930 lval = FETCH_OPND(-2);
2931 #if JS_HAS_XML_SUPPORT
2932 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;
2937 ops = (JSXMLObjectOps *) obj2->map->ops;
2938 SAVE_SP(fp);
2939 ok = ops->concatenate(cx, obj2, rval, &rval);
2940 if (!ok)
2941 goto out;
2942 sp--;
2943 STORE_OPND(-1, rval);
2944 break;
2945 }
2946 #endif
2947 {
2948 VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &sp[-2]);
2949 lval = sp[-2];
2950 VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &sp[-1]);
2951 rval = sp[-1];
2952 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
2953 SAVE_SP(fp);
2954 if (cond) {
2955 str = JSVAL_TO_STRING(lval);
2956 ok = (str2 = js_ValueToString(cx, rval)) != NULL;
2957 } else {
2958 str2 = JSVAL_TO_STRING(rval);
2959 ok = (str = js_ValueToString(cx, lval)) != NULL;
2960 }
2961 if (!ok)
2962 goto out;
2963 str = js_ConcatStrings(cx, str, str2);
2964 if (!str) {
2965 ok = JS_FALSE;
2966 goto out;
2967 }
2968 sp--;
2969 STORE_OPND(-1, STRING_TO_JSVAL(str));
2970 } else {
2971 VALUE_TO_NUMBER(cx, lval, d);
2972 VALUE_TO_NUMBER(cx, rval, d2);
2973 d += d2;
2974 sp--;
2975 STORE_NUMBER(cx, -1, d);
2976 }
2977 }
2978 break;
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
2989 case JSOP_SUB:
2990 BINARY_OP(-);
2991 break;
2993 case JSOP_MUL:
2994 BINARY_OP(*);
2995 break;
2997 case JSOP_DIV:
2998 FETCH_NUMBER(cx, -1, d2);
2999 FETCH_NUMBER(cx, -2, d);
3000 sp--;
3001 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 if (d == 0 || JSDOUBLE_IS_NaN(d))
3009 rval = DOUBLE_TO_JSVAL(rt->jsNaN);
3010 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
3011 rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
3012 else
3013 rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
3014 STORE_OPND(-1, rval);
3015 } else {
3016 d /= d2;
3017 STORE_NUMBER(cx, -1, d);
3018 }
3019 break;
3021 case JSOP_MOD:
3022 FETCH_NUMBER(cx, -1, d2);
3023 FETCH_NUMBER(cx, -2, d);
3024 sp--;
3025 if (d2 == 0) {
3026 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 d = fmod(d, d2);
3033 STORE_NUMBER(cx, -1, d);
3034 }
3035 break;
3037 case JSOP_NOT:
3038 POP_BOOLEAN(cx, rval, cond);
3039 PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
3040 break;
3042 case JSOP_BITNOT:
3043 FETCH_INT(cx, -1, i);
3044 d = (jsdouble) ~i;
3045 STORE_NUMBER(cx, -1, d);
3046 break;
3048 case JSOP_NEG:
3049 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 d = -d;
3059 #endif
3060 STORE_NUMBER(cx, -1, d);
3061 break;
3063 case JSOP_POS:
3064 FETCH_NUMBER(cx, -1, d);
3065 STORE_NUMBER(cx, -1, d);
3066 break;
3068 case JSOP_NEW:
3069 /* Get immediate argc and find the constructor function. */
3070 argc = GET_ARGC(pc);
3072 #if JS_HAS_INITIALIZERS
3073 do_new:
3074 #endif
3075 SAVE_SP(fp);
3076 vp = sp - (2 + argc);
3077 JS_ASSERT(vp >= fp->spbase);
3079 fun = NULL;
3080 obj2 = NULL;
3081 lval = *vp;
3082 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 fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
3089 if (!fun) {
3090 ok = JS_FALSE;
3091 goto out;
3092 }
3093 }
3095 clasp = &js_ObjectClass;
3096 if (!obj2) {
3097 proto = parent = NULL;
3098 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 ok = OBJ_GET_PROPERTY(cx, obj2,
3107 ATOM_TO_JSID(rt->atomState
3108 .classPrototypeAtom),
3109 &vp[1]);
3110 if (!ok)
3111 goto out;
3112 rval = vp[1];
3113 proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
3114 parent = OBJ_GET_PARENT(cx, obj2);
3116 if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
3117 funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp;
3118 if (funclasp)
3119 clasp = funclasp;
3120 }
3121 }
3122 obj = js_NewObject(cx, clasp, proto, parent);
3123 if (!obj) {
3124 ok = JS_FALSE;
3125 goto out;
3126 }
3128 /* Now we have an object with a constructor method; call it. */
3129 vp[1] = OBJECT_TO_JSVAL(obj);
3130 ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT);
3131 RESTORE_SP(fp);
3132 LOAD_BRANCH_CALLBACK(cx);
3133 LOAD_INTERRUPT_HANDLER(rt);
3134 if (!ok) {
3135 cx->newborn[GCX_OBJECT] = NULL;
3136 goto out;
3137 }
3139 /* Check the return value and update obj from it. */
3140 rval = *vp;
3141 if (JSVAL_IS_PRIMITIVE(rval)) {
3142 if (fun || !JS_VERSION_IS_ECMA(cx)) {
3143 *vp = OBJECT_TO_JSVAL(obj);
3144 break;
3145 }
3146 /* native [[Construct]] returning primitive is error */
3147 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3148 JSMSG_BAD_NEW_RESULT,
3149 js_ValueToPrintableString(cx, rval));
3150 ok = JS_FALSE;
3151 goto out;
3152 }
3153 obj = JSVAL_TO_OBJECT(rval);
3154 JS_RUNTIME_METER(rt, constructs);
3155 break;
3157 case JSOP_DELNAME:
3158 atom = GET_ATOM(cx, script, pc);
3159 id = ATOM_TO_JSID(atom);
3161 SAVE_SP(fp);
3162 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3163 if (!ok)
3164 goto out;
3166 /* ECMA says to return true if name is undefined or inherited. */
3167 rval = JSVAL_TRUE;
3168 if (prop) {
3169 OBJ_DROP_PROPERTY(cx, obj2, prop);
3170 ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval);
3171 if (!ok)
3172 goto out;
3173 }
3174 PUSH_OPND(rval);
3175 break;
3177 case JSOP_DELPROP:
3178 atom = GET_ATOM(cx, script, pc);
3179 id = ATOM_TO_JSID(atom);
3180 PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3181 STORE_OPND(-1, rval);
3182 break;
3184 case JSOP_DELELEM:
3185 ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3186 sp--;
3187 STORE_OPND(-1, rval);
3188 break;
3190 case JSOP_TYPEOF:
3191 rval = FETCH_OPND(-1);
3192 SAVE_SP(fp);
3193 type = JS_TypeOfValue(cx, rval);
3194 atom = rt->atomState.typeAtoms[type];
3195 STORE_OPND(-1, ATOM_KEY(atom));
3196 break;
3198 case JSOP_VOID:
3199 (void) POP_OPND();
3200 PUSH_OPND(JSVAL_VOID);
3201 break;
3203 case JSOP_INCNAME:
3204 case JSOP_DECNAME:
3205 case JSOP_NAMEINC:
3206 case JSOP_NAMEDEC:
3207 atom = GET_ATOM(cx, script, pc);
3208 id = ATOM_TO_JSID(atom);
3210 SAVE_SP(fp);
3211 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3212 if (!ok)
3213 goto out;
3214 if (!prop)
3215 goto atom_not_defined;
3217 OBJ_DROP_PROPERTY(cx, obj2, prop);
3218 lval = OBJECT_TO_JSVAL(obj);
3219 i = 0;
3220 goto do_incop;
3222 case JSOP_INCPROP:
3223 case JSOP_DECPROP:
3224 case JSOP_PROPINC:
3225 case JSOP_PROPDEC:
3226 atom = GET_ATOM(cx, script, pc);
3227 id = ATOM_TO_JSID(atom);
3228 lval = FETCH_OPND(-1);
3229 i = -1;
3230 goto do_incop;
3232 case JSOP_INCELEM:
3233 case JSOP_DECELEM:
3234 case JSOP_ELEMINC:
3235 case JSOP_ELEMDEC:
3236 FETCH_ELEMENT_ID(-1, id);
3237 lval = FETCH_OPND(-2);
3238 i = -2;
3240 do_incop:
3241 VALUE_TO_OBJECT(cx, lval, obj);
3242 if (i < 0)
3243 STORE_OPND(i, OBJECT_TO_JSVAL(obj));
3244 CHECK_ELEMENT_ID(obj, id);
3246 /* The operand must contain a number. */
3247 SAVE_SP(fp);
3248 CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
3249 if (!ok)
3250 goto out;
3252 /* The expression result goes in rtmp, the updated value in rval. */
3253 if (JSVAL_IS_INT(rval) &&
3254 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
3255 rval != INT_TO_JSVAL(JSVAL_INT_MAX)) {
3256 if (cs->format & JOF_POST) {
3257 rtmp = rval;
3258 (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3259 } else {
3260 (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3261 rtmp = rval;
3262 }
3263 } else {
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
3294 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 vp = sp;
3301 PUSH(JSVAL_VOID);
3302 SAVE_SP(fp);
3303 --i;
3304 }
3305 #ifdef __GNUC__
3306 else vp = NULL; /* suppress bogus gcc warnings */
3307 #endif
3309 NONINT_INCREMENT_OP_MIDDLE();
3310 }
3312 fp->flags |= JSFRAME_ASSIGNING;
3313 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
3314 fp->flags &= ~JSFRAME_ASSIGNING;
3315 if (!ok)
3316 goto out;
3317 sp += i;
3318 PUSH_OPND(rtmp);
3319 break;
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;
3341 case JSOP_INCARG:
3342 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX);
3343 case JSOP_DECARG:
3344 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN);
3345 case JSOP_ARGINC:
3346 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX);
3347 case JSOP_ARGDEC:
3348 FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN);
3350 case JSOP_INCVAR:
3351 FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX);
3352 case JSOP_DECVAR:
3353 FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN);
3354 case JSOP_VARINC:
3355 FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX);
3356 case JSOP_VARDEC:
3357 FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN);
3359 #undef FAST_INCREMENT_OP
3361 do_nonint_fast_incop:
3362 NONINT_INCREMENT_OP_MIDDLE();
3363 *vp = rval;
3364 PUSH_OPND(rtmp);
3365 break;
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;
3388 case JSOP_INCGVAR:
3389 FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX);
3390 case JSOP_DECGVAR:
3391 FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN);
3392 case JSOP_GVARINC:
3393 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX);
3394 case JSOP_GVARDEC:
3395 FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN);
3397 #undef FAST_GLOBAL_INCREMENT_OP
3399 do_nonint_fast_global_incop:
3400 vp = sp++;
3401 SAVE_SP(fp);
3402 NONINT_INCREMENT_OP_MIDDLE();
3403 OBJ_SET_SLOT(cx, obj, slot, rval);
3404 STORE_OPND(-1, rtmp);
3405 break;
3407 case JSOP_GETPROP:
3408 /* Get an immediate atom naming the property. */
3409 atom = GET_ATOM(cx, script, pc);
3410 id = ATOM_TO_JSID(atom);
3411 PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
3412 STORE_OPND(-1, rval);
3413 break;
3415 case JSOP_SETPROP:
3416 /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */
3417 rval = FETCH_OPND(-1);
3419 /* Get an immediate atom naming the property. */
3420 atom = GET_ATOM(cx, script, pc);
3421 id = ATOM_TO_JSID(atom);
3422 PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
3423 sp--;
3424 STORE_OPND(-1, rval);
3425 obj = NULL;
3426 break;
3428 case JSOP_GETELEM:
3429 ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
3430 sp--;
3431 STORE_OPND(-1, rval);
3432 break;
3434 case JSOP_SETELEM:
3435 rval = FETCH_OPND(-1);
3436 ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
3437 sp -= 2;
3438 STORE_OPND(-1, rval);
3439 obj = NULL;
3440 break;
3442 case JSOP_ENUMELEM:
3443 /* Funky: the value to set is under the [obj, id] pair. */
3444 FETCH_ELEMENT_ID(-1, id);
3445 FETCH_OBJECT(cx, -2, lval, obj);
3446 CHECK_ELEMENT_ID(obj, id);
3447 rval = FETCH_OPND(-3);
3448 SAVE_SP(fp);
3449 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3450 if (!ok)
3451 goto out;
3452 sp -= 3;
3453 break;
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)
3465 case JSOP_PUSHOBJ:
3466 if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) {
3467 ok = JS_FALSE;
3468 goto out;
3469 }
3470 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3471 break;
3473 case JSOP_CALL:
3474 case JSOP_EVAL:
3475 argc = GET_ARGC(pc);
3476 vp = sp - (argc + 2);
3477 lval = *vp;
3478 SAVE_SP(fp);
3480 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;
3493 /* Restrict recursion of lightweight functions. */
3494 if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
3495 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3496 JSMSG_OVER_RECURSED);
3497 ok = JS_FALSE;
3498 goto out;
3499 }
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
3508 /* Compute the number of stack slots needed for fun. */
3509 nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1)
3510 / sizeof(jsval);
3511 nvars = fun->nvars;
3512 script = fun->u.script;
3513 depth = (jsint) script->depth;
3515 /* Allocate the frame and space for vars and operands. */
3516 newsp = js_AllocRawStack(cx, nframeslots + nvars + 2 * depth,
3517 &newmark);
3518 if (!newsp) {
3519 ok = JS_FALSE;
3520 goto bad_inline_call;
3521 }
3522 newifp = (JSInlineFrame *) newsp;
3523 newsp += nframeslots;
3525 /* Initialize the stack frame. */
3526 memset(newifp, 0, sizeof(JSInlineFrame));
3527 newifp->frame.script = script;
3528 newifp->frame.fun = fun;
3529 newifp->frame.argc = argc;
3530 newifp->frame.argv = vp + 2;
3531 newifp->frame.rval = JSVAL_VOID;
3532 newifp->frame.nvars = nvars;
3533 newifp->frame.vars = newsp;
3534 newifp->frame.down = fp;
3535 newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj);
3536 newifp->mark = newmark;
3538 /* Compute the 'this' parameter now that argv is set. */
3539 ok = js_ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame);
3540 if (!ok) {
3541 js_FreeRawStack(cx, newmark);
3542 goto bad_inline_call;
3543 }
3544 #ifdef DUMP_CALL_TABLE
3545 LogCall(cx, *vp, argc, vp + 2);
3546 #endif
3548 /* Push void to initialize local variables. */
3549 sp = newsp;
3550 while (nvars--)
3551 PUSH(JSVAL_VOID);
3552 sp += depth;
3553 newifp->frame.spbase = sp;
3554 SAVE_SP(&newifp->frame);
3556 /* Call the debugger hook if present. */
3557 hook = cx->runtime->callHook;
3558 if (hook) {
3559 newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
3560 cx->runtime->callHookData);
3561 LOAD_INTERRUPT_HANDLER(rt);
3562 }
3564 /* Switch to new version if currentVersion wasn't overridden. */
3565 newifp->callerVersion = cx->version;
3566 if (cx->version == currentVersion) {
3567 currentVersion = script->version;
3568 if (currentVersion != cx->version)
3569 js_SetVersion(cx, currentVersion);
3570 }
3572 /* Push the frame and set interpreter registers. */
3573 cx->fp = fp = &newifp->frame;
3574 pc = script->code;
3575 endpc = pc + script->length;
3576 inlineCallCount++;
3577 JS_RUNTIME_METER(rt, inlineCalls);
3578 continue;
3580 bad_inline_call:
3581 script = fp->script;
3582 depth = (jsint) script->depth;
3583 goto out;
3584 }
3586 ok = js_Invoke(cx, argc, 0);
3587 RESTORE_SP(fp);
3588 LOAD_BRANCH_CALLBACK(cx);
3589 LOAD_INTERRUPT_HANDLER(rt);
3590 if (!ok)
3591 goto out;
3592 JS_RUNTIME_METER(rt, nonInlineCalls);
3593 #if JS_HAS_LVALUE_RETURN
3594 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 PUSH_OPND(cx->rval2);
3610 cx->rval2set = JS_FALSE;
3611 ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval));
3612 sp--;
3613 STORE_OPND(-1, rval);
3614 }
3615 #endif
3616 obj = NULL;
3617 break;
3619 #if JS_HAS_LVALUE_RETURN
3620 case JSOP_SETCALL:
3621 argc = GET_ARGC(pc);
3622 SAVE_SP(fp);
3623 ok = js_Invoke(cx, argc, 0);
3624 RESTORE_SP(fp);
3625 LOAD_BRANCH_CALLBACK(cx);
3626 LOAD_INTERRUPT_HANDLER(rt);
3627 if (!ok)
3628 goto out;
3629 if (!cx->rval2set) {
3630 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3631 JSMSG_BAD_LEFTSIDE_OF_ASS);
3632 ok = JS_FALSE;
3633 goto out;
3634 }
3635 PUSH_OPND(cx->rval2);
3636 cx->rval2set = JS_FALSE;
3637 obj = NULL;
3638 break;
3639 #endif
3641 case JSOP_NAME:
3642 atom = GET_ATOM(cx, script, pc);
3643 id = ATOM_TO_JSID(atom);
3645 SAVE_SP(fp);
3646 ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3647 if (!ok)
3648 goto out;
3649 if (!prop) {
3650 /* Kludge to allow (typeof foo == "undefined") tests. */
3651 for (pc2 = pc + len; pc2 < endpc; pc2++) {
3652 op2 = (JSOp)*pc2;
3653 if (op2 == JSOP_TYPEOF) {
3654 PUSH_OPND(JSVAL_VOID);
3655 goto advance_pc;
3656 }
3657 if (op2 != JSOP_GROUP)
3658 break;
3659 }
3660 goto atom_not_defined;
3661 }
3663 /* Take the slow path if prop was not found in a native object. */
3664 if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
3665 OBJ_DROP_PROPERTY(cx, obj2, prop);
3666 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
3667 if (!ok)
3668 goto out;
3669 PUSH_OPND(rval);
3670 break;
3671 }
3673 /* Get and push the obj[id] property's value. */
3674 sprop = (JSScopeProperty *)prop;
3675 slot = (uintN)sprop->slot;
3676 rval = (slot != SPROP_INVALID_SLOT)
3677 ? LOCKED_OBJ_GET_SLOT(obj2, slot)
3678 : JSVAL_VOID;
3679 JS_UNLOCK_OBJ(cx, obj2);
3680 ok = SPROP_GET(cx, sprop, obj, obj2, &rval);
3681 JS_LOCK_OBJ(cx, obj2);
3682 if (!ok) {
3683 OBJ_DROP_PROPERTY(cx, obj2, prop);
3684 goto out;
3685 }
3686 if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)))
3687 LOCKED_OBJ_SET_SLOT(obj2, slot, rval);
3688 OBJ_DROP_PROPERTY(cx, obj2, prop);
3689 PUSH_OPND(rval);
3690 break;
3692 case JSOP_UINT16:
3693 i = (jsint) GET_ATOM_INDEX(pc);
3694 rval = INT_TO_JSVAL(i);
3695 PUSH_OPND(rval);
3696 obj = NULL;
3697 break;
3699 case JSOP_UINT24:
3700 i = (jsint) GET_LITERAL_INDEX(pc);
3701 rval = INT_TO_JSVAL(i);
3702 PUSH_OPND(rval);
3703 break;
3705 case JSOP_LITERAL:
3706 atomIndex = GET_LITERAL_INDEX(pc);
3707 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
3708 PUSH_OPND(ATOM_KEY(atom));
3709 obj = NULL;
3710 break;
3712 case JSOP_FINDNAME:
3713 atomIndex = GET_LITERAL_INDEX(pc);
3714 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
3715 SAVE_SP(fp);
3716 obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom));
3717 if (!obj) {
3718 ok = JS_FALSE;
3719 goto out;
3720 }
3721 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3722 PUSH_OPND(ATOM_KEY(atom));
3723 break;
3725 case JSOP_LITOPX:
3726 atomIndex = GET_LITERAL_INDEX(pc);
3727 op = pc[1 + LITERAL_INDEX_LEN];
3728 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;
3765 case JSOP_NUMBER:
3766 case JSOP_STRING:
3767 case JSOP_OBJECT:
3768 atomIndex = GET_ATOM_INDEX(pc);
3770 do_JSOP_NUMBER:
3771 do_JSOP_STRING:
3772 do_JSOP_OBJECT:
3773 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
3774 PUSH_OPND(ATOM_KEY(atom));
3775 obj = NULL;
3776 break;
3778 BEGIN_LITOPX_CASE(JSOP_REGEXP, 0)
3779 {
3780 JSRegExp *re;
3781 JSObject *funobj;
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 obj = ATOM_TO_OBJECT(atom);
3809 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
3811 re = (JSRegExp *) JS_GetPrivate(cx, obj);
3812 slot = re->cloneIndex;
3813 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 funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
3821 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
3822 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
3823 return JS_FALSE;
3824 if (JSVAL_IS_VOID(rval))
3825 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 rval = fp->vars[slot];
3834 #ifdef __GNUC__
3835 funobj = NULL; /* suppress bogus gcc warnings */
3836 #endif
3837 }
3839 if (JSVAL_IS_NULL(rval)) {
3840 /* Compute the current global object in obj2. */
3841 obj2 = fp->scopeChain;
3842 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
3843 obj2 = parent;
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 SAVE_SP(fp);
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 if (OBJ_GET_PARENT(cx, obj) != obj2) {
3879 obj = js_CloneRegExpObject(cx, obj, obj2);
3880 if (!obj) {
3881 ok = JS_FALSE;
3882 goto out;
3883 }
3884 }
3885 rval = OBJECT_TO_JSVAL(obj);
3887 /* Store the regexp object value in its cloneIndex slot. */
3888 if (fp->fun) {
3889 if (!JS_SetReservedSlot(cx, funobj, slot, rval))
3890 return JS_FALSE;
3891 } else {
3892 fp->vars[slot] = rval;
3893 }
3894 }
3896 PUSH_OPND(rval);
3897 obj = NULL;
3898 }
3899 END_LITOPX_CASE
3901 case JSOP_ZERO:
3902 PUSH_OPND(JSVAL_ZERO);
3903 obj = NULL;
3904 break;
3906 case JSOP_ONE:
3907 PUSH_OPND(JSVAL_ONE);
3908 obj = NULL;
3909 break;
3911 case JSOP_NULL:
3912 PUSH_OPND(JSVAL_NULL);
3913 obj = NULL;
3914 break;
3916 case JSOP_THIS:
3917 obj = fp->thisp;
3918 clasp = OBJ_GET_CLASS(cx, obj);
3919 if (clasp->flags & JSCLASS_IS_EXTENDED) {
3920 JSExtendedClass *xclasp;
3922 xclasp = (JSExtendedClass *) clasp;
3923 if (xclasp->outerObject) {
3924 obj = xclasp->outerObject(cx, obj);
3925 if (!obj) {
3926 ok = JS_FALSE;
3927 goto out;
3928 }
3929 }
3930 }
3932 PUSH_OPND(OBJECT_TO_JSVAL(obj));
3933 obj = NULL;
3934 break;
3936 case JSOP_FALSE:
3937 PUSH_OPND(JSVAL_FALSE);
3938 obj = NULL;
3939 break;
3941 case JSOP_TRUE:
3942 PUSH_OPND(JSVAL_TRUE);
3943 obj = NULL;
3944 break;
3946 #if JS_HAS_SWITCH_STATEMENT
3947 case JSOP_TABLESWITCH:
3948 pc2 = pc;
3949 len = GET_JUMP_OFFSET(pc2);
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 if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT ||
3957 (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) {
3958 rval = POP_OPND();
3959 if (!JSVAL_IS_INT(rval))
3960 break;
3961 i = JSVAL_TO_INT(rval);
3962 } else {
3963 FETCH_INT(cx, -1, i);
3964 sp--;
3965 }
3967 pc2 += JUMP_OFFSET_LEN;
3968 low = GET_JUMP_OFFSET(pc2);
3969 pc2 += JUMP_OFFSET_LEN;
3970 high = GET_JUMP_OFFSET(pc2);
3972 i -= low;
3973 if ((jsuint)i < (jsuint)(high - low + 1)) {
3974 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
3975 off = (jsint) GET_JUMP_OFFSET(pc2);
3976 if (off)
3977 len = off;
3978 }
3979 break;
3981 case JSOP_LOOKUPSWITCH:
3982 lval = POP_OPND();
3983 pc2 = pc;
3984 len = GET_JUMP_OFFSET(pc2);
3986 if (!JSVAL_IS_NUMBER(lval) &&
3987 !JSVAL_IS_STRING(lval) &&
3988 !JSVAL_IS_BOOLEAN(lval)) {
3989 goto advance_pc;
3990 }
3992 pc2 += JUMP_OFFSET_LEN;
3993 npairs = (jsint) GET_ATOM_INDEX(pc2);
3994 pc2 += ATOM_INDEX_LEN;
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 if (JSVAL_IS_STRING(lval)) {
4010 str = JSVAL_TO_STRING(lval);
4011 SEARCH_PAIRS(
4012 match = (JSVAL_IS_STRING(rval) &&
4013 ((str2 = JSVAL_TO_STRING(rval)) == str ||
4014 !js_CompareStrings(str2, str)));
4015 )
4016 } else if (JSVAL_IS_DOUBLE(lval)) {
4017 d = *JSVAL_TO_DOUBLE(lval);
4018 SEARCH_PAIRS(
4019 match = (JSVAL_IS_DOUBLE(rval) &&
4020 *JSVAL_TO_DOUBLE(rval) == d);
4021 )
4022 } else {
4023 SEARCH_PAIRS(
4024 match = (lval == rval);
4025 )
4026 }
4027 #undef SEARCH_PAIRS
4028 break;
4030 case JSOP_TABLESWITCHX:
4031 pc2 = pc;
4032 len = GET_JUMPX_OFFSET(pc2);
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 if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT ||
4040 (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) {
4041 rval = POP_OPND();
4042 if (!JSVAL_IS_INT(rval))
4043 break;
4044 i = JSVAL_TO_INT(rval);
4045 } else {
4046 FETCH_INT(cx, -1, i);
4047 sp--;
4048 }
4050 pc2 += JUMPX_OFFSET_LEN;
4051 low = GET_JUMP_OFFSET(pc2);
4052 pc2 += JUMP_OFFSET_LEN;
4053 high = GET_JUMP_OFFSET(pc2);
4055 i -= low;
4056 if ((jsuint)i < (jsuint)(high - low + 1)) {
4057 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
4058 off = (jsint) GET_JUMPX_OFFSET(pc2);
4059 if (off)
4060 len = off;
4061 }
4062 break;
4064 case JSOP_LOOKUPSWITCHX:
4065 lval = POP_OPND();
4066 pc2 = pc;
4067 len = GET_JUMPX_OFFSET(pc2);
4069 if (!JSVAL_IS_NUMBER(lval) &&
4070 !JSVAL_IS_STRING(lval) &&
4071 !JSVAL_IS_BOOLEAN(lval)) {
4072 goto advance_pc;
4073 }
4075 pc2 += JUMPX_OFFSET_LEN;
4076 npairs = (jsint) GET_ATOM_INDEX(pc2);
4077 pc2 += ATOM_INDEX_LEN;
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 if (JSVAL_IS_STRING(lval)) {
4093 str = JSVAL_TO_STRING(lval);
4094 SEARCH_EXTENDED_PAIRS(
4095 match = (JSVAL_IS_STRING(rval) &&
4096 ((str2 = JSVAL_TO_STRING(rval)) == str ||
4097 !js_CompareStrings(str2, str)));
4098 )
4099 } else if (JSVAL_IS_DOUBLE(lval)) {
4100 d = *JSVAL_TO_DOUBLE(lval);
4101 SEARCH_EXTENDED_PAIRS(
4102 match = (JSVAL_IS_DOUBLE(rval) &&
4103 *JSVAL_TO_DOUBLE(rval) == d);
4104 )
4105 } else {
4106 SEARCH_EXTENDED_PAIRS(
4107 match = (lval == rval);
4108 )
4109 }
4110 #undef SEARCH_EXTENDED_PAIRS
4111 break;
4113 case JSOP_CONDSWITCH:
4114 break;
4116 #endif /* JS_HAS_SWITCH_STATEMENT */
4118 #if JS_HAS_EXPORT_IMPORT
4119 case JSOP_EXPORTALL:
4120 SAVE_SP(fp);
4121 obj = fp->varobj;
4122 ida = JS_Enumerate(cx, obj);
4123 if (!ida) {
4124 ok = JS_FALSE;
4125 } else {
4126 for (i = 0, j = ida->length; i < j; i++) {
4127 id = ida->vector[i];
4128 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
4129 if (!ok)
4130 break;
4131 if (!prop)
4132 continue;
4133 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4134 if (ok) {
4135 attrs |= JSPROP_EXPORTED;
4136 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4137 }
4138 OBJ_DROP_PROPERTY(cx, obj2, prop);
4139 if (!ok)
4140 break;
4141 }
4142 JS_DestroyIdArray(cx, ida);
4143 }
4144 break;
4146 BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0)
4147 id = ATOM_TO_JSID(atom);
4148 obj = fp->varobj;
4149 SAVE_SP(fp);
4150 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
4151 if (!ok)
4152 goto out;
4153 if (!prop) {
4154 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4155 JSPROP_EXPORTED, NULL);
4156 } else {
4157 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4158 if (ok) {
4159 attrs |= JSPROP_EXPORTED;
4160 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
4161 }
4162 OBJ_DROP_PROPERTY(cx, obj2, prop);
4163 }
4164 if (!ok)
4165 goto out;
4166 END_LITOPX_CASE
4168 case JSOP_IMPORTALL:
4169 id = (jsid) JSVAL_VOID;
4170 PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
4171 sp--;
4172 break;
4174 case JSOP_IMPORTPROP:
4175 /* Get an immediate atom naming the property. */
4176 atom = GET_ATOM(cx, script, pc);
4177 id = ATOM_TO_JSID(atom);
4178 PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
4179 sp--;
4180 break;
4182 case JSOP_IMPORTELEM:
4183 ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id));
4184 sp -= 2;
4185 break;
4186 #endif /* JS_HAS_EXPORT_IMPORT */
4188 case JSOP_TRAP:
4189 switch (JS_HandleTrap(cx, script, pc, &rval)) {
4190 case JSTRAP_ERROR:
4191 ok = JS_FALSE;
4192 goto out;
4193 case JSTRAP_CONTINUE:
4194 JS_ASSERT(JSVAL_IS_INT(rval));
4195 op = (JSOp) JSVAL_TO_INT(rval);
4196 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
4197 LOAD_INTERRUPT_HANDLER(rt);
4198 goto do_op;
4199 case JSTRAP_RETURN:
4200 fp->rval = rval;
4201 goto out;
4202 #if JS_HAS_EXCEPTIONS
4203 case JSTRAP_THROW:
4204 cx->throwing = JS_TRUE;
4205 cx->exception = rval;
4206 ok = JS_FALSE;
4207 goto out;
4208 #endif /* JS_HAS_EXCEPTIONS */
4209 default:;
4210 }
4211 LOAD_INTERRUPT_HANDLER(rt);
4212 break;
4214 case JSOP_ARGUMENTS:
4215 SAVE_SP(fp);
4216 ok = js_GetArgsValue(cx, fp, &rval);
4217 if (!ok)
4218 goto out;
4219 PUSH_OPND(rval);
4220 break;
4222 case JSOP_ARGSUB:
4223 id = INT_TO_JSID(GET_ARGNO(pc));
4224 SAVE_SP(fp);
4225 ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
4226 if (!ok)
4227 goto out;
4228 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 obj = LAZY_ARGS_THISP;
4238 }
4239 PUSH_OPND(rval);
4240 break;
4242 #undef LAZY_ARGS_THISP
4244 case JSOP_ARGCNT:
4245 id = ATOM_TO_JSID(rt->atomState.lengthAtom);
4246 SAVE_SP(fp);
4247 ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
4248 if (!ok)
4249 goto out;
4250 PUSH_OPND(rval);
4251 break;
4253 case JSOP_GETARG:
4254 slot = GET_ARGNO(pc);
4255 JS_ASSERT(slot < fp->fun->nargs);
4256 PUSH_OPND(fp->argv[slot]);
4257 obj = NULL;
4258 break;
4260 case JSOP_SETARG:
4261 slot = GET_ARGNO(pc);
4262 JS_ASSERT(slot < fp->fun->nargs);
4263 vp = &fp->argv[slot];
4264 GC_POKE(cx, *vp);
4265 *vp = FETCH_OPND(-1);
4266 obj = NULL;
4267 break;
4269 case JSOP_GETVAR:
4270 slot = GET_VARNO(pc);
4271 JS_ASSERT(slot < fp->fun->nvars);
4272 PUSH_OPND(fp->vars[slot]);
4273 obj = NULL;
4274 break;
4276 case JSOP_SETVAR:
4277 slot = GET_VARNO(pc);
4278 JS_ASSERT(slot < fp->fun->nvars);
4279 vp = &fp->vars[slot];
4280 GC_POKE(cx, *vp);
4281 *vp = FETCH_OPND(-1);
4282 obj = NULL;
4283 break;
4285 case JSOP_GETGVAR:
4286 slot = GET_VARNO(pc);
4287 JS_ASSERT(slot < fp->nvars);
4288 lval = fp->vars[slot];
4289 if (JSVAL_IS_NULL(lval)) {
4290 op = JSOP_NAME;
4291 goto do_op;
4292 }
4293 slot = JSVAL_TO_INT(lval);
4294 obj = fp->varobj;
4295 rval = OBJ_GET_SLOT(cx, obj, slot);
4296 PUSH_OPND(rval);
4297 break;
4299 case JSOP_SETGVAR:
4300 slot = GET_VARNO(pc);
4301 JS_ASSERT(slot < fp->nvars);
4302 rval = FETCH_OPND(-1);
4303 lval = fp->vars[slot];
4304 obj = fp->varobj;
4305 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 atom = GET_ATOM(cx, script, pc);
4312 id = ATOM_TO_JSID(atom);
4313 SAVE_SP(fp);
4314 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
4315 if (!ok)
4316 goto out;
4317 STORE_OPND(-1, rval);
4318 } else {
4319 slot = JSVAL_TO_INT(lval);
4320 GC_POKE(cx, obj->slots[slot]);
4321 OBJ_SET_SLOT(cx, obj, slot, rval);
4322 }
4323 obj = NULL;
4324 break;
4326 case JSOP_DEFCONST:
4327 case JSOP_DEFVAR:
4328 atomIndex = GET_ATOM_INDEX(pc);
4330 do_JSOP_DEFCONST:
4331 do_JSOP_DEFVAR:
4332 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4333 obj = fp->varobj;
4334 attrs = JSPROP_ENUMERATE;
4335 if (!(fp->flags & JSFRAME_EVAL))
4336 attrs |= JSPROP_PERMANENT;
4337 if (op == JSOP_DEFCONST)
4338 attrs |= JSPROP_READONLY;
4340 /* Lookup id in order to check for redeclaration problems. */
4341 id = ATOM_TO_JSID(atom);
4342 SAVE_SP(fp);
4343 ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop);
4344 if (!ok)
4345 goto out;
4347 /* Bind a variable only if it's not yet defined. */
4348 if (!prop) {
4349 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4350 attrs, &prop);
4351 if (!ok)
4352 goto out;
4353 JS_ASSERT(prop);
4354 obj2 = obj;
4355 }
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 if (atomIndex < script->numGlobalVars &&
4364 (attrs & JSPROP_PERMANENT) &&
4365 obj2 == obj &&
4366 OBJ_IS_NATIVE(obj)) {
4367 sprop = (JSScopeProperty *) prop;
4368 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 fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
4378 }
4379 }
4381 OBJ_DROP_PROPERTY(cx, obj2, prop);
4382 break;
4384 BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0)
4385 {
4386 uintN flags;
4388 atomIndex = GET_ATOM_INDEX(pc);
4389 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4390 obj = ATOM_TO_OBJECT(atom);
4391 fun = (JSFunction *) JS_GetPrivate(cx, obj);
4392 id = ATOM_TO_JSID(fun->atom);
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 obj2 = fp->scopeChain;
4425 if (OBJ_GET_PARENT(cx, obj) != obj2) {
4426 obj = js_CloneFunctionObject(cx, obj, obj2);
4427 if (!obj) {
4428 ok = JS_FALSE;
4429 goto out;
4430 }
4431 }
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 fp->scopeChain = obj;
4439 rval = OBJECT_TO_JSVAL(obj);
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 attrs = JSPROP_ENUMERATE;
4447 if (!(fp->flags & JSFRAME_EVAL))
4448 attrs |= JSPROP_PERMANENT;
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 flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
4456 if (flags) {
4457 attrs |= flags | JSPROP_SHARED;
4458 rval = JSVAL_VOID;
4459 }
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 parent = fp->varobj;
4468 SAVE_SP(fp);
4469 ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
4470 if (ok) {
4471 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 }
4482 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
4483 fp->scopeChain = obj2;
4484 if (!ok)
4485 goto out;
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 OBJ_DROP_PROPERTY(cx, parent, prop);
4500 }
4501 END_LITOPX_CASE
4503 #if JS_HAS_LEXICAL_CLOSURE
4504 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 slot = GET_VARNO(pc);
4513 atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4514 obj = ATOM_TO_OBJECT(atom);
4515 fun = (JSFunction *) JS_GetPrivate(cx, obj);
4517 parent = fp->scopeChain;
4518 if (OBJ_GET_PARENT(cx, obj) != parent) {
4519 SAVE_SP(fp);
4520 obj = js_CloneFunctionObject(cx, obj, parent);
4521 if (!obj) {
4522 ok = JS_FALSE;
4523 goto out;
4524 }
4525 }
4526 fp->vars[slot] = OBJECT_TO_JSVAL(obj);
4527 END_LITOPX_CASE
4529 BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0)
4530 /* Push the specified function object literal. */
4531 obj = ATOM_TO_OBJECT(atom);
4533 /* If re-parenting, push a clone of the function object. */
4534 parent = fp->scopeChain;
4535 if (OBJ_GET_PARENT(cx, obj) != parent) {
4536 SAVE_SP(fp);
4537 obj = js_CloneFunctionObject(cx, obj, parent);
4538 if (!obj) {
4539 ok = JS_FALSE;
4540 goto out;
4541 }
4542 }
4543 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4544 obj = NULL;
4545 END_LITOPX_CASE
4547 BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0)
4548 /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */
4549 rval = ATOM_KEY(atom);
4550 JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval));
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 SAVE_SP(fp);
4561 obj2 = fp->scopeChain;
4562 parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2);
4563 if (!parent) {
4564 ok = JS_FALSE;
4565 goto out;
4566 }
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 fp->scopeChain = parent;
4583 obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent);
4584 if (!obj) {
4585 ok = JS_FALSE;
4586 goto out;
4587 }
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 fp->scopeChain = obj;
4595 rval = OBJECT_TO_JSVAL(obj);
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 fun = (JSFunction *) JS_GetPrivate(cx, obj);
4603 attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
4604 if (attrs) {
4605 attrs |= JSPROP_SHARED;
4606 rval = JSVAL_VOID;
4607 }
4608 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);
4620 /* Restore fp->scopeChain now that obj is defined in parent. */
4621 fp->scopeChain = obj2;
4622 if (!ok) {
4623 cx->newborn[GCX_OBJECT] = NULL;
4624 goto out;
4625 }
4627 /*
4628 * 5. Remove Result(1) from the front of the scope chain [no-op].
4629 * 6. Return Result(3).
4630 */
4631 PUSH_OPND(OBJECT_TO_JSVAL(obj));
4632 obj = NULL;
4633 END_LITOPX_CASE
4635 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 obj = ATOM_TO_OBJECT(atom);
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 SAVE_SP(fp);
4655 obj2 = fp->scopeChain;
4656 if (OBJ_GET_PARENT(cx, obj) != obj2) {
4657 obj = js_CloneFunctionObject(cx, obj, obj2);
4658 if (!obj) {
4659 ok = JS_FALSE;
4660 goto out;
4661 }
4662 }
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 fp->scopeChain = obj;
4670 rval = OBJECT_TO_JSVAL(obj);
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 fun = (JSFunction *) JS_GetPrivate(cx, obj);
4678 attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
4679 if (attrs) {
4680 attrs |= JSPROP_SHARED;
4681 rval = JSVAL_VOID;
4682 }
4683 parent = fp->varobj;
4684 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);
4695 /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
4696 fp->scopeChain = obj2;
4697 if (!ok) {
4698 cx->newborn[GCX_OBJECT] = NULL;
4699 goto out;
4700 }
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 OBJ_DROP_PROPERTY(cx, parent, prop);
4714 END_LITOPX_CASE
4715 #endif /* JS_HAS_LEXICAL_CLOSURE */
4717 #if JS_HAS_GETTER_SETTER
4718 case JSOP_GETTER:
4719 case JSOP_SETTER:
4720 JS_ASSERT(len == 1);
4721 op2 = (JSOp) *++pc;
4722 cs = &js_CodeSpec[op2];
4723 len = cs->length;
4724 switch (op2) {
4725 case JSOP_SETNAME:
4726 case JSOP_SETPROP:
4727 atom = GET_ATOM(cx, script, pc);
4728 id = ATOM_TO_JSID(atom);
4729 rval = FETCH_OPND(-1);
4730 i = -1;
4731 goto gs_pop_lval;
4733 case JSOP_SETELEM:
4734 rval = FETCH_OPND(-1);
4735 FETCH_ELEMENT_ID(-2, id);
4736 i = -2;
4737 gs_pop_lval:
4738 FETCH_OBJECT(cx, i - 1, lval, obj);
4739 break;
4741 #if JS_HAS_INITIALIZERS
4742 case JSOP_INITPROP:
4743 JS_ASSERT(sp - fp->spbase >= 2);
4744 rval = FETCH_OPND(-1);
4745 i = -1;
4746 atom = GET_ATOM(cx, script, pc);
4747 id = ATOM_TO_JSID(atom);
4748 goto gs_get_lval;
4750 case JSOP_INITELEM:
4751 JS_ASSERT(sp - fp->spbase >= 3);
4752 rval = FETCH_OPND(-1);
4753 FETCH_ELEMENT_ID(-2, id);
4754 i = -2;
4755 gs_get_lval:
4756 lval = FETCH_OPND(i-1);
4757 JS_ASSERT(JSVAL_IS_OBJECT(lval));
4758 obj = JSVAL_TO_OBJECT(lval);
4759 break;
4760 #endif /* JS_HAS_INITIALIZERS */
4762 default:
4763 JS_ASSERT(0);
4764 }
4766 /* Ensure that id has a type suitable for use with obj. */
4767 CHECK_ELEMENT_ID(obj, id);
4769 SAVE_SP(fp);
4770 if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
4771 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 ok = JS_FALSE;
4777 goto out;
4778 }
4780 /*
4781 * Getters and setters are just like watchpoints from an access
4782 * control point of view.
4783 */
4784 ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs);
4785 if (!ok)
4786 goto out;
4788 if (op == JSOP_GETTER) {
4789 getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval);
4790 setter = NULL;
4791 attrs = JSPROP_GETTER;
4792 } else {
4793 getter = NULL;
4794 setter = (JSPropertyOp) JSVAL_TO_OBJECT(rval);
4795 attrs = JSPROP_SETTER;
4796 }
4797 attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
4799 /* Check for a readonly or permanent property of the same name. */
4800 ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL);
4801 if (!ok)
4802 goto out;
4804 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,
4805 attrs, NULL);
4806 if (!ok)
4807 goto out;
4809 obj = NULL;
4810 sp += i;
4811 if (cs->ndefs)
4812 STORE_OPND(-1, rval);
4813 break;
4814 #endif /* JS_HAS_GETTER_SETTER */
4816 #if JS_HAS_INITIALIZERS
4817 case JSOP_NEWINIT:
4818 argc = 0;
4819 fp->sharpDepth++;
4820 goto do_new;
4822 case JSOP_ENDINIT:
4823 if (--fp->sharpDepth == 0)
4824 fp->sharpArray = NULL;
4826 /* Re-set the newborn root to the top of this object tree. */
4827 JS_ASSERT(sp - fp->spbase >= 1);
4828 lval = FETCH_OPND(-1);
4829 JS_ASSERT(JSVAL_IS_OBJECT(lval));
4830 cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval);
4831 break;
4833 case JSOP_INITPROP:
4834 /* Pop the property's value into rval. */
4835 JS_ASSERT(sp - fp->spbase >= 2);
4836 rval = FETCH_OPND(-1);
4838 /* Get the immediate property name into id. */
4839 atom = GET_ATOM(cx, script, pc);
4840 id = ATOM_TO_JSID(atom);
4841 i = -1;
4842 goto do_init;
4844 case JSOP_INITELEM:
4845 /* Pop the element's value into rval. */
4846 JS_ASSERT(sp - fp->spbase >= 3);
4847 rval = FETCH_OPND(-1);
4849 /* Pop and conditionally atomize the element id. */
4850 FETCH_ELEMENT_ID(-2, id);
4851 i = -2;
4853 do_init:
4854 /* Find the object being initialized at top of stack. */
4855 lval = FETCH_OPND(i-1);
4856 JS_ASSERT(JSVAL_IS_OBJECT(lval));
4857 obj = JSVAL_TO_OBJECT(lval);
4859 /* Ensure that id has a type suitable for use with obj. */
4860 CHECK_ELEMENT_ID(obj, id);
4862 /* Set the property named by obj[id] to rval. */
4863 SAVE_SP(fp);
4864 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
4865 if (!ok)
4866 goto out;
4867 sp += i;
4868 break;
4870 #if JS_HAS_SHARP_VARS
4871 case JSOP_DEFSHARP:
4872 SAVE_SP(fp);
4873 obj = fp->sharpArray;
4874 if (!obj) {
4875 obj = js_NewArrayObject(cx, 0, NULL);
4876 if (!obj) {
4877 ok = JS_FALSE;
4878 goto out;
4879 }
4880 fp->sharpArray = obj;
4881 }
4882 i = (jsint) GET_ATOM_INDEX(pc);
4883 id = INT_TO_JSID(i);
4884 rval = FETCH_OPND(-1);
4885 if (JSVAL_IS_PRIMITIVE(rval)) {
4886 char numBuf[12];
4887 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
4888 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4889 JSMSG_BAD_SHARP_DEF, numBuf);
4890 ok = JS_FALSE;
4891 goto out;
4892 }
4893 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
4894 if (!ok)
4895 goto out;
4896 break;
4898 case JSOP_USESHARP:
4899 i = (jsint) GET_ATOM_INDEX(pc);
4900 id = INT_TO_JSID(i);
4901 obj = fp->sharpArray;
4902 if (!obj) {
4903 rval = JSVAL_VOID;
4904 } else {
4905 SAVE_SP(fp);
4906 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
4907 if (!ok)
4908 goto out;
4909 }
4910 if (!JSVAL_IS_OBJECT(rval)) {
4911 char numBuf[12];
4912 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
4913 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4914 JSMSG_BAD_SHARP_USE, numBuf);
4915 ok = JS_FALSE;
4916 goto out;
4917 }
4918 PUSH_OPND(rval);
4919 break;
4920 #endif /* JS_HAS_SHARP_VARS */
4921 #endif /* JS_HAS_INITIALIZERS */
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;
4929 /* Reset the stack to the given depth. */
4930 case JSOP_SETSP:
4931 i = (jsint) GET_ATOM_INDEX(pc);
4932 JS_ASSERT(i >= 0);
4933 sp = fp->spbase + i;
4935 obj = fp->scopeChain;
4936 while (OBJ_GET_CLASS(cx, obj) == &js_WithClass &&
4937 JS_GetPrivate(cx, obj) == fp &&
4938 OBJ_BLOCK_DEPTH(cx, obj) >= i) {
4939 obj = OBJ_GET_PARENT(cx, obj);
4940 }
4941 fp->scopeChain = obj;
4942 break;
4944 case JSOP_GOSUB:
4945 JS_ASSERT(cx->exception != JSVAL_HOLE);
4946 if (!cx->throwing) {
4947 lval = JSVAL_HOLE;
4948 } else {
4949 lval = cx->exception;
4950 cx->throwing = JS_FALSE;
4951 }
4952 PUSH(lval);
4953 i = PTRDIFF(pc, script->main, jsbytecode) + len;
4954 len = GET_JUMP_OFFSET(pc);
4955 PUSH(INT_TO_JSVAL(i));
4956 break;
4958 case JSOP_GOSUBX:
4959 JS_ASSERT(cx->exception != JSVAL_HOLE);
4960 lval = cx->throwing ? cx->exception : JSVAL_HOLE;
4961 PUSH(lval);
4962 i = PTRDIFF(pc, script->main, jsbytecode) + len;
4963 len = GET_JUMPX_OFFSET(pc);
4964 PUSH(INT_TO_JSVAL(i));
4965 break;
4967 case JSOP_RETSUB:
4968 rval = POP();
4969 JS_ASSERT(JSVAL_IS_INT(rval));
4970 lval = POP();
4971 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 cx->throwing = JS_TRUE;
4979 cx->exception = lval;
4980 ok = JS_FALSE;
4981 goto out;
4982 }
4983 i = JSVAL_TO_INT(rval);
4984 pc = script->main + i;
4985 len = 0;
4986 break;
4988 case JSOP_EXCEPTION:
4989 PUSH(cx->exception);
4990 cx->throwing = JS_FALSE;
4991 break;
4993 case JSOP_THROWING:
4994 JS_ASSERT(!cx->throwing);
4995 cx->throwing = JS_TRUE;
4996 break;
4998 case JSOP_THROW:
4999 cx->throwing = JS_TRUE;
5000 cx->exception = POP_OPND();
5001 ok = JS_FALSE;
5002 /* let the code at out try to catch the exception. */
5003 goto out;
5005 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 rval = FETCH_OPND(-1);
5010 /* Get the immediate catch variable name into id. */
5011 id = ATOM_TO_JSID(atom);
5013 /* Find the object being initialized at top of stack. */
5014 lval = FETCH_OPND(-2);
5015 JS_ASSERT(JSVAL_IS_OBJECT(lval));
5016 obj = JSVAL_TO_OBJECT(lval);
5018 SAVE_SP(fp);
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 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
5026 if (!ok)
5027 goto out;
5028 if (!(attrs & (JSPROP_READONLY | JSPROP_PERMANENT |
5029 JSPROP_GETTER | JSPROP_SETTER))) {
5030 /* Define obj[id] to contain rval and to be permanent. */
5031 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
5032 JSPROP_PERMANENT, NULL);
5033 if (!ok)
5034 goto out;
5035 }
5037 /* Now that we're done with rval, pop it. */
5038 sp--;
5039 END_LITOPX_CASE
5040 #endif /* JS_HAS_EXCEPTIONS */
5042 #if JS_HAS_INSTANCEOF
5043 case JSOP_INSTANCEOF:
5044 rval = FETCH_OPND(-1);
5045 if (JSVAL_IS_PRIMITIVE(rval) ||
5046 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
5047 SAVE_SP(fp);
5048 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
5049 if (str) {
5050 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5051 JSMSG_BAD_INSTANCEOF_RHS,
5052 JS_GetStringBytes(str));
5053 }
5054 ok = JS_FALSE;
5055 goto out;
5056 }
5057 lval = FETCH_OPND(-2);
5058 cond = JS_FALSE;
5059 SAVE_SP(fp);
5060 ok = obj->map->ops->hasInstance(cx, obj, lval, &cond);
5061 if (!ok)
5062 goto out;
5063 sp--;
5064 STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
5065 break;
5066 #endif /* JS_HAS_INSTANCEOF */
5068 #if JS_HAS_DEBUGGER_KEYWORD
5069 case JSOP_DEBUGGER:
5070 {
5071 JSTrapHandler handler = rt->debuggerHandler;
5072 if (handler) {
5073 SAVE_SP(fp);
5074 switch (handler(cx, script, pc, &rval,
5075 rt->debuggerHandlerData)) {
5076 case JSTRAP_ERROR:
5077 ok = JS_FALSE;
5078 goto out;
5079 case JSTRAP_CONTINUE:
5080 break;
5081 case JSTRAP_RETURN:
5082 fp->rval = rval;
5083 goto out;
5084 #if JS_HAS_EXCEPTIONS
5085 case JSTRAP_THROW:
5086 cx->throwing = JS_TRUE;
5087 cx->exception = rval;
5088 ok = JS_FALSE;
5089 goto out;
5090 #endif /* JS_HAS_EXCEPTIONS */
5091 default:;
5092 }
5093 LOAD_INTERRUPT_HANDLER(rt);
5094 }
5095 break;
5096 }
5097 #endif /* JS_HAS_DEBUGGER_KEYWORD */
5099 #if JS_HAS_XML_SUPPORT
5100 case JSOP_DEFXMLNS:
5101 rval = POP();
5102 SAVE_SP(fp);
5103 ok = js_SetDefaultXMLNamespace(cx, rval);
5104 if (!ok)
5105 goto out;
5106 break;
5108 case JSOP_ANYNAME:
5109 SAVE_SP(fp);
5110 ok = js_GetAnyName(cx, &rval);
5111 if (!ok)
5112 goto out;
5113 PUSH_OPND(rval);
5114 break;
5116 BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0)
5117 PUSH_OPND(ATOM_KEY(atom));
5118 END_LITOPX_CASE
5120 BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0)
5121 rval = ATOM_KEY(atom);
5122 lval = FETCH_OPND(-1);
5123 SAVE_SP(fp);
5124 obj = js_ConstructXMLQNameObject(cx, lval, rval);
5125 if (!obj) {
5126 ok = JS_FALSE;
5127 goto out;
5128 }
5129 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5130 END_LITOPX_CASE
5132 case JSOP_QNAME:
5133 rval = FETCH_OPND(-1);
5134 lval = FETCH_OPND(-2);
5135 SAVE_SP(fp);
5136 obj = js_ConstructXMLQNameObject(cx, lval, rval);
5137 if (!obj) {
5138 ok = JS_FALSE;
5139 goto out;
5140 }
5141 sp--;
5142 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5143 break;
5145 case JSOP_TOATTRNAME:
5146 rval = FETCH_OPND(-1);
5147 SAVE_SP(fp);
5148 ok = js_ToAttributeName(cx, &rval);
5149 if (!ok)
5150 goto out;
5151 STORE_OPND(-1, rval);
5152 break;
5154 case JSOP_TOATTRVAL:
5155 rval = FETCH_OPND(-1);
5156 JS_ASSERT(JSVAL_IS_STRING(rval));
5157 SAVE_SP(fp);
5158 str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval));
5159 if (!str) {
5160 ok = JS_FALSE;
5161 goto out;
5162 }
5163 STORE_OPND(-1, STRING_TO_JSVAL(str));
5164 break;
5166 case JSOP_ADDATTRNAME:
5167 case JSOP_ADDATTRVAL:
5168 rval = FETCH_OPND(-1);
5169 lval = FETCH_OPND(-2);
5170 str = JSVAL_TO_STRING(lval);
5171 str2 = JSVAL_TO_STRING(rval);
5172 SAVE_SP(fp);
5173 str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
5174 if (!str) {
5175 ok = JS_FALSE;
5176 goto out;
5177 }
5178 sp--;
5179 STORE_OPND(-1, STRING_TO_JSVAL(str));
5180 break;
5182 case JSOP_BINDXMLNAME:
5183 lval = FETCH_OPND(-1);
5184 SAVE_SP(fp);
5185 ok = js_FindXMLProperty(cx, lval, &obj, &rval);
5186 if (!ok)
5187 goto out;
5188 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5189 PUSH_OPND(rval);
5190 break;
5192 case JSOP_SETXMLNAME:
5193 obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
5194 lval = FETCH_OPND(-2);
5195 rval = FETCH_OPND(-1);
5196 SAVE_SP(fp);
5197 ok = js_SetXMLProperty(cx, obj, lval, &rval);
5198 if (!ok)
5199 goto out;
5200 sp -= 2;
5201 STORE_OPND(-1, rval);
5202 obj = NULL;
5203 break;
5205 case JSOP_XMLNAME:
5206 lval = FETCH_OPND(-1);
5207 SAVE_SP(fp);
5208 ok = js_FindXMLProperty(cx, lval, &obj, &rval);
5209 if (!ok)
5210 goto out;
5211 ok = js_GetXMLProperty(cx, obj, rval, &rval);
5212 if (!ok)
5213 goto out;
5214 STORE_OPND(-1, rval);
5215 break;
5217 case JSOP_DESCENDANTS:
5218 case JSOP_DELDESC:
5219 FETCH_OBJECT(cx, -2, lval, obj);
5220 rval = FETCH_OPND(-1);
5221 SAVE_SP(fp);
5222 ok = js_GetXMLDescendants(cx, obj, rval, &rval);
5223 if (!ok)
5224 goto out;
5226 if (op == JSOP_DELDESC) {
5227 sp[-1] = rval; /* set local root */
5228 ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval));
5229 if (!ok)
5230 goto out;
5231 rval = JSVAL_TRUE; /* always succeed */
5232 }
5234 sp--;
5235 STORE_OPND(-1, rval);
5236 break;
5238 case JSOP_FILTER:
5239 FETCH_OBJECT(cx, -1, lval, obj);
5240 len = GET_JUMP_OFFSET(pc);
5241 SAVE_SP(fp);
5242 ok = js_FilterXMLList(cx, obj, pc + cs->length, &rval);
5243 if (!ok)
5244 goto out;
5245 JS_ASSERT(fp->sp == sp);
5246 STORE_OPND(-1, rval);
5247 break;
5249 case JSOP_ENDFILTER:
5250 *result = POP_OPND();
5251 goto out;
5253 case JSOP_STARTXML:
5254 case JSOP_STARTXMLEXPR:
5255 break;
5257 case JSOP_TOXML:
5258 rval = FETCH_OPND(-1);
5259 SAVE_SP(fp);
5260 obj = js_ValueToXMLObject(cx, rval);
5261 if (!obj) {
5262 ok = JS_FALSE;
5263 goto out;
5264 }
5265 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5266 break;
5268 case JSOP_TOXMLLIST:
5269 rval = FETCH_OPND(-1);
5270 SAVE_SP(fp);
5271 obj = js_ValueToXMLListObject(cx, rval);
5272 if (!obj) {
5273 ok = JS_FALSE;
5274 goto out;
5275 }
5276 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5277 break;
5279 case JSOP_XMLTAGEXPR:
5280 rval = FETCH_OPND(-1);
5281 SAVE_SP(fp);
5282 str = js_ValueToString(cx, rval);
5283 if (!str) {
5284 ok = JS_FALSE;
5285 goto out;
5286 }
5287 STORE_OPND(-1, STRING_TO_JSVAL(str));
5288 break;
5290 case JSOP_XMLELTEXPR:
5291 rval = FETCH_OPND(-1);
5292 SAVE_SP(fp);
5293 if (VALUE_IS_XML(cx, rval)) {
5294 str = js_ValueToXMLString(cx, rval);
5295 } else {
5296 str = js_ValueToString(cx, rval);
5297 if (str)
5298 str = js_EscapeElementValue(cx, str);
5299 }
5300 if (!str) {
5301 ok = JS_FALSE;
5302 goto out;
5303 }
5304 STORE_OPND(-1, STRING_TO_JSVAL(str));
5305 break;
5307 BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0)
5308 SAVE_SP(fp);
5309 obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom));
5310 if (!obj) {
5311 ok = JS_FALSE;
5312 goto out;
5313 }
5314 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5315 obj = NULL;
5316 END_LITOPX_CASE
5318 BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0)
5319 str = ATOM_TO_STRING(atom);
5320 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str);
5321 if (!obj) {
5322 ok = JS_FALSE;
5323 goto out;
5324 }
5325 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5326 END_LITOPX_CASE
5328 BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0)
5329 str = ATOM_TO_STRING(atom);
5330 obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str);
5331 if (!obj) {
5332 ok = JS_FALSE;
5333 goto out;
5334 }
5335 PUSH_OPND(OBJECT_TO_JSVAL(obj));
5336 END_LITOPX_CASE
5338 BEGIN_LITOPX_CASE(JSOP_XMLPI, 0)
5339 str = ATOM_TO_STRING(atom);
5340 rval = FETCH_OPND(-1);
5341 str2 = JSVAL_TO_STRING(rval);
5342 SAVE_SP(fp);
5343 obj = js_NewXMLSpecialObject(cx,
5344 JSXML_CLASS_PROCESSING_INSTRUCTION,
5345 str, str2);
5346 if (!obj) {
5347 ok = JS_FALSE;
5348 goto out;
5349 }
5350 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
5351 END_LITOPX_CASE
5353 BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0)
5354 /* Get an immediate atom naming the property. */
5355 id = ATOM_TO_JSID(atom);
5356 FETCH_OBJECT(cx, -1, lval, obj);
5357 SAVE_SP(fp);
5359 /* Special-case XML object method lookup, per ECMA-357. */
5360 if (OBJECT_IS_XML(cx, obj)) {
5361 JSXMLObjectOps *ops;
5363 ops = (JSXMLObjectOps *) obj->map->ops;
5364 obj = ops->getMethod(cx, obj, id, &rval);
5365 if (!obj)
5366 ok = JS_FALSE;
5367 } else {
5368 CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
5369 }
5370 if (!ok)
5371 goto out;
5372 STORE_OPND(-1, rval);
5373 END_LITOPX_CASE
5375 BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0)
5376 /* Get an immediate atom naming the property. */
5377 id = ATOM_TO_JSID(atom);
5378 rval = FETCH_OPND(-1);
5379 FETCH_OBJECT(cx, -2, lval, obj);
5380 SAVE_SP(fp);
5382 /* Special-case XML object method lookup, per ECMA-357. */
5383 if (OBJECT_IS_XML(cx, obj)) {
5384 JSXMLObjectOps *ops;
5386 ops = (JSXMLObjectOps *) obj->map->ops;
5387 ok = ops->setMethod(cx, obj, id, &rval);
5388 } else {
5389 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
5390 }
5391 if (!ok)
5392 goto out;
5393 --sp;
5394 STORE_OPND(-1, rval);
5395 obj = NULL;
5396 END_LITOPX_CASE
5398 case JSOP_GETFUNNS:
5399 ok = js_GetFunctionNamespace(cx, &rval);
5400 if (!ok)
5401 goto out;
5402 PUSH_OPND(rval);
5403 break;
5405 case JSOP_FOREACH:
5406 foreach = JS_TRUE;
5407 break;
5408 #endif /* JS_HAS_XML_SUPPORT */
5410 default: {
5411 char numBuf[12];
5412 JS_snprintf(numBuf, sizeof numBuf, "%d", op);
5413 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5414 JSMSG_BAD_BYTECODE, numBuf);
5415 ok = JS_FALSE;
5416 goto out;
5417 }
5418 }
5420 advance_pc:
5421 pc += len;
5423 #ifdef DEBUG
5424 if (tracefp) {
5425 intN ndefs, n;
5426 jsval *siter;
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 out:
5455 #if JS_HAS_EXCEPTIONS
5456 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 if (cx->throwing && JS_LIKELY(mark != NULL)) {
5484 /*
5485 * Call debugger throw hook if set (XXX thread safety?).
5486 */
5487 JSTrapHandler handler = rt->throwHook;
5488 if (handler) {
5489 SAVE_SP(fp);
5490 switch (handler(cx, script, pc, &rval, rt->throwHookData)) {
5491 case JSTRAP_ERROR:
5492 cx->throwing = JS_FALSE;
5493 goto no_catch;
5494 case JSTRAP_RETURN:
5495 ok = JS_TRUE;
5496 cx->throwing = JS_FALSE;
5497 fp->rval = rval;
5498 goto no_catch;
5499 case JSTRAP_THROW:
5500 cx->exception = rval;
5501 case JSTRAP_CONTINUE:
5502 default:;
5503 }
5504 LOAD_INTERRUPT_HANDLER(rt);
5505 }
5507 /*
5508 * Look for a try block in script that can catch this exception.
5509 */
5510 SCRIPT_FIND_CATCH_START(script, pc, pc);
5511 if (pc) {
5512 /* Don't clear cx->throwing to save cx->exception from GC. */
5513 len = 0;
5514 ok = JS_TRUE;
5515 #if JS_HAS_XML_SUPPORT
5516 foreach = JS_FALSE;
5517 #endif
5518 goto advance_pc;
5519 }
5520 }
5521 no_catch:;
5522 }
5523 #endif
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 if (inlineCallCount) {
5531 #if JS_HAS_XML_SUPPORT
5532 foreach = JS_FALSE;
5533 #endif
5534 goto inline_return;
5535 }
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 if (JS_LIKELY(mark != NULL)) {
5543 fp->sp = fp->spbase;
5544 fp->spbase = NULL;
5545 js_FreeRawStack(cx, mark);
5546 } else {
5547 SAVE_SP(fp);
5548 }
5550 out2:
5551 if (cx->version == currentVersion && currentVersion != originalVersion)
5552 js_SetVersion(cx, originalVersion);
5553 cx->interpLevel--;
5554 return ok;
5556 atom_not_defined:
5557 {
5558 const char *printable = js_AtomToPrintableString(cx, atom);
5559 if (printable)
5560 js_ReportIsNotDefined(cx, printable);
5561 ok = JS_FALSE;
5562 goto out;
5563 }
5564 }