Code

fix 1243587 and misc fixes
[inkscape.git] / src / dom / js / jsinterp.c
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
112 void
113 js_DisablePropertyCache(JSContext *cx)
115     JS_ASSERT(!cx->runtime->propertyCache.disabled);
116     cx->runtime->propertyCache.disabled = JS_TRUE;
119 void
120 js_EnablePropertyCache(JSContext *cx)
122     JS_ASSERT(cx->runtime->propertyCache.disabled);
123     ASSERT_CACHE_IS_EMPTY(&cx->runtime->propertyCache);
124     cx->runtime->propertyCache.disabled = JS_FALSE;
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)
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]);
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)
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;
343 JS_FRIEND_API(void)
344 js_FreeRawStack(JSContext *cx, void *mark)
346     JS_ARENA_RELEASE(&cx->stackPool, mark);
349 JS_FRIEND_API(jsval *)
350 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
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;
411 JS_FRIEND_API(void)
412 js_FreeStack(JSContext *cx, void *mark)
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);
436 JSBool
437 js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
439     return JS_TRUE;
442 JSBool
443 js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
445     return JS_TRUE;
448 JSBool
449 js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
451     return JS_TRUE;
454 JSBool
455 js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
457     return JS_TRUE;
460 JSBool
461 js_ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
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;
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)
562     return malloc(size);
565 static void
566 FreeCallTable(void *pool, void *item)
568     free(item);
571 static JSHashEntry *
572 AllocCallEntry(void *pool, const void *key)
574     return (JSHashEntry*) calloc(1, sizeof(CallEntry));
577 static void
578 FreeCallEntry(void *pool, JSHashEntry *he, uintN flag)
580     JS_ASSERT(flag == HT_FREE_ENTRY);
581     free(he);
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)
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;
602 JS_STATIC_DLL_CALLBACK(intN)
603 js_compare_call_keys(const void *k1, const void *k2)
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;
614 JSHashTable *js_CallTable;
615 size_t      js_LogCallToSourceLimit;
617 JS_STATIC_DLL_CALLBACK(intN)
618 CallTableDumper(JSHashEntry *he, intN k, void *arg)
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;
687 void
688 js_DumpCallTable(JSContext *cx)
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);
707 static void
708 LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv)
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     }
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)
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;
1257 JSBool
1258 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1259                   uintN argc, jsval *argv, jsval *rval)
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;
1315 JSBool
1316 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1317                     JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
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);
1346 JSBool
1347 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1348            JSStackFrame *down, uintN flags, jsval *result)
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;
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)
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;
1564 #endif /* JS_HAS_EXPORT_IMPORT */
1566 JSBool
1567 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1568                       JSObject **objp, JSProperty **propp)
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;
1643 JSBool
1644 js_StrictlyEqual(jsval lval, jsval rval)
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;
1675 static JSBool
1676 InternStringElementId(JSContext *cx, jsval idval, jsid *idp)
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;
1687 static JSBool
1688 InternNonIntElementId(JSContext *cx, jsval idval, jsid *idp)
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);
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)
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     }