Code

r11451@tres: ted | 2006-04-17 22:21:33 -0700
[inkscape.git] / src / dom / js / jsinterp.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is Mozilla Communicator client code, released
17  * March 31, 1998.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 1998
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Alternatively, the contents of this file may be used under the terms of
27  * either of the GNU General Public License Version 2 or later (the "GPL"),
28  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29  * in which case the provisions of the GPL or the LGPL are applicable instead
30  * of those above. If you wish to allow use of your version of this file only
31  * under the terms of either the GPL or the LGPL, and not to allow others to
32  * use your version of this file under the terms of the MPL, indicate your
33  * decision by deleting the provisions above and replace them with the notice
34  * and other provisions required by the GPL or the LGPL. If you do not delete
35  * the provisions above, a recipient may use your version of this file under
36  * the terms of any one of the MPL, the GPL or the LGPL.
37  *
38  * ***** END LICENSE BLOCK ***** */
40 /* build on macs with low memory */
41 #if defined(XP_MAC) && defined(MOZ_MAC_LOWMEM)
42 #pragma optimization_level 1
43 #endif
45 /*
46  * JavaScript bytecode interpreter.
47  */
48 #include "jsstddef.h"
49 #include <stdio.h>
50 #include <string.h>
51 #include <math.h>
52 #include "jstypes.h"
53 #include "jsarena.h" /* Added by JSIFY */
54 #include "jsutil.h" /* Added by JSIFY */
55 #include "jsprf.h"
56 #include "jsapi.h"
57 #include "jsarray.h"
58 #include "jsatom.h"
59 #include "jsbool.h"
60 #include "jscntxt.h"
61 #include "jsconfig.h"
62 #include "jsdbgapi.h"
63 #include "jsfun.h"
64 #include "jsgc.h"
65 #include "jsinterp.h"
66 #include "jslock.h"
67 #include "jsnum.h"
68 #include "jsobj.h"
69 #include "jsopcode.h"
70 #include "jsscope.h"
71 #include "jsscript.h"
72 #include "jsstr.h"
74 #if JS_HAS_JIT
75 #include "jsjit.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]);
147     /* XXX force the GC to restart so we can collect iteratee, if possible,
148            during the current collector activation */
149     cx->runtime->gcLevel++;
152 static JSClass prop_iterator_class = {
153     "PropertyIterator",
154     0,
155     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
156     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   prop_iterator_finalize,
157     JSCLASS_NO_OPTIONAL_MEMBERS
158 };
160 /*
161  * Stack macros and functions.  These all use a local variable, jsval *sp, to
162  * point to the next free stack slot.  SAVE_SP must be called before any call
163  * to a function that may invoke the interpreter.  RESTORE_SP must be called
164  * only after return from js_Invoke, because only js_Invoke changes fp->sp.
165  */
166 #define PUSH(v)         (*sp++ = (v))
167 #define POP()           (*--sp)
168 #ifdef DEBUG
169 #define SAVE_SP(fp)                                                           \
170     (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase),        \
171      (fp)->sp = sp)
172 #else
173 #define SAVE_SP(fp)     ((fp)->sp = sp)
174 #endif
175 #define RESTORE_SP(fp)  (sp = (fp)->sp)
177 /*
178  * Push the generating bytecode's pc onto the parallel pc stack that runs
179  * depth slots below the operands.
180  *
181  * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment.  See
182  * js_Interpret for these local variables' declarations and uses.
183  */
184 #define PUSH_OPND(v)    (sp[-depth] = (jsval)pc, PUSH(v))
185 #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v))
186 #define POP_OPND()      POP()
187 #define FETCH_OPND(n)   (sp[n])
189 /*
190  * Push the jsdouble d using sp, depth, and pc from the lexical environment.
191  * Try to convert d to a jsint that fits in a jsval, otherwise GC-alloc space
192  * for it and push a reference.
193  */
194 #define STORE_NUMBER(cx, n, d)                                                \
195     JS_BEGIN_MACRO                                                            \
196         jsint i_;                                                             \
197         jsval v_;                                                             \
198                                                                               \
199         if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) {                \
200             v_ = INT_TO_JSVAL(i_);                                            \
201         } else {                                                              \
202             ok = js_NewDoubleValue(cx, d, &v_);                               \
203             if (!ok)                                                          \
204                 goto out;                                                     \
205         }                                                                     \
206         STORE_OPND(n, v_);                                                    \
207     JS_END_MACRO
209 #define FETCH_NUMBER(cx, n, d)                                                \
210     JS_BEGIN_MACRO                                                            \
211         jsval v_;                                                             \
212                                                                               \
213         v_ = FETCH_OPND(n);                                                   \
214         VALUE_TO_NUMBER(cx, v_, d);                                           \
215     JS_END_MACRO
217 #define FETCH_INT(cx, n, i)                                                   \
218     JS_BEGIN_MACRO                                                            \
219         jsval v_ = FETCH_OPND(n);                                             \
220         if (JSVAL_IS_INT(v_)) {                                               \
221             i = JSVAL_TO_INT(v_);                                             \
222         } else {                                                              \
223             SAVE_SP(fp);                                                      \
224             ok = js_ValueToECMAInt32(cx, v_, &i);                             \
225             if (!ok)                                                          \
226                 goto out;                                                     \
227         }                                                                     \
228     JS_END_MACRO
230 #define FETCH_UINT(cx, n, ui)                                                 \
231     JS_BEGIN_MACRO                                                            \
232         jsval v_ = FETCH_OPND(n);                                             \
233         jsint i_;                                                             \
234         if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) {               \
235             ui = (uint32) i_;                                                 \
236         } else {                                                              \
237             SAVE_SP(fp);                                                      \
238             ok = js_ValueToECMAUint32(cx, v_, &ui);                           \
239             if (!ok)                                                          \
240                 goto out;                                                     \
241         }                                                                     \
242     JS_END_MACRO
244 /*
245  * Optimized conversion macros that test for the desired type in v before
246  * homing sp and calling a conversion function.
247  */
248 #define VALUE_TO_NUMBER(cx, v, d)                                             \
249     JS_BEGIN_MACRO                                                            \
250         if (JSVAL_IS_INT(v)) {                                                \
251             d = (jsdouble)JSVAL_TO_INT(v);                                    \
252         } else if (JSVAL_IS_DOUBLE(v)) {                                      \
253             d = *JSVAL_TO_DOUBLE(v);                                          \
254         } else {                                                              \
255             SAVE_SP(fp);                                                      \
256             ok = js_ValueToNumber(cx, v, &d);                                 \
257             if (!ok)                                                          \
258                 goto out;                                                     \
259         }                                                                     \
260     JS_END_MACRO
262 #define POP_BOOLEAN(cx, v, b)                                                 \
263     JS_BEGIN_MACRO                                                            \
264         v = FETCH_OPND(-1);                                                   \
265         if (v == JSVAL_NULL) {                                                \
266             b = JS_FALSE;                                                     \
267         } else if (JSVAL_IS_BOOLEAN(v)) {                                     \
268             b = JSVAL_TO_BOOLEAN(v);                                          \
269         } else {                                                              \
270             SAVE_SP(fp);                                                      \
271             ok = js_ValueToBoolean(cx, v, &b);                                \
272             if (!ok)                                                          \
273                 goto out;                                                     \
274         }                                                                     \
275         sp--;                                                                 \
276     JS_END_MACRO
278 #define VALUE_TO_OBJECT(cx, v, obj)                                           \
279     JS_BEGIN_MACRO                                                            \
280         if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) {                          \
281             obj = JSVAL_TO_OBJECT(v);                                         \
282         } else {                                                              \
283             SAVE_SP(fp);                                                      \
284             obj = js_ValueToNonNullObject(cx, v);                             \
285             if (!obj) {                                                       \
286                 ok = JS_FALSE;                                                \
287                 goto out;                                                     \
288             }                                                                 \
289         }                                                                     \
290     JS_END_MACRO
292 #if JS_BUG_VOID_TOSTRING
293 #define CHECK_VOID_TOSTRING(cx, v)                                            \
294     if (JSVAL_IS_VOID(v)) {                                                   \
295         JSString *str_;                                                       \
296         str_ = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]); \
297         v = STRING_TO_JSVAL(str_);                                            \
298     }
299 #else
300 #define CHECK_VOID_TOSTRING(cx, v)  ((void)0)
301 #endif
303 #if JS_BUG_EAGER_TOSTRING
304 #define CHECK_EAGER_TOSTRING(hint)  (hint = JSTYPE_STRING)
305 #else
306 #define CHECK_EAGER_TOSTRING(hint)  ((void)0)
307 #endif
309 #define VALUE_TO_PRIMITIVE(cx, v, hint, vp)                                   \
310     JS_BEGIN_MACRO                                                            \
311         if (JSVAL_IS_PRIMITIVE(v)) {                                          \
312             CHECK_VOID_TOSTRING(cx, v);                                       \
313             *vp = v;                                                          \
314         } else {                                                              \
315             SAVE_SP(fp);                                                      \
316             CHECK_EAGER_TOSTRING(hint);                                       \
317             ok = OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, vp);         \
318             if (!ok)                                                          \
319                 goto out;                                                     \
320         }                                                                     \
321     JS_END_MACRO
323 JS_FRIEND_API(jsval *)
324 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
326     jsval *sp;
328     if (markp)
329         *markp = JS_ARENA_MARK(&cx->stackPool);
330     JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
331     if (!sp) {
332         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_STACK_OVERFLOW,
333                              (cx->fp && cx->fp->fun)
334                              ? JS_GetFunctionName(cx->fp->fun)
335                              : "script");
336     }
337     return sp;
340 JS_FRIEND_API(void)
341 js_FreeRawStack(JSContext *cx, void *mark)
343     JS_ARENA_RELEASE(&cx->stackPool, mark);
346 JS_FRIEND_API(jsval *)
347 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
349     jsval *sp, *vp, *end;
350     JSArena *a;
351     JSStackHeader *sh;
352     JSStackFrame *fp;
354     /* Callers don't check for zero nslots: we do to avoid empty segments. */
355     if (nslots == 0) {
356         *markp = NULL;
357         return JS_ARENA_MARK(&cx->stackPool);
358     }
360     /* Allocate 2 extra slots for the stack segment header we'll likely need. */
361     sp = js_AllocRawStack(cx, 2 + nslots, markp);
362     if (!sp)
363         return NULL;
365     /* Try to avoid another header if we can piggyback on the last segment. */
366     a = cx->stackPool.current;
367     sh = cx->stackHeaders;
368     if (sh && JS_STACK_SEGMENT(sh) + sh->nslots == sp) {
369         /* Extend the last stack segment, give back the 2 header slots. */
370         sh->nslots += nslots;
371         a->avail -= 2 * sizeof(jsval);
372     } else {
373         /*
374          * Need a new stack segment, so we must initialize unused slots in the
375          * current frame.  See js_GC, just before marking the "operand" jsvals,
376          * where we scan from fp->spbase to fp->sp or through fp->script->depth
377          * (whichever covers fewer slots).
378          */
379         fp = cx->fp;
380         if (fp && fp->script && fp->spbase) {
381 #ifdef DEBUG
382             jsuword depthdiff = fp->script->depth * sizeof(jsval);
383             JS_ASSERT(JS_UPTRDIFF(fp->sp, fp->spbase) <= depthdiff);
384             JS_ASSERT(JS_UPTRDIFF(*markp, fp->spbase) >= depthdiff);
385 #endif
386             end = fp->spbase + fp->script->depth;
387             for (vp = fp->sp; vp < end; vp++)
388                 *vp = JSVAL_VOID;
389         }
391         /* Allocate and push a stack segment header from the 2 extra slots. */
392         sh = (JSStackHeader *)sp;
393         sh->nslots = nslots;
394         sh->down = cx->stackHeaders;
395         cx->stackHeaders = sh;
396         sp += 2;
397     }
399     return sp;
402 JS_FRIEND_API(void)
403 js_FreeStack(JSContext *cx, void *mark)
405     JSStackHeader *sh;
406     jsuword slotdiff;
408     /* Check for zero nslots allocation special case. */
409     if (!mark)
410         return;
412     /* We can assert because js_FreeStack always balances js_AllocStack. */
413     sh = cx->stackHeaders;
414     JS_ASSERT(sh);
416     /* If mark is in the current segment, reduce sh->nslots, else pop sh. */
417     slotdiff = JS_UPTRDIFF(mark, JS_STACK_SEGMENT(sh)) / sizeof(jsval);
418     if (slotdiff < (jsuword)sh->nslots)
419         sh->nslots = slotdiff;
420     else
421         cx->stackHeaders = sh->down;
423     /* Release the stackPool space allocated since mark was set. */
424     JS_ARENA_RELEASE(&cx->stackPool, mark);
427 /*
428  * To economize on slots space in functions, the compiler records arguments and
429  * local variables as shared (JSPROP_SHARED) properties with well-known getters
430  * and setters: js_{Get,Set}Argument, js_{Get,Set}LocalVariable.  Now, we could
431  * record args and vars in lists or hash tables in function-private data, but
432  * that means more duplication in code, and more data at runtime in the hash
433  * table case due to round-up to powers of two, just to recapitulate the scope
434  * machinery in the function object.
435  *
436  * What's more, for a long time (to the dawn of "Mocha" in 1995), these getters
437  * and setters knew how to search active stack frames in a context to find the
438  * top activation of the function f, in order to satisfy a get or set of f.a,
439  * for argument a, or f.x, for local variable x.  You could use f.a instead of
440  * just a in function f(a) { return f.a }, for example, to return the actual
441  * parameter.
442  *
443  * ECMA requires that we give up on this ancient extension, because it is not
444  * compatible with the standard as used by real-world scripts.  While Chapter
445  * 16 does allow for additional properties to be defined on native objects by
446  * a conforming implementation, these magic getters and setters cause f.a's
447  * meaning to vary unexpectedly.  Real-world scripts set f.A = 42 to define
448  * "class static" (after Java) constants, for example, but if A also names an
449  * arg or var in f, the constant is not available while f is active, and any
450  * non-constant class-static can't be set while f is active.
451  *
452  * So, to label arg and var properties in functions without giving them magic
453  * abilities to affect active frame stack slots, while keeping the properties
454  * shared (slot-less) to save space in the common case (where no assignment
455  * sets a function property with the same name as an arg or var), the setters
456  * for args and vars must handle two special cases here.
457  *
458  * XXX functions tend to have few args and vars, so we risk O(n^2) growth here
459  * XXX ECMA *really* wants args and vars to be stored in function-private data,
460  *     not as function object properties.
461  */
462 static JSBool
463 SetFunctionSlot(JSContext *cx, JSObject *obj, JSPropertyOp setter, jsid id,
464                 jsval v)
466     uintN slot;
467     JSObject *origobj;
468     JSScope *scope;
469     JSScopeProperty *sprop;
470     JSString *str;
471     JSBool ok;
473     slot = (uintN) JSVAL_TO_INT(id);
474     if (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
475         /*
476          * Given a non-function object obj that has a function object in its
477          * prototype chain, where an argument or local variable property named
478          * by (setter, slot) is being set, override the shared property in the
479          * prototype with an unshared property in obj.  This situation arises
480          * in real-world JS due to .prototype setting and collisions among a
481          * function's "static property" names and arg or var names, believe it
482          * or not.
483          */
484         origobj = obj;
485         do {
486             obj = OBJ_GET_PROTO(cx, obj);
487             if (!obj)
488                 return JS_TRUE;
489         } while (OBJ_GET_CLASS(cx, obj) != &js_FunctionClass);
491         JS_LOCK_OBJ(cx, obj);
492         scope = OBJ_SCOPE(obj);
493         for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
494             if (sprop->setter == setter) {
495                 JS_ASSERT(!JSVAL_IS_INT(sprop->id) &&
496                           ATOM_IS_STRING((JSAtom *)sprop->id) &&
497                           (sprop->flags & SPROP_HAS_SHORTID));
499                 if ((uintN) sprop->shortid == slot) {
500                     str = ATOM_TO_STRING((JSAtom *)sprop->id);
501                     JS_UNLOCK_SCOPE(cx, scope);
503                     return JS_DefineUCProperty(cx, origobj,
504                                                JSSTRING_CHARS(str),
505                                                JSSTRING_LENGTH(str),
506                                                v, NULL, NULL,
507                                                JSPROP_ENUMERATE);
508                 }
509             }
510         }
511         JS_UNLOCK_SCOPE(cx, scope);
512         return JS_TRUE;
513     }
515     /*
516      * Argument and local variable properties of function objects are shared
517      * by default (JSPROP_SHARED), therefore slot-less.  But if for function
518      * f(a) {}, f.a = 42 is evaluated, f.a should be 42 after the assignment,
519      * whether or not f is active.  So js_SetArgument and js_SetLocalVariable
520      * must be prepared to change an arg or var from shared to unshared status,
521      * allocating a slot in obj to hold v.
522      */
523     ok = JS_TRUE;
524     JS_LOCK_OBJ(cx, obj);
525     scope = OBJ_SCOPE(obj);
526     for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
527         if (sprop->setter == setter && (uintN) sprop->shortid == slot) {
528             if (sprop->attrs & JSPROP_SHARED) {
529                 sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop,
530                                                     0, ~JSPROP_SHARED,
531                                                     sprop->getter, setter);
532                 if (!sprop) {
533                     ok = JS_FALSE;
534                 } else {
535                     /* See js_SetProperty, near the bottom. */
536                     GC_POKE(cx, pval);
537                     LOCKED_OBJ_SET_SLOT(obj, sprop->slot, v);
538                 }
539             }
540             break;
541         }
542     }
543     JS_UNLOCK_SCOPE(cx, scope);
544     return ok;
547 JSBool
548 js_GetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
550     return JS_TRUE;
553 JSBool
554 js_SetArgument(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
556     return SetFunctionSlot(cx, obj, js_SetArgument, id, *vp);
559 JSBool
560 js_GetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
562     return JS_TRUE;
565 JSBool
566 js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
568     return SetFunctionSlot(cx, obj, js_SetLocalVariable, id, *vp);
571 /*
572  * Compute the 'this' parameter and store it in frame as frame.thisp.
573  * Activation objects ("Call" objects not created with "new Call()", i.e.,
574  * "Call" objects that have private data) may not be referred to by 'this',
575  * as dictated by ECMA.
576  *
577  * N.B.: fp->argv must be set, fp->argv[-1] the nominal 'this' paramter as
578  * a jsval, and fp->argv[-2] must be the callee object reference, usually a
579  * function object.  Also, fp->flags must contain JSFRAME_CONSTRUCTING if we
580  * are preparing for a constructor call.
581  */
582 static JSBool
583 ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
585     JSObject *parent;
587     if (thisp && OBJ_GET_CLASS(cx, thisp) != &js_CallClass) {
588         /* Some objects (e.g., With) delegate 'this' to another object. */
589         thisp = OBJ_THIS_OBJECT(cx, thisp);
590         if (!thisp)
591             return JS_FALSE;
593         /* Default return value for a constructor is the new object. */
594         if (fp->flags & JSFRAME_CONSTRUCTING)
595             fp->rval = OBJECT_TO_JSVAL(thisp);
596     } else {
597         /*
598          * ECMA requires "the global object", but in the presence of multiple
599          * top-level objects (windows, frames, or certain layers in the client
600          * object model), we prefer fun's parent.  An example that causes this
601          * code to run:
602          *
603          *   // in window w1
604          *   function f() { return this }
605          *   function g() { return f }
606          *
607          *   // in window w2
608          *   var h = w1.g()
609          *   alert(h() == w1)
610          *
611          * The alert should display "true".
612          */
613         JS_ASSERT(!(fp->flags & JSFRAME_CONSTRUCTING));
614         if (JSVAL_IS_PRIMITIVE(fp->argv[-2]) ||
615             !(parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2])))) {
616             thisp = cx->globalObject;
617         } else {
618             /* walk up to find the top-level object */
619             thisp = parent;
620             while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
621                 thisp = parent;
622         }
623     }
624     fp->thisp = thisp;
625     fp->argv[-1] = OBJECT_TO_JSVAL(thisp);
626     return JS_TRUE;
629 #ifdef DUMP_CALL_TABLE
631 #include "jsclist.h"
632 #include "jshash.h"
633 #include "jsdtoa.h"
635 typedef struct CallKey {
636     jsval               callee;                 /* callee value */
637     const char          *filename;              /* function filename or null */
638     uintN               lineno;                 /* function lineno or 0 */
639 } CallKey;
641 /* Compensate for typeof null == "object" brain damage. */
642 #define JSTYPE_NULL     JSTYPE_LIMIT
643 #define TYPEOF(cx,v)    (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v))
644 #define TYPENAME(t)     (((t) == JSTYPE_NULL) ? js_null_str : js_type_str[t])
645 #define NTYPEHIST       (JSTYPE_LIMIT + 1)
647 typedef struct CallValue {
648     uint32              total;                  /* total call count */
649     uint32              recycled;               /* LRU-recycled calls lost */
650     uint16              minargc;                /* minimum argument count */
651     uint16              maxargc;                /* maximum argument count */
652     struct ArgInfo {
653         uint32          typeHist[NTYPEHIST];    /* histogram by type */
654         JSCList         lruList;                /* top 10 values LRU list */
655         struct ArgValCount {
656             JSCList     lruLink;                /* LRU list linkage */
657             jsval       value;                  /* recently passed value */
658             uint32      count;                  /* number of times passed */
659             char        strbuf[112];            /* string conversion buffer */
660         } topValCounts[10];                     /* top 10 value storage */
661     } argInfo[8];
662 } CallValue;
664 typedef struct CallEntry {
665     JSHashEntry         entry;
666     CallKey             key;
667     CallValue           value;
668     char                name[32];               /* function name copy */
669 } CallEntry;
671 static void *
672 AllocCallTable(void *pool, size_t size)
674     return malloc(size);
677 static void
678 FreeCallTable(void *pool, void *item)
680     free(item);
683 static JSHashEntry *
684 AllocCallEntry(void *pool, const void *key)
686     return (JSHashEntry*) calloc(1, sizeof(CallEntry));
689 static void
690 FreeCallEntry(void *pool, JSHashEntry *he, uintN flag)
692     JS_ASSERT(flag == HT_FREE_ENTRY);
693     free(he);
696 static JSHashAllocOps callTableAllocOps = {
697     AllocCallTable, FreeCallTable,
698     AllocCallEntry, FreeCallEntry
699 };
701 JS_STATIC_DLL_CALLBACK(JSHashNumber)
702 js_hash_call_key(const void *key)
704     CallKey *ck = (CallKey *) key;
705     JSHashNumber hash = (jsuword)ck->callee >> 3;
707     if (ck->filename) {
708         hash = (hash << 4) ^ JS_HashString(ck->filename);
709         hash = (hash << 4) ^ ck->lineno;
710     }
711     return hash;
714 JS_STATIC_DLL_CALLBACK(intN)
715 js_compare_call_keys(const void *k1, const void *k2)
717     CallKey *ck1 = (CallKey *)k1, *ck2 = (CallKey *)k2;
719     return ck1->callee == ck2->callee &&
720            ((ck1->filename && ck2->filename)
721             ? strcmp(ck1->filename, ck2->filename) == 0
722             : ck1->filename == ck2->filename) &&
723            ck1->lineno == ck2->lineno;
726 JSHashTable *js_CallTable;
727 size_t      js_LogCallToSourceLimit;
729 JS_STATIC_DLL_CALLBACK(intN)
730 CallTableDumper(JSHashEntry *he, intN k, void *arg)
732     CallEntry *ce = (CallEntry *)he;
733     FILE *fp = (FILE *)arg;
734     uintN argc, i, n;
735     struct ArgInfo *ai;
736     JSType save, type;
737     JSCList *cl;
738     struct ArgValCount *avc;
739     jsval argval;
741     if (ce->key.filename) {
742         /* We're called at the end of the mark phase, so mark our filenames. */
743         js_MarkScriptFilename(ce->key.filename);
744         fprintf(fp, "%s:%u ", ce->key.filename, ce->key.lineno);
745     } else {
746         fprintf(fp, "@%p ", (void *) ce->key.callee);
747     }
749     if (ce->name[0])
750         fprintf(fp, "name %s ", ce->name);
751     fprintf(fp, "calls %lu (%lu) argc %u/%u\n",
752             (unsigned long) ce->value.total,
753             (unsigned long) ce->value.recycled,
754             ce->value.minargc, ce->value.maxargc);
756     argc = JS_MIN(ce->value.maxargc, 8);
757     for (i = 0; i < argc; i++) {
758         ai = &ce->value.argInfo[i];
760         n = 0;
761         save = -1;
762         for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
763             if (ai->typeHist[type]) {
764                 save = type;
765                 ++n;
766             }
767         }
768         if (n == 1) {
769             fprintf(fp, "  arg %u type %s: %lu\n",
770                     i, TYPENAME(save), (unsigned long) ai->typeHist[save]);
771         } else {
772             fprintf(fp, "  arg %u type histogram:\n", i);
773             for (type = JSTYPE_VOID; type <= JSTYPE_LIMIT; type++) {
774                 fprintf(fp, "  %9s: %8lu ",
775                        TYPENAME(type), (unsigned long) ai->typeHist[type]);
776                 for (n = (uintN) JS_HOWMANY(ai->typeHist[type], 10); n > 0; --n)
777                     fputc('*', fp);
778                 fputc('\n', fp);
779             }
780         }
782         fprintf(fp, "  arg %u top 10 values:\n", i);
783         n = 1;
784         for (cl = ai->lruList.prev; cl != &ai->lruList; cl = cl->prev) {
785             avc = (struct ArgValCount *)cl;
786             if (!avc->count)
787                 break;
788             argval = avc->value;
789             fprintf(fp, "  %9u: %8lu %.*s (%#lx)\n",
790                     n, (unsigned long) avc->count,
791                     sizeof avc->strbuf, avc->strbuf, argval);
792             ++n;
793         }
794     }
796     return HT_ENUMERATE_NEXT;
799 void
800 js_DumpCallTable(JSContext *cx)
802     char name[24];
803     FILE *fp;
804     static uintN dumpCount;
806     if (!js_CallTable)
807         return;
809     JS_snprintf(name, sizeof name, "/tmp/calltable.dump.%u", dumpCount & 7);
810     dumpCount++;
811     fp = fopen(name, "w");
812     if (!fp)
813         return;
815     JS_HashTableEnumerateEntries(js_CallTable, CallTableDumper, fp);
816     fclose(fp);
819 static void
820 LogCall(JSContext *cx, jsval callee, uintN argc, jsval *argv)
822     CallKey key;
823     const char *name, *cstr;
824     JSFunction *fun;
825     JSHashNumber keyHash;
826     JSHashEntry **hep, *he;
827     CallEntry *ce;
828     uintN i, j;
829     jsval argval;
830     JSType type;
831     struct ArgInfo *ai;
832     struct ArgValCount *avc;
833     JSString *str;
835     if (!js_CallTable) {
836         js_CallTable = JS_NewHashTable(1024, js_hash_call_key,
837                                        js_compare_call_keys, NULL,
838                                        &callTableAllocOps, NULL);
839         if (!js_CallTable)
840             return;
841     }
843     key.callee = callee;
844     key.filename = NULL;
845     key.lineno = 0;
846     name = "";
847     if (JSVAL_IS_FUNCTION(cx, callee)) {
848         fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(callee));
849         if (fun->atom)
850             name = js_AtomToPrintableString(cx, fun->atom);
851         if (fun->interpreted) {
852             key.filename = fun->u.script->filename;
853             key.lineno = fun->u.script->lineno;
854         }
855     }
856     keyHash = js_hash_call_key(&key);
858     hep = JS_HashTableRawLookup(js_CallTable, keyHash, &key);
859     he = *hep;
860     if (he) {
861         ce = (CallEntry *) he;
862         JS_ASSERT(strncmp(ce->name, name, sizeof ce->name) == 0);
863     } else {
864         he = JS_HashTableRawAdd(js_CallTable, hep, keyHash, &key, NULL);
865         if (!he)
866             return;
867         ce = (CallEntry *) he;
868         ce->entry.key = &ce->key;
869         ce->entry.value = &ce->value;
870         ce->key = key;
871         for (i = 0; i < 8; i++) {
872             ai = &ce->value.argInfo[i];
873             JS_INIT_CLIST(&ai->lruList);
874             for (j = 0; j < 10; j++)
875                 JS_APPEND_LINK(&ai->topValCounts[j].lruLink, &ai->lruList);
876         }
877         strncpy(ce->name, name, sizeof ce->name);
878     }
880     ++ce->value.total;
881     if (ce->value.minargc < argc)
882         ce->value.minargc = argc;
883     if (ce->value.maxargc < argc)
884         ce->value.maxargc = argc;
885     if (argc > 8)
886         argc = 8;
887     for (i = 0; i < argc; i++) {
888         ai = &ce->value.argInfo[i];
889         argval = argv[i];
890         type = TYPEOF(cx, argval);
891         ++ai->typeHist[type];
893         for (j = 0; ; j++) {
894             if (j == 10) {
895                 avc = (struct ArgValCount *) ai->lruList.next;
896                 ce->value.recycled += avc->count;
897                 avc->value = argval;
898                 avc->count = 1;
899                 break;
900             }
901             avc = &ai->topValCounts[j];
902             if (avc->value == argval) {
903                 ++avc->count;
904                 break;
905             }
906         }
908         /* Move avc to the back of the LRU list. */
909         JS_REMOVE_LINK(&avc->lruLink);
910         JS_APPEND_LINK(&avc->lruLink, &ai->lruList);
912         str = NULL;
913         cstr = "";
914         switch (TYPEOF(cx, argval)) {
915           case JSTYPE_VOID:
916             cstr = js_type_str[JSTYPE_VOID];
917             break;
918           case JSTYPE_NULL:
919             cstr = js_null_str;
920             break;
921           case JSTYPE_BOOLEAN:
922             cstr = js_boolean_str[JSVAL_TO_BOOLEAN(argval)];
923             break;
924           case JSTYPE_NUMBER:
925             if (JSVAL_IS_INT(argval)) {
926                 JS_snprintf(avc->strbuf, sizeof avc->strbuf, "%ld",
927                             JSVAL_TO_INT(argval));
928             } else {
929                 JS_dtostr(avc->strbuf, sizeof avc->strbuf, DTOSTR_STANDARD, 0,
930                           *JSVAL_TO_DOUBLE(argval));
931             }
932             continue;
933           case JSTYPE_STRING:
934             str = js_QuoteString(cx, JSVAL_TO_STRING(argval), (jschar)'"');
935             break;
936           case JSTYPE_FUNCTION:
937             if (JSVAL_IS_FUNCTION(cx, argval)) {
938                 fun = (JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(argval));
939                 if (fun && fun->atom) {
940                     str = ATOM_TO_STRING(fun->atom);
941                     break;
942                 }
943             }
944             /* FALL THROUGH */
945           case JSTYPE_OBJECT:
946             js_LogCallToSourceLimit = sizeof avc->strbuf;
947             cx->options |= JSOPTION_LOGCALL_TOSOURCE;
948             str = js_ValueToSource(cx, argval);
949             cx->options &= ~JSOPTION_LOGCALL_TOSOURCE;
950             break;
951         }
952         if (str)
953             cstr = JS_GetStringBytes(str);
954         strncpy(avc->strbuf, cstr, sizeof avc->strbuf);
955     }
958 #endif /* DUMP_CALL_TABLE */
960 /*
961  * Find a function reference and its 'this' object implicit first parameter
962  * under argc arguments on cx's stack, and call the function.  Push missing
963  * required arguments, allocate declared local variables, and pop everything
964  * when done.  Then push the return value.
965  */
966 JS_FRIEND_API(JSBool)
967 js_Invoke(JSContext *cx, uintN argc, uintN flags)
969     void *mark;
970     JSStackFrame *fp, frame;
971     jsval *sp, *newsp, *limit;
972     jsval *vp, v;
973     JSObject *funobj, *parent, *thisp;
974     JSBool ok;
975     JSClass *clasp;
976     JSObjectOps *ops;
977     JSNative native;
978     JSFunction *fun;
979     JSScript *script;
980     uintN minargs, nvars;
981     intN nslots, nalloc, surplus;
982     JSInterpreterHook hook;
983     void *hookData;
985     /* Mark the top of stack and load frequently-used registers. */
986     mark = JS_ARENA_MARK(&cx->stackPool);
987     fp = cx->fp;
988     sp = fp->sp;
990     /*
991      * Set vp to the callee value's stack slot (it's where rval goes).
992      * Once vp is set, control should flow through label out2: to return.
993      * Set frame.rval early so native class and object ops can throw and
994      * return false, causing a goto out2 with ok set to false.  Also set
995      * frame.flags to flags so that ComputeThis can test bits in it.
996      */
997     vp = sp - (2 + argc);
998     v = *vp;
999     frame.rval = JSVAL_VOID;
1000     frame.flags = flags;
1001     thisp = JSVAL_TO_OBJECT(vp[1]);
1003     /*
1004      * A callee must be an object reference, unless its |this| parameter
1005      * implements the __noSuchMethod__ method, in which case that method will
1006      * be called like so:
1007      *
1008      *   thisp.__noSuchMethod__(id, args)
1009      *
1010      * where id is the name of the method that this invocation attempted to
1011      * call by name, and args is an Array containing this invocation's actual
1012      * parameters.
1013      */
1014     if (JSVAL_IS_PRIMITIVE(v)) {
1015 #if JS_HAS_NO_SUCH_METHOD
1016         jsbytecode *pc;
1017         jsatomid atomIndex;
1018         JSAtom *atom;
1019         JSObject *argsobj;
1020         JSArena *a;
1022         if (!fp->script || (flags & JSINVOKE_INTERNAL))
1023             goto bad;
1025         /*
1026          * We must ComputeThis here to censor Call objects; performance hit,
1027          * but at least it's idempotent.
1028          *
1029          * Normally, we call ComputeThis after all frame members have been
1030          * set, and in particular, after any revision of the callee value at
1031          * *vp  due to clasp->convert (see below).  This matters because
1032          * ComputeThis may access *vp via fp->argv[-2], to follow the parent
1033          * chain to a global object to use as the |this| parameter.
1034          *
1035          * Obviously, here in the JSVAL_IS_PRIMITIVE(v) case, there can't be
1036          * any such defaulting of |this| to callee (v, *vp) ancestor.
1037          */
1038         frame.argv = vp + 2;
1039         ok = ComputeThis(cx, thisp, &frame);
1040         if (!ok)
1041             goto out2;
1042         thisp = frame.thisp;
1044         ok = OBJ_GET_PROPERTY(cx, thisp,
1045                               (jsid)cx->runtime->atomState.noSuchMethodAtom,
1046                               &v);
1047         if (!ok)
1048             goto out2;
1049         if (JSVAL_IS_PRIMITIVE(v))
1050             goto bad;
1052         pc = (jsbytecode *) vp[-(intN)fp->script->depth];
1053         switch ((JSOp) *pc) {
1054           case JSOP_NAME:
1055           case JSOP_GETPROP:
1056             atomIndex = GET_ATOM_INDEX(pc);
1057             atom = js_GetAtom(cx, &fp->script->atomMap, atomIndex);
1058             argsobj = js_NewArrayObject(cx, argc, vp + 2);
1059             if (!argsobj) {
1060                 ok = JS_FALSE;
1061                 goto out2;
1062             }
1064             sp = vp + 4;
1065             if (argc < 2) {
1066                 a = cx->stackPool.current;
1067                 if ((jsuword)sp > a->limit) {
1068                     /*
1069                      * Arguments must be contiguous, and must include argv[-1]
1070                      * and argv[-2], so allocate more stack, advance sp, and
1071                      * set newsp[1] to thisp (vp[1]).  The other argv elements
1072                      * will be set below, using negative indexing from sp.
1073                      */
1074                     newsp = js_AllocRawStack(cx, 4, NULL);
1075                     if (!newsp) {
1076                         ok = JS_FALSE;
1077                         goto out2;
1078                     }
1079                     newsp[1] = OBJECT_TO_JSVAL(thisp);
1080                     sp = newsp + 4;
1081                 } else if ((jsuword)sp > a->avail) {
1082                     /*
1083                      * Inline, optimized version of JS_ARENA_ALLOCATE to claim
1084                      * the small number of words not already allocated as part
1085                      * of the caller's operand stack.
1086                      */
1087                     JS_ArenaCountAllocation(&cx->stackPool,
1088                                             (jsuword)sp - a->avail);
1089                     a->avail = (jsuword)sp;
1090                 }
1091             }
1093             sp[-4] = v;
1094             JS_ASSERT(sp[-3] == OBJECT_TO_JSVAL(thisp));
1095             sp[-2] = ATOM_KEY(atom);
1096             sp[-1] = OBJECT_TO_JSVAL(argsobj);
1097             fp->sp = sp;
1098             argc = 2;
1099             break;
1101           default:
1102             goto bad;
1103         }
1104 #else
1105         goto bad;
1106 #endif
1107     }
1109     funobj = JSVAL_TO_OBJECT(v);
1110     parent = OBJ_GET_PARENT(cx, funobj);
1111     clasp = OBJ_GET_CLASS(cx, funobj);
1112     if (clasp != &js_FunctionClass) {
1113         /* Function is inlined, all other classes use object ops. */
1114         ops = funobj->map->ops;
1116         /*
1117          * XXX this makes no sense -- why convert to function if clasp->call?
1118          * XXX better to call that hook without converting
1119          * XXX the only thing that needs fixing is liveconnect
1120          *
1121          * Try converting to function, for closure and API compatibility.
1122          * We attempt the conversion under all circumstances for 1.2, but
1123          * only if there is a call op defined otherwise.
1124          */
1125         if (cx->version == JSVERSION_1_2 ||
1126             ((ops == &js_ObjectOps) ? clasp->call : ops->call)) {
1127             ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
1128             if (!ok)
1129                 goto out2;
1131             if (JSVAL_IS_FUNCTION(cx, v)) {
1132                 /* Make vp refer to funobj to keep it available as argv[-2]. */
1133                 *vp = v;
1134                 funobj = JSVAL_TO_OBJECT(v);
1135                 parent = OBJ_GET_PARENT(cx, funobj);
1136                 goto have_fun;
1137             }
1138         }
1139         fun = NULL;
1140         script = NULL;
1141         minargs = nvars = 0;
1143         /* Try a call or construct native object op. */
1144         native = (flags & JSINVOKE_CONSTRUCT) ? ops->construct : ops->call;
1145         if (!native)
1146             goto bad;
1147     } else {
1148 have_fun:
1149         /* Get private data and set derived locals from it. */
1150         fun = (JSFunction *) JS_GetPrivate(cx, funobj);
1151         if (fun->interpreted) {
1152             native = NULL;
1153             script = fun->u.script;
1154         } else {
1155             native = fun->u.native;
1156             script = NULL;
1157         }
1158         minargs = fun->nargs + fun->extra;
1159         nvars = fun->nvars;
1161         /* Handle bound method special case. */
1162         if (fun->flags & JSFUN_BOUND_METHOD)
1163             thisp = parent;
1164     }
1166     /* Initialize the rest of frame, except for sp (set by SAVE_SP later). */
1167     frame.varobj = NULL;
1168     frame.callobj = frame.argsobj = NULL;
1169     frame.script = script;
1170     frame.fun = fun;
1171     frame.argc = argc;
1172     frame.argv = sp - argc;
1173     frame.nvars = nvars;
1174     frame.vars = sp;
1175     frame.down = fp;
1176     frame.annotation = NULL;
1177     frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
1178     frame.pc = NULL;
1179     frame.spbase = NULL;
1180     frame.sharpDepth = 0;
1181     frame.sharpArray = NULL;
1182     frame.dormantNext = NULL;
1184     /* Compute the 'this' parameter and store it in frame as frame.thisp. */
1185     ok = ComputeThis(cx, thisp, &frame);
1186     if (!ok)
1187         goto out2;
1189     /* From here on, control must flow through label out: to return. */
1190     cx->fp = &frame;
1192     /* Init these now in case we goto out before first hook call. */
1193     hook = cx->runtime->callHook;
1194     hookData = NULL;
1196     /* Check for missing arguments expected by the function. */
1197     nslots = (intN)((argc < minargs) ? minargs - argc : 0);
1198     if (nslots) {
1199         /* All arguments must be contiguous, so we may have to copy actuals. */
1200         nalloc = nslots;
1201         limit = (jsval *) cx->stackPool.current->limit;
1202         if (sp + nslots > limit) {
1203             /* Hit end of arena: we have to copy argv[-2..(argc+nslots-1)]. */
1204             nalloc += 2 + argc;
1205         } else {
1206             /* Take advantage of surplus slots in the caller's frame depth. */
1207             surplus = (jsval *)mark - sp;
1208             JS_ASSERT(surplus >= 0);
1209             nalloc -= surplus;
1210         }
1212         /* Check whether we have enough space in the caller's frame. */
1213         if (nalloc > 0) {
1214             /* Need space for actuals plus missing formals minus surplus. */
1215             newsp = js_AllocRawStack(cx, (uintN)nalloc, NULL);
1216             if (!newsp) {
1217                 ok = JS_FALSE;
1218                 goto out;
1219             }
1221             /* If we couldn't allocate contiguous args, copy actuals now. */
1222             if (newsp != mark) {
1223                 JS_ASSERT(sp + nslots > limit);
1224                 JS_ASSERT(2 + argc + nslots == (uintN)nalloc);
1225                 *newsp++ = vp[0];
1226                 *newsp++ = vp[1];
1227                 if (argc)
1228                     memcpy(newsp, frame.argv, argc * sizeof(jsval));
1229                 frame.argv = newsp;
1230                 sp = frame.vars = newsp + argc;
1231             }
1232         }
1234         /* Advance frame.vars to make room for the missing args. */
1235         frame.vars += nslots;
1237         /* Push void to initialize missing args. */
1238         while (--nslots >= 0)
1239             PUSH(JSVAL_VOID);
1240     }
1242     /* Now allocate stack space for local variables. */
1243     nslots = (intN)frame.nvars;
1244     if (nslots) {
1245         surplus = (intN)((jsval *)cx->stackPool.current->avail - frame.vars);
1246         if (surplus < nslots) {
1247             newsp = js_AllocRawStack(cx, (uintN)nslots, NULL);
1248             if (!newsp) {
1249                 ok = JS_FALSE;
1250                 goto out;
1251             }
1252             if (newsp != sp) {
1253                 /* NB: Discontinuity between argv and vars. */
1254                 sp = frame.vars = newsp;
1255             }
1256         }
1258         /* Push void to initialize local variables. */
1259         while (--nslots >= 0)
1260             PUSH(JSVAL_VOID);
1261     }
1263     /* Store the current sp in frame before calling fun. */
1264     SAVE_SP(&frame);
1266     /* call the hook if present */
1267     if (hook && (native || script))
1268         hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->callHookData);
1270     /* Call the function, either a native method or an interpreted script. */
1271     if (native) {
1272 #if JS_HAS_LVALUE_RETURN
1273         /* Set by JS_SetCallReturnValue2, used to return reference types. */
1274         cx->rval2set = JS_FALSE;
1275 #endif
1277         /* If native, use caller varobj and scopeChain for eval. */
1278         frame.varobj = fp->varobj;
1279         frame.scopeChain = fp->scopeChain;
1280         ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1281         JS_RUNTIME_METER(cx->runtime, nativeCalls);
1282     } else if (script) {
1283 #ifdef DUMP_CALL_TABLE
1284         LogCall(cx, *vp, argc, frame.argv);
1285 #endif
1286         /* Use parent scope so js_GetCallObject can find the right "Call". */
1287         frame.scopeChain = parent;
1288         if (fun->flags & JSFUN_HEAVYWEIGHT) {
1289 #if JS_HAS_CALL_OBJECT
1290             /* Scope with a call object parented by the callee's parent. */
1291             if (!js_GetCallObject(cx, &frame, parent)) {
1292                 ok = JS_FALSE;
1293                 goto out;
1294             }
1295 #else
1296             /* Bad old code used the function as a proxy for all calls to it. */
1297             frame.scopeChain = funobj;
1298 #endif
1299         }
1300         ok = js_Interpret(cx, &v);
1301     } else {
1302         /* fun might be onerror trying to report a syntax error in itself. */
1303         frame.scopeChain = NULL;
1304         ok = JS_TRUE;
1305     }
1307 out:
1308     if (hookData) {
1309         hook = cx->runtime->callHook;
1310         if (hook)
1311             hook(cx, &frame, JS_FALSE, &ok, hookData);
1312     }
1313 #if JS_HAS_CALL_OBJECT
1314     /* If frame has a call object, sync values and clear back-pointer. */
1315     if (frame.callobj)
1316         ok &= js_PutCallObject(cx, &frame);
1317 #endif
1318 #if JS_HAS_ARGS_OBJECT
1319     /* If frame has an arguments object, sync values and clear back-pointer. */
1320     if (frame.argsobj)
1321         ok &= js_PutArgsObject(cx, &frame);
1322 #endif
1324     /* Restore cx->fp now that we're done releasing frame objects. */
1325     cx->fp = fp;
1327 out2:
1328     /* Pop everything we may have allocated off the stack. */
1329     JS_ARENA_RELEASE(&cx->stackPool, mark);
1331     /* Store the return value and restore sp just above it. */
1332     *vp = frame.rval;
1333     fp->sp = vp + 1;
1335     /*
1336      * Store the location of the JSOP_CALL or JSOP_EVAL that generated the
1337      * return value, but only if this is an external (compiled from script
1338      * source) call that has stack budget for the generating pc.
1339      */
1340     if (fp->script && !(flags & JSINVOKE_INTERNAL))
1341         vp[-(intN)fp->script->depth] = (jsval)fp->pc;
1342     return ok;
1344 bad:
1345     js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_CONSTRUCT);
1346     ok = JS_FALSE;
1347     goto out2;
1350 JSBool
1351 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
1352                   uintN argc, jsval *argv, jsval *rval)
1354     JSStackFrame *fp, *oldfp, frame;
1355     jsval *oldsp, *sp;
1356     void *mark;
1357     uintN i;
1358     JSBool ok;
1360     fp = oldfp = cx->fp;
1361     if (!fp) {
1362         memset(&frame, 0, sizeof frame);
1363         cx->fp = fp = &frame;
1364     }
1365     oldsp = fp->sp;
1366     sp = js_AllocStack(cx, 2 + argc, &mark);
1367     if (!sp) {
1368         ok = JS_FALSE;
1369         goto out;
1370     }
1372     PUSH(fval);
1373     PUSH(OBJECT_TO_JSVAL(obj));
1374     for (i = 0; i < argc; i++)
1375         PUSH(argv[i]);
1376     fp->sp = sp;
1377     ok = js_Invoke(cx, argc, flags | JSINVOKE_INTERNAL);
1378     if (ok) {
1379         RESTORE_SP(fp);
1380         *rval = POP_OPND();
1381     }
1383     js_FreeStack(cx, mark);
1384 out:
1385     fp->sp = oldsp;
1386     if (oldfp != fp)
1387         cx->fp = oldfp;
1389     return ok;
1392 JSBool
1393 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
1394                     JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
1396     /*
1397      * Check general (not object-ops/class-specific) access from the running
1398      * script to obj.id only if id has a scripted getter or setter that we're
1399      * about to invoke.  If we don't check this case, nothing else will -- no
1400      * other native code has the chance to check.
1401      *
1402      * Contrast this non-native (scripted) case with native getter and setter
1403      * accesses, where the native itself must do an access check, if security
1404      * policies requires it.  We make a checkAccess or checkObjectAccess call
1405      * back to the embedding program only in those cases where we're not going
1406      * to call an embedding-defined native function, getter, setter, or class
1407      * hook anyway.  Where we do call such a native, there's no need for the
1408      * engine to impose a separate access check callback on all embeddings --
1409      * many embeddings have no security policy at all.
1410      */
1411     JS_ASSERT(mode == JSACC_READ || mode == JSACC_WRITE);
1412     if (cx->runtime->checkObjectAccess &&
1413         JSVAL_IS_FUNCTION(cx, fval) &&
1414         ((JSFunction *)JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)))->interpreted &&
1415         !cx->runtime->checkObjectAccess(cx, obj, ID_TO_VALUE(id), mode,
1416                                         &fval)) {
1417         return JS_FALSE;
1418     }
1420     return js_InternalCall(cx, obj, fval, argc, argv, rval);
1423 JSBool
1424 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
1425            JSStackFrame *down, uintN flags, jsval *result)
1427     JSInterpreterHook hook;
1428     void *hookData, *mark;
1429     JSStackFrame *oldfp, frame;
1430     JSObject *obj, *tmp;
1431     JSBool ok;
1433     hook = cx->runtime->executeHook;
1434     hookData = mark = NULL;
1435     oldfp = cx->fp;
1436     frame.callobj = frame.argsobj = NULL;
1437     frame.script = script;
1438     if (down) {
1439         /* Propagate arg/var state for eval and the debugger API. */
1440         frame.varobj = down->varobj;
1441         frame.fun = down->fun;
1442         frame.thisp = down->thisp;
1443         frame.argc = down->argc;
1444         frame.argv = down->argv;
1445         frame.nvars = down->nvars;
1446         frame.vars = down->vars;
1447         frame.annotation = down->annotation;
1448         frame.sharpArray = down->sharpArray;
1449     } else {
1450         obj = chain;
1451         if (cx->options & JSOPTION_VAROBJFIX) {
1452             while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1453                 obj = tmp;
1454         }
1455         frame.varobj = obj;
1456         frame.fun = NULL;
1457         frame.thisp = chain;
1458         frame.argc = 0;
1459         frame.argv = NULL;
1460         frame.nvars = script->numGlobalVars;
1461         if (frame.nvars) {
1462             frame.vars = js_AllocRawStack(cx, frame.nvars, &mark);
1463             if (!frame.vars)
1464                 return JS_FALSE;
1465             memset(frame.vars, 0, frame.nvars * sizeof(jsval));
1466         } else {
1467             frame.vars = NULL;
1468         }
1469         frame.annotation = NULL;
1470         frame.sharpArray = NULL;
1471     }
1472     frame.rval = JSVAL_VOID;
1473     frame.down = down;
1474     frame.scopeChain = chain;
1475     frame.pc = NULL;
1476     frame.sp = oldfp ? oldfp->sp : NULL;
1477     frame.spbase = NULL;
1478     frame.sharpDepth = 0;
1479     frame.flags = flags;
1480     frame.dormantNext = NULL;
1482     /*
1483      * Here we wrap the call to js_Interpret with code to (conditionally)
1484      * save and restore the old stack frame chain into a chain of 'dormant'
1485      * frame chains.  Since we are replacing cx->fp, we were running into
1486      * the problem that if GC was called under this frame, some of the GC
1487      * things associated with the old frame chain (available here only in
1488      * the C variable 'oldfp') were not rooted and were being collected.
1489      *
1490      * So, now we preserve the links to these 'dormant' frame chains in cx
1491      * before calling js_Interpret and cleanup afterwards.  The GC walks
1492      * these dormant chains and marks objects in the same way that it marks
1493      * objects in the primary cx->fp chain.
1494      */
1495     if (oldfp && oldfp != down) {
1496         JS_ASSERT(!oldfp->dormantNext);
1497         oldfp->dormantNext = cx->dormantFrameChain;
1498         cx->dormantFrameChain = oldfp;
1499     }
1501     cx->fp = &frame;
1502     if (hook)
1503         hookData = hook(cx, &frame, JS_TRUE, 0, cx->runtime->executeHookData);
1505     /*
1506      * Use frame.rval, not result, so the last result stays rooted across any
1507      * GC activations nested within this js_Interpret.
1508      */
1509     ok = js_Interpret(cx, &frame.rval);
1510     *result = frame.rval;
1512     if (hookData) {
1513         hook = cx->runtime->executeHook;
1514         if (hook)
1515             hook(cx, &frame, JS_FALSE, &ok, hookData);
1516     }
1517     if (mark)
1518         js_FreeRawStack(cx, mark);
1519     cx->fp = oldfp;
1521     if (oldfp && oldfp != down) {
1522         JS_ASSERT(cx->dormantFrameChain == oldfp);
1523         cx->dormantFrameChain = oldfp->dormantNext;
1524         oldfp->dormantNext = NULL;
1525     }
1527     return ok;
1530 #if JS_HAS_EXPORT_IMPORT
1531 /*
1532  * If id is JSVAL_VOID, import all exported properties from obj.
1533  */
1534 static JSBool
1535 ImportProperty(JSContext *cx, JSObject *obj, jsid id)
1537     JSBool ok;
1538     JSIdArray *ida;
1539     JSProperty *prop;
1540     JSObject *obj2, *target, *funobj, *closure;
1541     JSString *str;
1542     uintN attrs;
1543     jsint i;
1544     jsval value;
1546     if (JSVAL_IS_VOID(id)) {
1547         ida = JS_Enumerate(cx, obj);
1548         if (!ida)
1549             return JS_FALSE;
1550         ok = JS_TRUE;
1551         if (ida->length == 0)
1552             goto out;
1553     } else {
1554         ida = NULL;
1555         if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1556             return JS_FALSE;
1557         if (!prop) {
1558             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1559                                              ID_TO_VALUE(id), NULL);
1560             if (str)
1561                 js_ReportIsNotDefined(cx, JS_GetStringBytes(str));
1562             return JS_FALSE;
1563         }
1564         ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
1565         OBJ_DROP_PROPERTY(cx, obj2, prop);
1566         if (!ok)
1567             return JS_FALSE;
1568         if (!(attrs & JSPROP_EXPORTED)) {
1569             str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
1570                                              ID_TO_VALUE(id), NULL);
1571             if (str) {
1572                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1573                                      JSMSG_NOT_EXPORTED,
1574                                      JS_GetStringBytes(str));
1575             }
1576             return JS_FALSE;
1577         }
1578     }
1580     target = cx->fp->varobj;
1581     i = 0;
1582     do {
1583         if (ida) {
1584             id = ida->vector[i];
1585             ok = OBJ_GET_ATTRIBUTES(cx, obj, id, NULL, &attrs);
1586             if (!ok)
1587                 goto out;
1588             if (!(attrs & JSPROP_EXPORTED))
1589                 continue;
1590         }
1591         ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_IMPORT, &value, &attrs);
1592         if (!ok)
1593             goto out;
1594         if (JSVAL_IS_FUNCTION(cx, value)) {
1595             funobj = JSVAL_TO_OBJECT(value);
1596             closure = js_CloneFunctionObject(cx, funobj, obj);
1597             if (!closure) {
1598                 ok = JS_FALSE;
1599                 goto out;
1600             }
1601             value = OBJECT_TO_JSVAL(closure);
1602         }
1604         /*
1605          * Handle the case of importing a property that refers to a local
1606          * variable or formal parameter of a function activation.  These
1607          * properties are accessed by opcodes using stack slot numbers
1608          * generated by the compiler rather than runtime name-lookup.  These
1609          * local references, therefore, bypass the normal scope chain lookup.
1610          * So, instead of defining a new property in the activation object,
1611          * modify the existing value in the stack slot.
1612          */
1613         if (OBJ_GET_CLASS(cx, target) == &js_CallClass) {
1614             ok = OBJ_LOOKUP_PROPERTY(cx, target, id, &obj2, &prop);
1615             if (!ok)
1616                 goto out;
1617         } else {
1618             prop = NULL;
1619         }
1620         if (prop && target == obj2) {
1621             ok = OBJ_SET_PROPERTY(cx, target, id, &value);
1622         } else {
1623             ok = OBJ_DEFINE_PROPERTY(cx, target, id, value, NULL, NULL,
1624                                      attrs & ~JSPROP_EXPORTED,
1625                                      NULL);
1626         }
1627         if (prop)
1628             OBJ_DROP_PROPERTY(cx, obj2, prop);
1629         if (!ok)
1630             goto out;
1631     } while (ida && ++i < ida->length);
1633 out:
1634     if (ida)
1635         JS_DestroyIdArray(cx, ida);
1636     return ok;
1638 #endif /* JS_HAS_EXPORT_IMPORT */
1640 JSBool
1641 js_CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
1642                       JSObject **objp, JSProperty **propp)
1644     JSObject *obj2;
1645     JSProperty *prop;
1646     uintN oldAttrs, report;
1647     JSBool isFunction;
1648     jsval value;
1649     const char *type, *name;
1651     if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1652         return JS_FALSE;
1653     if (propp) {
1654         *objp = obj2;
1655         *propp = prop;
1656     }
1657     if (!prop)
1658         return JS_TRUE;
1660     /* From here, return true, or goto bad on failure to drop prop. */
1661     if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs))
1662         goto bad;
1664     /* If either property is readonly, we have an error. */
1665     report = ((oldAttrs | attrs) & JSPROP_READONLY)
1666              ? JSREPORT_ERROR
1667              : JSREPORT_WARNING | JSREPORT_STRICT;
1669     if (report != JSREPORT_ERROR) {
1670         /*
1671          * Allow redeclaration of variables and functions, but insist that the
1672          * new value is not a getter if the old value was, ditto for setters --
1673          * unless prop is impermanent (in which case anyone could delete it and
1674          * redefine it, willy-nilly).
1675          */
1676         if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1677             return JS_TRUE;
1678         if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1679             return JS_TRUE;
1680         if (!(oldAttrs & JSPROP_PERMANENT))
1681             return JS_TRUE;
1682         report = JSREPORT_ERROR;
1683     }
1685     isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1686     if (!isFunction) {
1687         if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1688             goto bad;
1689         isFunction = JSVAL_IS_FUNCTION(cx, value);
1690     }
1691     type = (oldAttrs & attrs & JSPROP_GETTER)
1692            ? js_getter_str
1693            : (oldAttrs & attrs & JSPROP_SETTER)
1694            ? js_setter_str
1695            : (oldAttrs & JSPROP_READONLY)
1696            ? js_const_str
1697            : isFunction
1698            ? js_function_str
1699            : js_var_str;
1700     name = js_AtomToPrintableString(cx, (JSAtom *)id);
1701     if (!name)
1702         goto bad;
1703     return JS_ReportErrorFlagsAndNumber(cx, report,
1704                                         js_GetErrorMessage, NULL,
1705                                         JSMSG_REDECLARED_VAR,
1706                                         type, name);
1708 bad:
1709     if (propp) {
1710         *objp = NULL;
1711         *propp = NULL;
1712     }
1713     OBJ_DROP_PROPERTY(cx, obj2, prop);
1714     return JS_FALSE;
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, 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 *pc, *pc2, *endpc;
1743     JSOp op, op2;
1744     const JSCodeSpec *cs;
1745     JSAtom *atom;
1746     uintN argc, slot, attrs;
1747     jsval *vp, lval, rval, ltmp, rtmp;
1748     jsid id;
1749     JSObject *withobj, *origobj, *propobj;
1750     jsval iter_state;
1751     JSProperty *prop;
1752     JSScopeProperty *sprop;
1753     JSString *str, *str2;
1754     jsint i, j;
1755     jsdouble d, d2;
1756     JSClass *clasp, *funclasp;
1757     JSFunction *fun;
1758     JSType type;
1759 #ifdef DEBUG
1760     FILE *tracefp;
1761 #endif
1762 #if JS_HAS_EXPORT_IMPORT
1763     JSIdArray *ida;
1764 #endif
1765 #if JS_HAS_SWITCH_STATEMENT
1766     jsint low, high, off, npairs;
1767     JSBool match;
1768 #endif
1769 #if JS_HAS_GETTER_SETTER
1770     JSPropertyOp getter, setter;
1771 #endif
1772     int stackDummy;
1774     *result = JSVAL_VOID;
1775     rt = cx->runtime;
1777     /* Set registerized frame pointer and derived script pointer. */
1778     fp = cx->fp;
1779     script = fp->script;
1781     /* Count of JS function calls that nest in this C js_Interpret frame. */
1782     inlineCallCount = 0;
1784     /*
1785      * Optimized Get and SetVersion for proper script language versioning.
1786      *
1787      * If any native method or JSClass/JSObjectOps hook calls JS_SetVersion
1788      * and changes cx->version, the effect will "stick" and we will stop
1789      * maintaining currentVersion.  This is relied upon by testsuites, for
1790      * the most part -- web browsers select version before compiling and not
1791      * at run-time.
1792      */
1793     currentVersion = script->version;
1794     originalVersion = cx->version;
1795     if (currentVersion != originalVersion)
1796         JS_SetVersion(cx, currentVersion);
1798     /*
1799      * Prepare to call a user-supplied branch handler, and abort the script
1800      * if it returns false.  We reload onbranch after calling out to native
1801      * functions (but not to getters, setters, or other native hooks).
1802      */
1803 #define LOAD_BRANCH_CALLBACK(cx)    (onbranch = (cx)->branchCallback)
1805     LOAD_BRANCH_CALLBACK(cx);
1806     ok = JS_TRUE;
1807 #define CHECK_BRANCH(len)                                                     \
1808     JS_BEGIN_MACRO                                                            \
1809         if (len <= 0 && onbranch) {                                           \
1810             SAVE_SP(fp);                                                      \
1811             if (!(ok = (*onbranch)(cx, script)))                              \
1812                 goto out;                                                     \
1813         }                                                                     \
1814     JS_END_MACRO
1816     /*
1817      * Load the debugger's interrupt hook here and after calling out to native
1818      * functions (but not to getters, setters, or other native hooks), so we do
1819      * not have to reload it each time through the interpreter loop -- we hope
1820      * the compiler can keep it in a register.
1821      * XXX if it spills, we still lose
1822      */
1823 #define LOAD_INTERRUPT_HANDLER(rt)  (interruptHandler = (rt)->interruptHandler)
1825     LOAD_INTERRUPT_HANDLER(rt);
1827     pc = script->code;
1828     endpc = pc + script->length;
1829     depth = (jsint) script->depth;
1830     len = -1;
1832     /* Check for too much js_Interpret nesting, or too deep a C stack. */
1833     if (++cx->interpLevel == MAX_INTERP_LEVEL ||
1834         !JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1835         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
1836         ok = JS_FALSE;
1837         goto out;
1838     }
1840     /*
1841      * Allocate operand and pc stack slots for the script's worst-case depth.
1842      */
1843     newsp = js_AllocRawStack(cx, (uintN)(2 * depth), &mark);
1844     if (!newsp) {
1845         ok = JS_FALSE;
1846         goto out;
1847     }
1848     sp = newsp + depth;
1849     fp->spbase = sp;
1850     SAVE_SP(fp);
1852     while (pc < endpc) {
1853         fp->pc = pc;
1854         op = (JSOp) *pc;
1855       do_op:
1856         cs = &js_CodeSpec[op];
1857         len = cs->length;
1859 #ifdef DEBUG
1860         tracefp = (FILE *) cx->tracefp;
1861         if (tracefp) {
1862             intN nuses, n;
1864             fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc));
1865             js_Disassemble1(cx, script, pc,
1866                             PTRDIFF(pc, script->code, jsbytecode), JS_FALSE,
1867                             tracefp);
1868             nuses = cs->nuses;
1869             if (nuses) {
1870                 SAVE_SP(fp);
1871                 for (n = -nuses; n < 0; n++) {
1872                     str = js_DecompileValueGenerator(cx, n, sp[n], NULL);
1873                     if (str != NULL) {
1874                         fprintf(tracefp, "%s %s",
1875                                 (n == -nuses) ? "  inputs:" : ",",
1876                                 JS_GetStringBytes(str));
1877                     }
1878                 }
1879                 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
1880             }
1881         }
1882 #endif
1884         if (interruptHandler) {
1885             SAVE_SP(fp);
1886             switch (interruptHandler(cx, script, pc, &rval,
1887                                      rt->interruptHandlerData)) {
1888               case JSTRAP_ERROR:
1889                 ok = JS_FALSE;
1890                 goto out;
1891               case JSTRAP_CONTINUE:
1892                 break;
1893               case JSTRAP_RETURN:
1894                 fp->rval = rval;
1895                 goto out;
1896 #if JS_HAS_EXCEPTIONS
1897               case JSTRAP_THROW:
1898                 cx->throwing = JS_TRUE;
1899                 cx->exception = rval;
1900                 ok = JS_FALSE;
1901                 goto out;
1902 #endif /* JS_HAS_EXCEPTIONS */
1903               default:;
1904             }
1905             LOAD_INTERRUPT_HANDLER(rt);
1906         }
1908         switch (op) {
1909           case JSOP_NOP:
1910             break;
1912           case JSOP_GROUP:
1913             obj = NULL;
1914             break;
1916           case JSOP_PUSH:
1917             PUSH_OPND(JSVAL_VOID);
1918             break;
1920           case JSOP_POP:
1921             sp--;
1922             break;
1924           case JSOP_POP2:
1925             sp -= 2;
1926             break;
1928           case JSOP_SWAP:
1929             /*
1930              * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs
1931              * for the operands it swaps.
1932              */
1933             ltmp = sp[-1];
1934             sp[-1] = sp[-2];
1935             sp[-2] = ltmp;
1936             break;
1938           case JSOP_POPV:
1939             *result = POP_OPND();
1940             break;
1942           case JSOP_ENTERWITH:
1943             rval = FETCH_OPND(-1);
1944             VALUE_TO_OBJECT(cx, rval, obj);
1945             withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain);
1946             if (!withobj)
1947                 goto out;
1948             fp->scopeChain = withobj;
1949             STORE_OPND(-1, OBJECT_TO_JSVAL(withobj));
1950             break;
1952           case JSOP_LEAVEWITH:
1953             rval = POP_OPND();
1954             JS_ASSERT(JSVAL_IS_OBJECT(rval));
1955             withobj = JSVAL_TO_OBJECT(rval);
1956             JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1958             rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT);
1959             JS_ASSERT(JSVAL_IS_OBJECT(rval));
1960             fp->scopeChain = JSVAL_TO_OBJECT(rval);
1961             break;
1963           case JSOP_SETRVAL:
1964             fp->rval = POP_OPND();
1965             break;
1967           case JSOP_RETURN:
1968             CHECK_BRANCH(-1);
1969             fp->rval = POP_OPND();
1970             /* FALL THROUGH */
1972           case JSOP_RETRVAL:    /* fp->rval already set */
1973             if (inlineCallCount)
1974           inline_return:
1975             {
1976                 JSInlineFrame *ifp = (JSInlineFrame *) fp;
1977                 void *hookData = ifp->hookData;
1979                 if (hookData) {
1980                     JSInterpreterHook hook = cx->runtime->callHook;
1981                     if (hook) {
1982                         hook(cx, fp, JS_FALSE, &ok, hookData);
1983                         LOAD_INTERRUPT_HANDLER(rt);
1984                     }
1985                 }
1986 #if JS_HAS_ARGS_OBJECT
1987                 if (fp->argsobj)
1988                     ok &= js_PutArgsObject(cx, fp);
1989 #endif
1991                 /* Restore context version only if callee hasn't set version. */
1992                 if (cx->version == currentVersion) {
1993                     currentVersion = ifp->callerVersion;
1994                     if (currentVersion != cx->version)
1995                         JS_SetVersion(cx, currentVersion);
1996                 }
1998                 /* Store the return value in the caller's operand frame. */
1999                 vp = fp->argv - 2;
2000                 *vp = fp->rval;
2002                 /* Restore cx->fp and release the inline frame's space. */
2003                 cx->fp = fp = fp->down;
2004                 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
2006                 /* Restore sp to point just above the return value. */
2007                 fp->sp = vp + 1;
2008                 RESTORE_SP(fp);
2010                 /* Restore the calling script's interpreter registers. */
2011                 script = fp->script;
2012                 depth = (jsint) script->depth;
2013                 pc = fp->pc;
2014                 endpc = script->code + script->length;
2016                 /* Store the generating pc for the return value. */
2017                 vp[-depth] = (jsval)pc;
2019                 /* Set remaining variables for 'goto advance_pc'. */
2020                 op = (JSOp) *pc;
2021                 cs = &js_CodeSpec[op];
2022                 len = cs->length;
2024                 /* Resume execution in the calling frame. */
2025                 inlineCallCount--;
2026                 if (ok)
2027                     goto advance_pc;
2028             }
2029             goto out;
2031 #if JS_HAS_SWITCH_STATEMENT
2032           case JSOP_DEFAULT:
2033             (void) POP();
2034             /* FALL THROUGH */
2035 #endif
2036           case JSOP_GOTO:
2037             len = GET_JUMP_OFFSET(pc);
2038             CHECK_BRANCH(len);
2039             break;
2041           case JSOP_IFEQ:
2042             POP_BOOLEAN(cx, rval, cond);
2043             if (cond == JS_FALSE) {
2044                 len = GET_JUMP_OFFSET(pc);
2045                 CHECK_BRANCH(len);
2046             }
2047             break;
2049           case JSOP_IFNE:
2050             POP_BOOLEAN(cx, rval, cond);
2051             if (cond != JS_FALSE) {
2052                 len = GET_JUMP_OFFSET(pc);
2053                 CHECK_BRANCH(len);
2054             }
2055             break;
2057           case JSOP_OR:
2058             POP_BOOLEAN(cx, rval, cond);
2059             if (cond == JS_TRUE) {
2060                 len = GET_JUMP_OFFSET(pc);
2061                 PUSH_OPND(rval);
2062             }
2063             break;
2065           case JSOP_AND:
2066             POP_BOOLEAN(cx, rval, cond);
2067             if (cond == JS_FALSE) {
2068                 len = GET_JUMP_OFFSET(pc);
2069                 PUSH_OPND(rval);
2070             }
2071             break;
2074 #if JS_HAS_SWITCH_STATEMENT
2075           case JSOP_DEFAULTX:
2076             (void) POP();
2077             /* FALL THROUGH */
2078 #endif
2079           case JSOP_GOTOX:
2080             len = GET_JUMPX_OFFSET(pc);
2081             CHECK_BRANCH(len);
2082             break;
2084           case JSOP_IFEQX:
2085             POP_BOOLEAN(cx, rval, cond);
2086             if (cond == JS_FALSE) {
2087                 len = GET_JUMPX_OFFSET(pc);
2088                 CHECK_BRANCH(len);
2089             }
2090             break;
2092           case JSOP_IFNEX:
2093             POP_BOOLEAN(cx, rval, cond);
2094             if (cond != JS_FALSE) {
2095                 len = GET_JUMPX_OFFSET(pc);
2096                 CHECK_BRANCH(len);
2097             }
2098             break;
2100           case JSOP_ORX:
2101             POP_BOOLEAN(cx, rval, cond);
2102             if (cond == JS_TRUE) {
2103                 len = GET_JUMPX_OFFSET(pc);
2104                 PUSH_OPND(rval);
2105             }
2106             break;
2108           case JSOP_ANDX:
2109             POP_BOOLEAN(cx, rval, cond);
2110             if (cond == JS_FALSE) {
2111                 len = GET_JUMPX_OFFSET(pc);
2112                 PUSH_OPND(rval);
2113             }
2114             break;
2116           case JSOP_TOOBJECT:
2117             SAVE_SP(fp);
2118             ok = js_ValueToObject(cx, FETCH_OPND(-1), &obj);
2119             if (!ok)
2120                 goto out;
2121             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
2122             break;
2124 #define FETCH_ELEMENT_ID(n, id)                                               \
2125     JS_BEGIN_MACRO                                                            \
2126         /* If the index is not a jsint, atomize it. */                        \
2127         id = (jsid) FETCH_OPND(n);                                            \
2128         if (JSVAL_IS_INT(id)) {                                               \
2129             atom = NULL;                                                      \
2130         } else {                                                              \
2131             SAVE_SP(fp);                                                      \
2132             atom = js_ValueToStringAtom(cx, (jsval)id);                       \
2133             if (!atom) {                                                      \
2134                 ok = JS_FALSE;                                                \
2135                 goto out;                                                     \
2136             }                                                                 \
2137             id = (jsid)atom;                                                  \
2138         }                                                                     \
2139     JS_END_MACRO
2141 #define POP_ELEMENT_ID(id)                                                    \
2142     JS_BEGIN_MACRO                                                            \
2143         FETCH_ELEMENT_ID(-1, id);                                             \
2144         sp--;                                                                 \
2145     JS_END_MACRO
2147 #if JS_HAS_IN_OPERATOR
2148           case JSOP_IN:
2149             rval = FETCH_OPND(-1);
2150             if (JSVAL_IS_PRIMITIVE(rval)) {
2151                 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
2152                 if (str) {
2153                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2154                                          JSMSG_IN_NOT_OBJECT,
2155                                          JS_GetStringBytes(str));
2156                 }
2157                 ok = JS_FALSE;
2158                 goto out;
2159             }
2160             sp--;
2161             obj = JSVAL_TO_OBJECT(rval);
2162             FETCH_ELEMENT_ID(-1, id);
2163             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
2164             if (!ok)
2165                 goto out;
2166             STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
2167             if (prop)
2168                 OBJ_DROP_PROPERTY(cx, obj2, prop);
2169             break;
2170 #endif /* JS_HAS_IN_OPERATOR */
2172           case JSOP_FORPROP:
2173             /*
2174              * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop
2175              * is not paid for the more common cases.
2176              */
2177             lval = FETCH_OPND(-1);
2178             atom = GET_ATOM(cx, script, pc);
2179             id   = (jsid)atom;
2180             i = -2;
2181             goto do_forinloop;
2183           case JSOP_FORNAME:
2184             atom = GET_ATOM(cx, script, pc);
2185             id   = (jsid)atom;
2187             /*
2188              * ECMA 12.6.3 says to eval the LHS after looking for properties
2189              * to enumerate, and bail without LHS eval if there are no props.
2190              * We do Find here to share the most code at label do_forinloop.
2191              * If looking for enumerable properties could have side effects,
2192              * then we'd have to move this into the common code and condition
2193              * it on op == JSOP_FORNAME.
2194              */
2195             SAVE_SP(fp);
2196             ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2197             if (!ok)
2198                 goto out;
2199             if (prop)
2200                 OBJ_DROP_PROPERTY(cx, obj2, prop);
2201             lval = OBJECT_TO_JSVAL(obj);
2202             /* FALL THROUGH */
2204           case JSOP_FORARG:
2205           case JSOP_FORVAR:
2206             /*
2207              * JSOP_FORARG and JSOP_FORVAR don't require any lval computation
2208              * here, because they address slots on the stack (in fp->args and
2209              * fp->vars, respectively).
2210              */
2211             /* FALL THROUGH */
2213           case JSOP_FORELEM:
2214             /*
2215              * JSOP_FORELEM simply initializes or updates the iteration state
2216              * and leaves the index expression evaluation and assignment to the
2217              * enumerator until after the next property has been acquired, via
2218              * a JSOP_ENUMELEM bytecode.
2219              */
2220             i = -1;
2222           do_forinloop:
2223             /*
2224              * ECMA-compatible for/in evals the object just once, before loop.
2225              * Bad old bytecodes (since removed) did it on every iteration.
2226              */
2227             obj = JSVAL_TO_OBJECT(sp[i]);
2229             /* If the thing to the right of 'in' has no properties, break. */
2230             if (!obj) {
2231                 rval = JSVAL_FALSE;
2232                 goto end_forinloop;
2233             }
2235             /*
2236              * Save the thing to the right of 'in' as origobj.  Later on, we
2237              * use this variable to suppress enumeration of shadowed prototype
2238              * properties.
2239              */
2240             origobj = obj;
2242             /*
2243              * Reach under the top of stack to find our property iterator, a
2244              * JSObject that contains the iteration state.  (An object is used
2245              * rather than a native struct so that the iteration state is
2246              * cleaned up via GC if the for-in loop terminates abruptly.)
2247              */
2248             vp = &sp[i - 1];
2249             rval = *vp;
2251             /* Is this the first iteration ? */
2252             if (JSVAL_IS_VOID(rval)) {
2253                 /* Yes, create a new JSObject to hold the iterator state */
2254                 propobj = js_NewObject(cx, &prop_iterator_class, NULL, obj);
2255                 if (!propobj) {
2256                     ok = JS_FALSE;
2257                     goto out;
2258                 }
2259                 propobj->slots[JSSLOT_ITER_STATE] = JSVAL_NULL;
2261                 /*
2262                  * Root the parent slot so we can get it even in our finalizer
2263                  * (otherwise, it would live as long as we do, but it might be
2264                  * finalized first).
2265                  */
2266                 ok = js_AddRoot(cx, &propobj->slots[JSSLOT_PARENT],
2267                                 "propobj->parent");
2268                 if (!ok)
2269                     goto out;
2271                 /*
2272                  * Rewrite the iterator so we know to do the next case.
2273                  * Do this before calling the enumerator, which could
2274                  * displace cx->newborn and cause GC.
2275                  */
2276                 *vp = OBJECT_TO_JSVAL(propobj);
2278                 ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0);
2280                 /*
2281                  * Stash private iteration state into property iterator object.
2282                  * We do this before checking 'ok' to ensure that propobj is
2283                  * in a valid state even if OBJ_ENUMERATE returned JS_FALSE.
2284                  * NB: This code knows that the first slots are pre-allocated.
2285                  */
2286 #if JS_INITIAL_NSLOTS < 5
2287 #error JS_INITIAL_NSLOTS must be greater than or equal to 5.
2288 #endif
2289                 propobj->slots[JSSLOT_ITER_STATE] = iter_state;
2290                 if (!ok)
2291                     goto out;
2292             } else {
2293                 /* This is not the first iteration. Recover iterator state. */
2294                 propobj = JSVAL_TO_OBJECT(rval);
2295                 JS_ASSERT(OBJ_GET_CLASS(cx, propobj) == &prop_iterator_class);
2296                 obj = JSVAL_TO_OBJECT(propobj->slots[JSSLOT_PARENT]);
2297                 iter_state = propobj->slots[JSSLOT_ITER_STATE];
2298             }
2300           enum_next_property:
2301             /* Get the next jsid to be enumerated and store it in rval. */
2302             OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &iter_state, &rval);
2303             propobj->slots[JSSLOT_ITER_STATE] = iter_state;
2305             /* No more jsids to iterate in obj? */
2306             if (iter_state == JSVAL_NULL) {
2307                 /* Enumerate the properties on obj's prototype chain. */
2308                 obj = OBJ_GET_PROTO(cx, obj);
2309                 if (!obj) {
2310                     /* End of property list -- terminate loop. */
2311                     rval = JSVAL_FALSE;
2312                     goto end_forinloop;
2313                 }
2315                 ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, 0);
2317                 /*
2318                  * Stash private iteration state into property iterator object.
2319                  * We do this before checking 'ok' to ensure that propobj is
2320                  * in a valid state even if OBJ_ENUMERATE returned JS_FALSE.
2321                  * NB: This code knows that the first slots are pre-allocated.
2322                  */
2323                 propobj->slots[JSSLOT_ITER_STATE] = iter_state;
2324                 if (!ok)
2325                     goto out;
2327                 /*
2328                  * Update the iterator JSObject's parent link to refer to the
2329                  * current object. This is used in the iterator JSObject's
2330                  * finalizer.
2331                  */
2332                 propobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj);
2333                 goto enum_next_property;
2334             }
2336             /* Skip properties not owned by obj, and leave next id in rval. */
2337             ok = OBJ_LOOKUP_PROPERTY(cx, origobj, rval, &obj2, &prop);
2338             if (!ok)
2339                 goto out;
2340             if (prop) {
2341                 OBJ_DROP_PROPERTY(cx, obj2, prop);
2343                 /* Yes, don't enumerate again.  Go to the next property. */
2344                 if (obj2 != obj)
2345                     goto enum_next_property;
2346             }
2348             /* Make sure rval is a string for uniformity and compatibility. */
2349             if (!JSVAL_IS_INT(rval)) {
2350                 rval = ATOM_KEY((JSAtom *)rval);
2351             } else if (cx->version != JSVERSION_1_2) {
2352                 str = js_NumberToString(cx, (jsdouble) JSVAL_TO_INT(rval));
2353                 if (!str) {
2354                     ok = JS_FALSE;
2355                     goto out;
2356                 }
2358                 rval = STRING_TO_JSVAL(str);
2359             }
2361             switch (op) {
2362               case JSOP_FORARG:
2363                 slot = GET_ARGNO(pc);
2364                 JS_ASSERT(slot < fp->fun->nargs);
2365                 fp->argv[slot] = rval;
2366                 break;
2368               case JSOP_FORVAR:
2369                 slot = GET_VARNO(pc);
2370                 JS_ASSERT(slot < fp->fun->nvars);
2371                 fp->vars[slot] = rval;
2372                 break;
2374               case JSOP_FORELEM:
2375                 /* FORELEM is not a SET operation, it's more like BINDNAME. */
2376                 PUSH_OPND(rval);
2377                 break;
2379               default:
2380                 /* Convert lval to a non-null object containing id. */
2381                 VALUE_TO_OBJECT(cx, lval, obj);
2383                 /* Set the variable obj[id] to refer to rval. */
2384                 fp->flags |= JSFRAME_ASSIGNING;
2385                 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
2386                 fp->flags &= ~JSFRAME_ASSIGNING;
2387                 if (!ok)
2388                     goto out;
2389                 break;
2390             }
2392             /* Push true to keep looping through properties. */
2393             rval = JSVAL_TRUE;
2395           end_forinloop:
2396             sp += i + 1;
2397             PUSH_OPND(rval);
2398             break;
2400           case JSOP_DUP:
2401             JS_ASSERT(sp > fp->spbase);
2402             rval = sp[-1];
2403             PUSH_OPND(rval);
2404             break;
2406           case JSOP_DUP2:
2407             JS_ASSERT(sp - 1 > fp->spbase);
2408             lval = FETCH_OPND(-2);
2409             rval = FETCH_OPND(-1);
2410             PUSH_OPND(lval);
2411             PUSH_OPND(rval);
2412             break;
2414 #define PROPERTY_OP(n, call)                                                  \
2415     JS_BEGIN_MACRO                                                            \
2416         /* Pop the left part and resolve it to a non-null object. */          \
2417         lval = FETCH_OPND(n);                                                 \
2418         VALUE_TO_OBJECT(cx, lval, obj);                                       \
2419                                                                               \
2420         /* Get or set the property, set ok false if error, true if success. */\
2421         SAVE_SP(fp);                                                          \
2422         call;                                                                 \
2423         if (!ok)                                                              \
2424             goto out;                                                         \
2425     JS_END_MACRO
2427 #define ELEMENT_OP(n, call)                                                   \
2428     JS_BEGIN_MACRO                                                            \
2429         FETCH_ELEMENT_ID(n, id);                                              \
2430         PROPERTY_OP(n-1, call);                                               \
2431     JS_END_MACRO
2433 /*
2434  * Direct callers, i.e. those who do not wrap CACHED_GET and CACHED_SET calls
2435  * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP(fp); beforehand, just
2436  * in case a getter or setter function is invoked.
2437  */
2438 #define CACHED_GET(call)                                                      \
2439     JS_BEGIN_MACRO                                                            \
2440         if (!OBJ_IS_NATIVE(obj)) {                                            \
2441             ok = call;                                                        \
2442         } else {                                                              \
2443             JS_LOCK_OBJ(cx, obj);                                             \
2444             PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);          \
2445             if (sprop) {                                                      \
2446                 JSScope *scope_ = OBJ_SCOPE(obj);                             \
2447                 slot = (uintN)sprop->slot;                                    \
2448                 rval = (slot != SPROP_INVALID_SLOT)                           \
2449                        ? LOCKED_OBJ_GET_SLOT(obj, slot)                       \
2450                        : JSVAL_VOID;                                          \
2451                 JS_UNLOCK_SCOPE(cx, scope_);                                  \
2452                 ok = SPROP_GET(cx, sprop, obj, obj, &rval);                   \
2453                 JS_LOCK_SCOPE(cx, scope_);                                    \
2454                 if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_))                \
2455                     LOCKED_OBJ_SET_SLOT(obj, slot, rval);                     \
2456                 JS_UNLOCK_SCOPE(cx, scope_);                                  \
2457             } else {                                                          \
2458                 JS_UNLOCK_OBJ(cx, obj);                                       \
2459                 ok = call;                                                    \
2460                 /* No fill here: js_GetProperty fills the cache. */           \
2461             }                                                                 \
2462         }                                                                     \
2463     JS_END_MACRO
2465 #define CACHED_SET(call)                                                      \
2466     JS_BEGIN_MACRO                                                            \
2467         if (!OBJ_IS_NATIVE(obj)) {                                            \
2468             ok = call;                                                        \
2469         } else {                                                              \
2470             JSScope *scope_;                                                  \
2471             JS_LOCK_OBJ(cx, obj);                                             \
2472             PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);          \
2473             if (sprop &&                                                      \
2474                 !(sprop->attrs & JSPROP_READONLY) &&                          \
2475                 (scope_ = OBJ_SCOPE(obj), !SCOPE_IS_SEALED(scope_))) {        \
2476                 JS_UNLOCK_SCOPE(cx, scope_);                                  \
2477                 ok = SPROP_SET(cx, sprop, obj, obj, &rval);                   \
2478                 JS_LOCK_SCOPE(cx, scope_);                                    \
2479                 if (ok && SPROP_HAS_VALID_SLOT(sprop, scope_)) {              \
2480                     LOCKED_OBJ_SET_SLOT(obj, sprop->slot, rval);              \
2481                     GC_POKE(cx, JSVAL_NULL);  /* XXX second arg ignored */    \
2482                 }                                                             \
2483                 JS_UNLOCK_SCOPE(cx, scope_);                                  \
2484             } else {                                                          \
2485                 JS_UNLOCK_OBJ(cx, obj);                                       \
2486                 ok = call;                                                    \
2487                 /* No fill here: js_SetProperty writes through the cache. */  \
2488             }                                                                 \
2489         }                                                                     \
2490     JS_END_MACRO
2492           case JSOP_SETCONST:
2493             obj = fp->varobj;
2494             atom = GET_ATOM(cx, script, pc);
2495             rval = FETCH_OPND(-1);
2496             ok = OBJ_DEFINE_PROPERTY(cx, obj, (jsid)atom, rval, NULL, NULL,
2497                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |
2498                                      JSPROP_READONLY,
2499                                      NULL);
2500             if (!ok)
2501                 goto out;
2502             STORE_OPND(-1, rval);
2503             break;
2505           case JSOP_BINDNAME:
2506             atom = GET_ATOM(cx, script, pc);
2507             SAVE_SP(fp);
2508             obj = js_FindIdentifierBase(cx, (jsid)atom);
2509             if (!obj) {
2510                 ok = JS_FALSE;
2511                 goto out;
2512             }
2513             PUSH_OPND(OBJECT_TO_JSVAL(obj));
2514             break;
2516           case JSOP_SETNAME:
2517             atom = GET_ATOM(cx, script, pc);
2518             id   = (jsid)atom;
2519             rval = FETCH_OPND(-1);
2520             lval = FETCH_OPND(-2);
2521             JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
2522             obj  = JSVAL_TO_OBJECT(lval);
2523             SAVE_SP(fp);
2524             CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
2525             if (!ok)
2526                 goto out;
2527             sp--;
2528             STORE_OPND(-1, rval);
2529             break;
2531 #define INTEGER_OP(OP, EXTRA_CODE)                                            \
2532     JS_BEGIN_MACRO                                                            \
2533         FETCH_INT(cx, -1, j);                                                 \
2534         FETCH_INT(cx, -2, i);                                                 \
2535         if (!ok)                                                              \
2536             goto out;                                                         \
2537         EXTRA_CODE                                                            \
2538         d = i OP j;                                                           \
2539         sp--;                                                                 \
2540         STORE_NUMBER(cx, -1, d);                                              \
2541     JS_END_MACRO
2543 #define BITWISE_OP(OP)          INTEGER_OP(OP, (void) 0;)
2544 #define SIGNED_SHIFT_OP(OP)     INTEGER_OP(OP, j &= 31;)
2546           case JSOP_BITOR:
2547             BITWISE_OP(|);
2548             break;
2550           case JSOP_BITXOR:
2551             BITWISE_OP(^);
2552             break;
2554           case JSOP_BITAND:
2555             BITWISE_OP(&);
2556             break;
2558 #if defined(XP_WIN)
2559 #define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN)                                \
2560     ((JSDOUBLE_IS_NaN(LVAL) || JSDOUBLE_IS_NaN(RVAL))                         \
2561      ? (IFNAN)                                                                \
2562      : (LVAL) OP (RVAL))
2563 #else
2564 #define COMPARE_DOUBLES(LVAL, OP, RVAL, IFNAN) ((LVAL) OP (RVAL))
2565 #endif
2567 #define RELATIONAL_OP(OP)                                                     \
2568     JS_BEGIN_MACRO                                                            \
2569         rval = FETCH_OPND(-1);                                                \
2570         lval = FETCH_OPND(-2);                                                \
2571         /* Optimize for two int-tagged operands (typical loop control). */    \
2572         if ((lval & rval) & JSVAL_INT) {                                      \
2573             ltmp = lval ^ JSVAL_VOID;                                         \
2574             rtmp = rval ^ JSVAL_VOID;                                         \
2575             if (ltmp && rtmp) {                                               \
2576                 cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval);              \
2577             } else {                                                          \
2578                 d  = ltmp ? JSVAL_TO_INT(lval) : *rt->jsNaN;                  \
2579                 d2 = rtmp ? JSVAL_TO_INT(rval) : *rt->jsNaN;                  \
2580                 cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE);                  \
2581             }                                                                 \
2582         } else {                                                              \
2583             VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_NUMBER, &lval);               \
2584             VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_NUMBER, &rval);               \
2585             if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) {             \
2586                 str  = JSVAL_TO_STRING(lval);                                 \
2587                 str2 = JSVAL_TO_STRING(rval);                                 \
2588                 cond = js_CompareStrings(str, str2) OP 0;                     \
2589             } else {                                                          \
2590                 VALUE_TO_NUMBER(cx, lval, d);                                 \
2591                 VALUE_TO_NUMBER(cx, rval, d2);                                \
2592                 cond = COMPARE_DOUBLES(d, OP, d2, JS_FALSE);                  \
2593             }                                                                 \
2594         }                                                                     \
2595         sp--;                                                                 \
2596         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \
2597     JS_END_MACRO
2599 #define EQUALITY_OP(OP, IFNAN)                                                \
2600     JS_BEGIN_MACRO                                                            \
2601         rval = FETCH_OPND(-1);                                                \
2602         lval = FETCH_OPND(-2);                                                \
2603         ltmp = JSVAL_TAG(lval);                                               \
2604         rtmp = JSVAL_TAG(rval);                                               \
2605         if (ltmp == rtmp) {                                                   \
2606             if (ltmp == JSVAL_STRING) {                                       \
2607                 str  = JSVAL_TO_STRING(lval);                                 \
2608                 str2 = JSVAL_TO_STRING(rval);                                 \
2609                 cond = js_CompareStrings(str, str2) OP 0;                     \
2610             } else if (ltmp == JSVAL_DOUBLE) {                                \
2611                 d  = *JSVAL_TO_DOUBLE(lval);                                  \
2612                 d2 = *JSVAL_TO_DOUBLE(rval);                                  \
2613                 cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \
2614             } else {                                                          \
2615                 /* Handle all undefined (=>NaN) and int combinations. */      \
2616                 cond = lval OP rval;                                          \
2617             }                                                                 \
2618         } else {                                                              \
2619             if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) {                 \
2620                 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1;     \
2621             } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) {          \
2622                 cond = 1 OP 0;                                                \
2623             } else {                                                          \
2624                 if (ltmp == JSVAL_OBJECT) {                                   \
2625                     VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &lval);         \
2626                     ltmp = JSVAL_TAG(lval);                                   \
2627                 } else if (rtmp == JSVAL_OBJECT) {                            \
2628                     VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rval);         \
2629                     rtmp = JSVAL_TAG(rval);                                   \
2630                 }                                                             \
2631                 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) {           \
2632                     str  = JSVAL_TO_STRING(lval);                             \
2633                     str2 = JSVAL_TO_STRING(rval);                             \
2634                     cond = js_CompareStrings(str, str2) OP 0;                 \
2635                 } else {                                                      \
2636                     VALUE_TO_NUMBER(cx, lval, d);                             \
2637                     VALUE_TO_NUMBER(cx, rval, d2);                            \
2638                     cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                 \
2639                 }                                                             \
2640             }                                                                 \
2641         }                                                                     \
2642         sp--;                                                                 \
2643         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \
2644     JS_END_MACRO
2646           case JSOP_EQ:
2647             EQUALITY_OP(==, JS_FALSE);
2648             break;
2650           case JSOP_NE:
2651             EQUALITY_OP(!=, JS_TRUE);
2652             break;
2654 #if !JS_BUG_FALLIBLE_EQOPS
2655 #define NEW_EQUALITY_OP(OP, IFNAN)                                            \
2656     JS_BEGIN_MACRO                                                            \
2657         rval = FETCH_OPND(-1);                                                \
2658         lval = FETCH_OPND(-2);                                                \
2659         ltmp = JSVAL_TAG(lval);                                               \
2660         rtmp = JSVAL_TAG(rval);                                               \
2661         if (ltmp == rtmp) {                                                   \
2662             if (ltmp == JSVAL_STRING) {                                       \
2663                 str  = JSVAL_TO_STRING(lval);                                 \
2664                 str2 = JSVAL_TO_STRING(rval);                                 \
2665                 cond = js_CompareStrings(str, str2) OP 0;                     \
2666             } else if (ltmp == JSVAL_DOUBLE) {                                \
2667                 d  = *JSVAL_TO_DOUBLE(lval);                                  \
2668                 d2 = *JSVAL_TO_DOUBLE(rval);                                  \
2669                 cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \
2670             } else {                                                          \
2671                 cond = lval OP rval;                                          \
2672             }                                                                 \
2673         } else {                                                              \
2674             if (ltmp == JSVAL_DOUBLE && JSVAL_IS_INT(rval)) {                 \
2675                 d  = *JSVAL_TO_DOUBLE(lval);                                  \
2676                 d2 = JSVAL_TO_INT(rval);                                      \
2677                 cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \
2678             } else if (JSVAL_IS_INT(lval) && rtmp == JSVAL_DOUBLE) {          \
2679                 d  = JSVAL_TO_INT(lval);                                      \
2680                 d2 = *JSVAL_TO_DOUBLE(rval);                                  \
2681                 cond = COMPARE_DOUBLES(d, OP, d2, IFNAN);                     \
2682             } else {                                                          \
2683                 cond = lval OP rval;                                          \
2684             }                                                                 \
2685         }                                                                     \
2686         sp--;                                                                 \
2687         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \
2688     JS_END_MACRO
2690           case JSOP_NEW_EQ:
2691             NEW_EQUALITY_OP(==, JS_FALSE);
2692             break;
2694           case JSOP_NEW_NE:
2695             NEW_EQUALITY_OP(!=, JS_TRUE);
2696             break;
2698 #if JS_HAS_SWITCH_STATEMENT
2699           case JSOP_CASE:
2700             NEW_EQUALITY_OP(==, JS_FALSE);
2701             (void) POP();
2702             if (cond) {
2703                 len = GET_JUMP_OFFSET(pc);
2704                 CHECK_BRANCH(len);
2705             } else {
2706                 PUSH(lval);
2707             }
2708             break;
2710           case JSOP_CASEX:
2711             NEW_EQUALITY_OP(==, JS_FALSE);
2712             (void) POP();
2713             if (cond) {
2714                 len = GET_JUMPX_OFFSET(pc);
2715                 CHECK_BRANCH(len);
2716             } else {
2717                 PUSH(lval);
2718             }
2719             break;
2720 #endif
2722 #endif /* !JS_BUG_FALLIBLE_EQOPS */
2724           case JSOP_LT:
2725             RELATIONAL_OP(<);
2726             break;
2728           case JSOP_LE:
2729             RELATIONAL_OP(<=);
2730             break;
2732           case JSOP_GT:
2733             RELATIONAL_OP(>);
2734             break;
2736           case JSOP_GE:
2737             RELATIONAL_OP(>=);
2738             break;
2740 #undef EQUALITY_OP
2741 #undef RELATIONAL_OP
2743           case JSOP_LSH:
2744             SIGNED_SHIFT_OP(<<);
2745             break;
2747           case JSOP_RSH:
2748             SIGNED_SHIFT_OP(>>);
2749             break;
2751           case JSOP_URSH:
2752           {
2753             uint32 u;
2755             FETCH_INT(cx, -1, j);
2756             FETCH_UINT(cx, -2, u);
2757             j &= 31;
2758             d = u >> j;
2759             sp--;
2760             STORE_NUMBER(cx, -1, d);
2761             break;
2762           }
2764 #undef INTEGER_OP
2765 #undef BITWISE_OP
2766 #undef SIGNED_SHIFT_OP
2768           case JSOP_ADD:
2769             rval = FETCH_OPND(-1);
2770             lval = FETCH_OPND(-2);
2771             VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, &ltmp);
2772             VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rtmp);
2773             if ((cond = JSVAL_IS_STRING(ltmp)) || JSVAL_IS_STRING(rtmp)) {
2774                 SAVE_SP(fp);
2775                 if (cond) {
2776                     str = JSVAL_TO_STRING(ltmp);
2777                     ok = (str2 = js_ValueToString(cx, rtmp)) != NULL;
2778                 } else {
2779                     str2 = JSVAL_TO_STRING(rtmp);
2780                     ok = (str = js_ValueToString(cx, ltmp)) != NULL;
2781                 }
2782                 if (!ok)
2783                     goto out;
2784                 str = js_ConcatStrings(cx, str, str2);
2785                 if (!str) {
2786                     ok = JS_FALSE;
2787                     goto out;
2788                 }
2789                 sp--;
2790                 STORE_OPND(-1, STRING_TO_JSVAL(str));
2791             } else {
2792                 VALUE_TO_NUMBER(cx, lval, d);
2793                 VALUE_TO_NUMBER(cx, rval, d2);
2794                 d += d2;
2795                 sp--;
2796                 STORE_NUMBER(cx, -1, d);
2797             }
2798             break;
2800 #define BINARY_OP(OP)                                                         \
2801     JS_BEGIN_MACRO                                                            \
2802         FETCH_NUMBER(cx, -1, d2);                                             \
2803         FETCH_NUMBER(cx, -2, d);                                              \
2804         d = d OP d2;                                                          \
2805         sp--;                                                                 \
2806         STORE_NUMBER(cx, -1, d);                                              \
2807     JS_END_MACRO
2809           case JSOP_SUB:
2810             BINARY_OP(-);
2811             break;
2813           case JSOP_MUL:
2814             BINARY_OP(*);
2815             break;
2817           case JSOP_DIV:
2818             FETCH_NUMBER(cx, -1, d2);
2819             FETCH_NUMBER(cx, -2, d);
2820             sp--;
2821             if (d2 == 0) {
2822 #if defined(XP_WIN)
2823                 /* XXX MSVC miscompiles such that (NaN == 0) */
2824                 if (JSDOUBLE_IS_NaN(d2))
2825                     rval = DOUBLE_TO_JSVAL(rt->jsNaN);
2826                 else
2827 #endif
2828                 if (d == 0 || JSDOUBLE_IS_NaN(d))
2829                     rval = DOUBLE_TO_JSVAL(rt->jsNaN);
2830                 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)
2831                     rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);
2832                 else
2833                     rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);
2834                 STORE_OPND(-1, rval);
2835             } else {
2836                 d /= d2;
2837                 STORE_NUMBER(cx, -1, d);
2838             }
2839             break;
2841           case JSOP_MOD:
2842             FETCH_NUMBER(cx, -1, d2);
2843             FETCH_NUMBER(cx, -2, d);
2844             sp--;
2845             if (d2 == 0) {
2846                 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
2847             } else {
2848 #if defined(XP_WIN)
2849               /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
2850               if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
2851 #endif
2852                 d = fmod(d, d2);
2853                 STORE_NUMBER(cx, -1, d);
2854             }
2855             break;
2857           case JSOP_NOT:
2858             POP_BOOLEAN(cx, rval, cond);
2859             PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));
2860             break;
2862           case JSOP_BITNOT:
2863             FETCH_INT(cx, -1, i);
2864             d = (jsdouble) ~i;
2865             STORE_NUMBER(cx, -1, d);
2866             break;
2868           case JSOP_NEG:
2869             FETCH_NUMBER(cx, -1, d);
2870 #ifdef HPUX
2871             /*
2872              * Negation of a zero doesn't produce a negative
2873              * zero on HPUX. Perform the operation by bit
2874              * twiddling.
2875              */
2876             JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
2877 #else
2878             d = -d;
2879 #endif
2880             STORE_NUMBER(cx, -1, d);
2881             break;
2883           case JSOP_POS:
2884             FETCH_NUMBER(cx, -1, d);
2885             STORE_NUMBER(cx, -1, d);
2886             break;
2888           case JSOP_NEW:
2889             /* Get immediate argc and find the constructor function. */
2890             argc = GET_ARGC(pc);
2892 #if JS_HAS_INITIALIZERS
2893           do_new:
2894 #endif
2895             vp = sp - (2 + argc);
2896             JS_ASSERT(vp >= fp->spbase);
2898             fun = NULL;
2899             obj2 = NULL;
2900             lval = *vp;
2901             if (!JSVAL_IS_OBJECT(lval) ||
2902                 (obj2 = JSVAL_TO_OBJECT(lval)) == NULL ||
2903                 /* XXX clean up to avoid special cases above ObjectOps layer */
2904                 OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass ||
2905                 !obj2->map->ops->construct)
2906             {
2907                 SAVE_SP(fp);
2908                 fun = js_ValueToFunction(cx, vp, JSV2F_CONSTRUCT);
2909                 if (!fun) {
2910                     ok = JS_FALSE;
2911                     goto out;
2912                 }
2913             }
2915             clasp = &js_ObjectClass;
2916             if (!obj2) {
2917                 proto = parent = NULL;
2918                 fun = NULL;
2919             } else {
2920                 /* Get the constructor prototype object for this function. */
2921                 ok = OBJ_GET_PROPERTY(cx, obj2,
2922                                       (jsid)rt->atomState.classPrototypeAtom,
2923                                       &rval);
2924                 if (!ok)
2925                     goto out;
2926                 proto = JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : NULL;
2927                 parent = OBJ_GET_PARENT(cx, obj2);
2929                 if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
2930                     funclasp = ((JSFunction *)JS_GetPrivate(cx, obj2))->clasp;
2931                     if (funclasp)
2932                         clasp = funclasp;
2933                 }
2934             }
2935             obj = js_NewObject(cx, clasp, proto, parent);
2936             if (!obj) {
2937                 ok = JS_FALSE;
2938                 goto out;
2939             }
2941             /* Now we have an object with a constructor method; call it. */
2942             vp[1] = OBJECT_TO_JSVAL(obj);
2943             SAVE_SP(fp);
2944             ok = js_Invoke(cx, argc, JSINVOKE_CONSTRUCT);
2945             RESTORE_SP(fp);
2946             LOAD_BRANCH_CALLBACK(cx);
2947             LOAD_INTERRUPT_HANDLER(rt);
2948             if (!ok) {
2949                 cx->newborn[GCX_OBJECT] = NULL;
2950                 goto out;
2951             }
2953             /* Check the return value and update obj from it. */
2954             rval = *vp;
2955             if (JSVAL_IS_PRIMITIVE(rval)) {
2956                 if (fun || !JSVERSION_IS_ECMA(cx->version)) {
2957                     *vp = OBJECT_TO_JSVAL(obj);
2958                     break;
2959                 }
2960                 /* native [[Construct]] returning primitive is error */
2961                 str = js_ValueToString(cx, rval);
2962                 if (str) {
2963                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2964                                          JSMSG_BAD_NEW_RESULT,
2965                                          JS_GetStringBytes(str));
2966                 }
2967                 ok = JS_FALSE;
2968                 goto out;
2969             }
2970             obj = JSVAL_TO_OBJECT(rval);
2971             JS_RUNTIME_METER(rt, constructs);
2972             break;
2974           case JSOP_DELNAME:
2975             atom = GET_ATOM(cx, script, pc);
2976             id   = (jsid)atom;
2978             SAVE_SP(fp);
2979             ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
2980             if (!ok)
2981                 goto out;
2983             /* ECMA says to return true if name is undefined or inherited. */
2984             rval = JSVAL_TRUE;
2985             if (prop) {
2986                 OBJ_DROP_PROPERTY(cx, obj2, prop);
2987                 ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval);
2988                 if (!ok)
2989                     goto out;
2990             }
2991             PUSH_OPND(rval);
2992             break;
2994           case JSOP_DELPROP:
2995             atom = GET_ATOM(cx, script, pc);
2996             id   = (jsid)atom;
2997             PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
2998             STORE_OPND(-1, rval);
2999             break;
3001           case JSOP_DELELEM:
3002             ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
3003             sp--;
3004             STORE_OPND(-1, rval);
3005             break;
3007           case JSOP_TYPEOF:
3008             rval = POP_OPND();
3009             type = JS_TypeOfValue(cx, rval);
3010             atom = rt->atomState.typeAtoms[type];
3011             str  = ATOM_TO_STRING(atom);
3012             PUSH_OPND(STRING_TO_JSVAL(str));
3013             break;
3015           case JSOP_VOID:
3016             (void) POP_OPND();
3017             PUSH_OPND(JSVAL_VOID);
3018             break;
3020           case JSOP_INCNAME:
3021           case JSOP_DECNAME:
3022           case JSOP_NAMEINC:
3023           case JSOP_NAMEDEC:
3024             atom = GET_ATOM(cx, script, pc);
3025             id   = (jsid)atom;
3027             SAVE_SP(fp);
3028             ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3029             if (!ok)
3030                 goto out;
3031             if (!prop)
3032                 goto atom_not_defined;
3034             OBJ_DROP_PROPERTY(cx, obj2, prop);
3035             lval = OBJECT_TO_JSVAL(obj);
3036             goto do_incop;
3038           case JSOP_INCPROP:
3039           case JSOP_DECPROP:
3040           case JSOP_PROPINC:
3041           case JSOP_PROPDEC:
3042             atom = GET_ATOM(cx, script, pc);
3043             id   = (jsid)atom;
3044             lval = POP_OPND();
3045             goto do_incop;
3047           case JSOP_INCELEM:
3048           case JSOP_DECELEM:
3049           case JSOP_ELEMINC:
3050           case JSOP_ELEMDEC:
3051             POP_ELEMENT_ID(id);
3052             lval = POP_OPND();
3054           do_incop:
3055             VALUE_TO_OBJECT(cx, lval, obj);
3057             /* The operand must contain a number. */
3058             SAVE_SP(fp);
3059             CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval));
3060             if (!ok)
3061                 goto out;
3063             /* The expression result goes in rtmp, the updated value in rval. */
3064             if (JSVAL_IS_INT(rval) &&
3065                 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&
3066                 rval != INT_TO_JSVAL(JSVAL_INT_MAX)) {
3067                 if (cs->format & JOF_POST) {
3068                     rtmp = rval;
3069                     (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3070                 } else {
3071                     (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2);
3072                     rtmp = rval;
3073                 }
3074             } else {
3076 /*
3077  * Initially, rval contains the value to increment or decrement, which is not
3078  * yet converted.  As above, the expression result goes in rtmp, the updated
3079  * value goes in rval.
3080  */
3081 #define NONINT_INCREMENT_OP_MIDDLE()                                          \
3082     JS_BEGIN_MACRO                                                            \
3083         VALUE_TO_NUMBER(cx, rval, d);                                         \
3084         if (cs->format & JOF_POST) {                                          \
3085             rtmp = rval;                                                      \
3086             if (!JSVAL_IS_NUMBER(rtmp)) {                                     \
3087                 ok = js_NewNumberValue(cx, d, &rtmp);                         \
3088                 if (!ok)                                                      \
3089                     goto out;                                                 \
3090             }                                                                 \
3091             (cs->format & JOF_INC) ? d++ : d--;                               \
3092             ok = js_NewNumberValue(cx, d, &rval);                             \
3093         } else {                                                              \
3094             (cs->format & JOF_INC) ? ++d : --d;                               \
3095             ok = js_NewNumberValue(cx, d, &rval);                             \
3096             rtmp = rval;                                                      \
3097         }                                                                     \
3098         if (!ok)                                                              \
3099             goto out;                                                         \
3100     JS_END_MACRO
3102                 NONINT_INCREMENT_OP_MIDDLE();
3103             }
3105             CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
3106             if (!ok)
3107                 goto out;
3108             PUSH_OPND(rtmp);
3109             break;
3111 /*
3112  * NB: This macro can't use JS_BEGIN_MACRO/JS_END_MACRO around its body because
3113  * it must break from the switch case that calls it, not from the do...while(0)
3114  * loop created by the JS_BEGIN/END_MACRO brackets.
3115  */
3116 #define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX)                      \
3117     slot = SLOT;                                                              \
3118     JS_ASSERT(slot < fp->fun->COUNT);                                         \
3119     vp = fp->BASE + slot;                                                     \
3120     rval = *vp;                                                               \
3121     if (JSVAL_IS_INT(rval) &&                                                 \
3122         rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) {                           \
3123         PRE = rval;                                                           \
3124         rval OP 2;                                                            \
3125         *vp = rval;                                                           \
3126         PUSH_OPND(PRE);                                                       \
3127         break;                                                                \
3128     }                                                                         \
3129     goto do_nonint_fast_incop;
3131           case JSOP_INCARG:
3132             FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX);
3133           case JSOP_DECARG:
3134             FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN);
3135           case JSOP_ARGINC:
3136             FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX);
3137           case JSOP_ARGDEC:
3138             FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN);
3140           case JSOP_INCVAR:
3141             FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX);
3142           case JSOP_DECVAR:
3143             FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN);
3144           case JSOP_VARINC:
3145             FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX);
3146           case JSOP_VARDEC:
3147             FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN);
3149 #undef FAST_INCREMENT_OP
3151           do_nonint_fast_incop:
3152             NONINT_INCREMENT_OP_MIDDLE();
3153             *vp = rval;
3154             PUSH_OPND(rtmp);
3155             break;
3157 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX)                        \
3158     slot = GET_VARNO(pc);                                                     \
3159     JS_ASSERT(slot < fp->nvars);                                              \
3160     lval = fp->vars[slot];                                                    \
3161     if (JSVAL_IS_NULL(lval)) {                                                \
3162         op = SLOWOP;                                                          \
3163         goto do_op;                                                           \
3164     }                                                                         \
3165     slot = JSVAL_TO_INT(lval);                                                \
3166     obj = fp->varobj;                                                         \
3167     rval = OBJ_GET_SLOT(cx, obj, slot);                                       \
3168     if (JSVAL_IS_INT(rval) &&                                                 \
3169         rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) {                           \
3170         PRE = rval;                                                           \
3171         rval OP 2;                                                            \
3172         OBJ_SET_SLOT(cx, obj, slot, rval);                                    \
3173         PUSH_OPND(PRE);                                                       \
3174         break;                                                                \
3175     }                                                                         \
3176     goto do_nonint_fast_global_incop;
3178           case JSOP_INCGVAR:
3179             FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX);
3180           case JSOP_DECGVAR:
3181             FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN);
3182           case JSOP_GVARINC:
3183             FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX);
3184           case JSOP_GVARDEC:
3185             FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN);
3187 #undef FAST_GLOBAL_INCREMENT_OP
3189           do_nonint_fast_global_incop:
3190             NONINT_INCREMENT_OP_MIDDLE();
3191             OBJ_SET_SLOT(cx, obj, slot, rval);
3192             PUSH_OPND(rtmp);
3193             break;
3195           case JSOP_GETPROP:
3196             /* Get an immediate atom naming the property. */
3197             atom = GET_ATOM(cx, script, pc);
3198             id   = (jsid)atom;
3199             PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
3200             STORE_OPND(-1, rval);
3201             break;
3203           case JSOP_SETPROP:
3204             /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */
3205             rval = FETCH_OPND(-1);
3207             /* Get an immediate atom naming the property. */
3208             atom = GET_ATOM(cx, script, pc);
3209             id   = (jsid)atom;
3210             PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
3211             sp--;
3212             STORE_OPND(-1, rval);
3213             break;
3215           case JSOP_GETELEM:
3216             ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)));
3217             sp--;
3218             STORE_OPND(-1, rval);
3219             break;
3221           case JSOP_SETELEM:
3222             rval = FETCH_OPND(-1);
3223             ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)));
3224             sp -= 2;
3225             STORE_OPND(-1, rval);
3226             break;
3228           case JSOP_ENUMELEM:
3229             /* Funky: the value to set is under the [obj, id] pair. */
3230             FETCH_ELEMENT_ID(-1, id);
3231             lval = FETCH_OPND(-2);
3232             VALUE_TO_OBJECT(cx, lval, obj);
3233             rval = FETCH_OPND(-3);
3234             SAVE_SP(fp);
3235             ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
3236             if (!ok)
3237                 goto out;
3238             sp -= 3;
3239             break;
3241 /*
3242  * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the
3243  * arguments object until it is truly needed.  JSOP_ARGSUB optimizes away
3244  * arguments objects when the only uses of the 'arguments' parameter are to
3245  * fetch individual actual parameters.  But if such a use were then invoked,
3246  * e.g., arguments[i](), the 'this' parameter would and must bind to the
3247  * caller's arguments object.  So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP.
3248  */
3249 #define LAZY_ARGS_THISP ((JSObject *) 1)
3251           case JSOP_PUSHOBJ:
3252             if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) {
3253                 ok = JS_FALSE;
3254                 goto out;
3255             }
3256             PUSH_OPND(OBJECT_TO_JSVAL(obj));
3257             break;
3259           case JSOP_CALL:
3260           case JSOP_EVAL:
3261             argc = GET_ARGC(pc);
3262             vp = sp - (argc + 2);
3263             lval = *vp;
3264             SAVE_SP(fp);
3266             if (JSVAL_IS_FUNCTION(cx, lval) &&
3267                 (obj = JSVAL_TO_OBJECT(lval),
3268                  fun = (JSFunction *) JS_GetPrivate(cx, obj),
3269                  fun->interpreted &&
3270                  !(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) &&
3271                  argc >= (uintN)(fun->nargs + fun->extra)))
3272           /* inline_call: */
3273             {
3274                 uintN nframeslots, nvars;
3275                 void *newmark;
3276                 JSInlineFrame *newifp;
3277                 JSInterpreterHook hook;
3279                 /* Restrict recursion of lightweight functions. */
3280                 if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
3281                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3282                                          JSMSG_OVER_RECURSED);
3283                     ok = JS_FALSE;
3284                     goto out;
3285                 }
3287 #if JS_HAS_JIT
3288                 /* ZZZbe should do this only if interpreted often enough. */
3289                 ok = jsjit_Compile(cx, fun);
3290                 if (!ok)
3291                     goto out;
3292 #endif
3294                 /* Compute the number of stack slots needed for fun. */
3295                 nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1)
3296                               / sizeof(jsval);
3297                 nvars = fun->nvars;
3298                 script = fun->u.script;
3299                 depth = (jsint) script->depth;
3301                 /* Allocate the frame and space for vars and operands. */
3302                 newsp = js_AllocRawStack(cx, nframeslots + nvars + 2 * depth,
3303                                          &newmark);
3304                 if (!newsp) {
3305                     ok = JS_FALSE;
3306                     goto bad_inline_call;
3307                 }
3308                 newifp = (JSInlineFrame *) newsp;
3309                 newsp += nframeslots;
3311                 /* Initialize the stack frame. */
3312                 memset(newifp, 0, sizeof(JSInlineFrame));
3313                 newifp->frame.script = script;
3314                 newifp->frame.fun = fun;
3315                 newifp->frame.argc = argc;
3316                 newifp->frame.argv = vp + 2;
3317                 newifp->frame.rval = JSVAL_VOID;
3318                 newifp->frame.nvars = nvars;
3319                 newifp->frame.vars = newsp;
3320                 newifp->frame.down = fp;
3321                 newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj);
3322                 newifp->mark = newmark;
3324                 /* Compute the 'this' parameter now that argv is set. */
3325                 ok = ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame);
3326                 if (!ok) {
3327                     js_FreeRawStack(cx, newmark);
3328                     goto bad_inline_call;
3329                 }
3330 #ifdef DUMP_CALL_TABLE
3331                 LogCall(cx, *vp, argc, vp + 2);
3332 #endif
3334                 /* Push void to initialize local variables. */
3335                 sp = newsp;
3336                 while (nvars--)
3337                     PUSH(JSVAL_VOID);
3338                 sp += depth;
3339                 newifp->frame.spbase = sp;
3340                 SAVE_SP(&newifp->frame);
3342                 /* Call the debugger hook if present. */
3343                 hook = cx->runtime->callHook;
3344                 if (hook) {
3345                     newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
3346                                             cx->runtime->callHookData);
3347                     LOAD_INTERRUPT_HANDLER(rt);
3348                 }
3350                 /* Switch to new version if currentVersion wasn't overridden. */
3351                 newifp->callerVersion = cx->version;
3352                 if (cx->version == currentVersion) {
3353                     currentVersion = script->version;
3354                     if (currentVersion != cx->version)
3355                         JS_SetVersion(cx, currentVersion);
3356                 }
3358                 /* Push the frame and set interpreter registers. */
3359                 cx->fp = fp = &newifp->frame;
3360                 pc = script->code;
3361                 endpc = pc + script->length;
3362                 inlineCallCount++;
3363                 JS_RUNTIME_METER(rt, inlineCalls);
3364                 continue;
3366               bad_inline_call:
3367                 script = fp->script;
3368                 depth = (jsint) script->depth;
3369                 goto out;
3370             }
3372             ok = js_Invoke(cx, argc, 0);
3373             RESTORE_SP(fp);
3374             LOAD_BRANCH_CALLBACK(cx);
3375             LOAD_INTERRUPT_HANDLER(rt);
3376             if (!ok)
3377                 goto out;
3378             JS_RUNTIME_METER(rt, nonInlineCalls);
3379 #if JS_HAS_LVALUE_RETURN
3380             if (cx->rval2set) {
3381                 /*
3382                  * Sneaky: use the stack depth we didn't claim in our budget,
3383                  * but that we know is there on account of [fun, this] already
3384                  * having been pushed, at a minimum (if no args).  Those two
3385                  * slots have been popped and [rval] has been pushed, which
3386                  * leaves one more slot for rval2 before we might overflow.
3387                  *
3388                  * NB: rval2 must be the property identifier, and rval the
3389                  * object from which to get the property.  The pair form an
3390                  * ECMA "reference type", which can be used on the right- or
3391                  * left-hand side of assignment ops.  Only native methods can
3392                  * return reference types.  See JSOP_SETCALL just below for
3393                  * the left-hand-side case.
3394                  */
3395                 PUSH_OPND(cx->rval2);
3396                 cx->rval2set = JS_FALSE;
3397                 ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval));
3398                 sp--;
3399                 STORE_OPND(-1, rval);
3400             }
3401 #endif
3402             obj = NULL;
3403             break;
3405 #if JS_HAS_LVALUE_RETURN
3406           case JSOP_SETCALL:
3407             argc = GET_ARGC(pc);
3408             SAVE_SP(fp);
3409             ok = js_Invoke(cx, argc, 0);
3410             RESTORE_SP(fp);
3411             LOAD_BRANCH_CALLBACK(cx);
3412             LOAD_INTERRUPT_HANDLER(rt);
3413             if (!ok)
3414                 goto out;
3415             if (!cx->rval2set) {
3416                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3417                                      JSMSG_BAD_LEFTSIDE_OF_ASS);
3418                 ok = JS_FALSE;
3419                 goto out;
3420             }
3421             PUSH_OPND(cx->rval2);
3422             cx->rval2set = JS_FALSE;
3423             obj = NULL;
3424             break;
3425 #endif
3427           case JSOP_NAME:
3428             atom = GET_ATOM(cx, script, pc);
3429             id   = (jsid)atom;
3431             SAVE_SP(fp);
3432             ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
3433             if (!ok)
3434                 goto out;
3435             if (!prop) {
3436                 /* Kludge to allow (typeof foo == "undefined") tests. */
3437                 for (pc2 = pc + len; pc2 < endpc; pc2++) {
3438                     op2 = (JSOp)*pc2;
3439                     if (op2 == JSOP_TYPEOF) {
3440                         PUSH_OPND(JSVAL_VOID);
3441                         goto advance_pc;
3442                     }
3443                     if (op2 != JSOP_GROUP)
3444                         break;
3445                 }
3446                 goto atom_not_defined;
3447             }
3449             /* Take the slow path if prop was not found in a native object. */
3450             if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) {
3451                 OBJ_DROP_PROPERTY(cx, obj2, prop);
3452                 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
3453                 if (!ok)
3454                     goto out;
3455                 PUSH_OPND(rval);
3456                 break;
3457             }
3459             /* Get and push the obj[id] property's value. */
3460             sprop = (JSScopeProperty *)prop;
3461             slot = (uintN)sprop->slot;
3462             rval = (slot != SPROP_INVALID_SLOT)
3463                    ? LOCKED_OBJ_GET_SLOT(obj2, slot)
3464                    : JSVAL_VOID;
3465             JS_UNLOCK_OBJ(cx, obj2);
3466             ok = SPROP_GET(cx, sprop, obj, obj2, &rval);
3467             JS_LOCK_OBJ(cx, obj2);
3468             if (!ok) {
3469                 OBJ_DROP_PROPERTY(cx, obj2, prop);
3470                 goto out;
3471             }
3472             if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2)))
3473                 LOCKED_OBJ_SET_SLOT(obj2, slot, rval);
3474             OBJ_DROP_PROPERTY(cx, obj2, prop);
3475             PUSH_OPND(rval);
3476             break;
3478           case JSOP_UINT16:
3479             i = (jsint) GET_ATOM_INDEX(pc);
3480             rval = INT_TO_JSVAL(i);
3481             PUSH_OPND(rval);
3482             obj = NULL;
3483             break;
3485           case JSOP_NUMBER:
3486           case JSOP_STRING:
3487           case JSOP_OBJECT:
3488             atom = GET_ATOM(cx, script, pc);
3489             PUSH_OPND(ATOM_KEY(atom));
3490             obj = NULL;
3491             break;
3493           case JSOP_REGEXP:
3494           {
3495             JSRegExp *re;
3496             JSObject *funobj;
3498             /*
3499              * Push a regexp object for the atom mapped by the bytecode at pc,
3500              * cloning the literal's regexp object if necessary, to simulate in
3501              * the pre-compile/execute-later case what ECMA specifies for the
3502              * compile-and-go case: that scanning each regexp literal creates
3503              * a single corresponding RegExp object.
3504              *
3505              * To support pre-compilation transparently, we must handle the
3506              * case where a regexp object literal is used in a different global
3507              * at execution time from the global with which it was scanned at
3508              * compile time.  We do this by re-wrapping the JSRegExp private
3509              * data struct with a cloned object having the right prototype and
3510              * parent, and having its own lastIndex property value storage.
3511              *
3512              * Unlike JSOP_DEFFUN and other prolog bytecodes that may clone
3513              * literal objects, we don't want to pay a script prolog execution
3514              * price for all regexp literals in a script (many may not be used
3515              * by a particular execution of that script, depending on control
3516              * flow), so we initialize lazily here.
3517              *
3518              * XXX This code is specific to regular expression objects.  If we
3519              * need a similar op for other kinds of object literals, we should
3520              * push cloning down under JSObjectOps and reuse code here.
3521              */
3522             atom = GET_ATOM(cx, script, pc);
3523             JS_ASSERT(ATOM_IS_OBJECT(atom));
3524             obj = ATOM_TO_OBJECT(atom);
3525             JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
3527             re = (JSRegExp *) JS_GetPrivate(cx, obj);
3528             slot = re->cloneIndex;
3529             if (fp->fun) {
3530                 /*
3531                  * We're in function code, not global or eval code (in eval
3532                  * code, JSOP_REGEXP is never emitted).  The code generator
3533                  * recorded in fp->fun->nregexps the number of re->cloneIndex
3534                  * slots that it reserved in the cloned funobj.
3535                  */
3536                 funobj = JSVAL_TO_OBJECT(fp->argv[-2]);
3537                 slot += JSCLASS_RESERVED_SLOTS(&js_FunctionClass);
3538                 if (!JS_GetReservedSlot(cx, funobj, slot, &rval))
3539                     return JS_FALSE;
3540                 if (JSVAL_IS_VOID(rval))
3541                     rval = JSVAL_NULL;
3542             } else {
3543                 /*
3544                  * We're in global code.  The code generator already arranged
3545                  * via script->numGlobalVars to reserve a global variable slot
3546                  * at cloneIndex.  All global variable slots are initialized
3547                  * to null, not void, for faster testing in JSOP_*GVAR cases.
3548                  */
3549                 rval = fp->vars[slot];
3550 #ifdef __GNUC__
3551                 funobj = NULL;  /* suppress bogus gcc warnings */
3552 #endif
3553             }
3555             if (JSVAL_IS_NULL(rval)) {
3556                 /* Compute the current global object in obj2. */
3557                 obj2 = fp->scopeChain;
3558                 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
3559                     obj2 = parent;
3561                 /*
3562                  * If obj's parent is not obj2, we must clone obj so that it
3563                  * has the right parent, and therefore, the right prototype.
3564                  *
3565                  * Yes, this means we assume that the correct RegExp.prototype
3566                  * to which regexp instances (including literals) delegate can
3567                  * be distinguished solely by the instance's parent, which was
3568                  * set to the parent of the RegExp constructor function object
3569                  * when the instance was created.  In other words,
3570                  *
3571                  *   (/x/.__parent__ == RegExp.__parent__) implies
3572                  *   (/x/.__proto__ == RegExp.prototype)
3573                  *
3574                  * (unless you assign a different object to RegExp.prototype
3575                  * at runtime, in which case, ECMA doesn't specify operation,
3576                  * and you get what you deserve).
3577                  *
3578                  * This same coupling between instance parent and constructor
3579                  * parent turns up everywhere (see jsobj.c's FindConstructor,
3580                  * js_ConstructObject, and js_NewObject).  It's fundamental to
3581                  * the design of the language when you consider multiple global
3582                  * objects and separate compilation and execution, even though
3583                  * it is not specified fully in ECMA.
3584                  */
3585                 if (OBJ_GET_PARENT(cx, obj) != obj2) {
3586                     obj = js_CloneRegExpObject(cx, obj, obj2);
3587                     if (!obj) {
3588                         ok = JS_FALSE;
3589                         goto out;
3590                     }
3591                 }
3592                 rval = OBJECT_TO_JSVAL(obj);
3594                 /* Store the regexp object value in its cloneIndex slot. */
3595                 if (fp->fun) {
3596                     if (!JS_SetReservedSlot(cx, funobj, slot, rval))
3597                         return JS_FALSE;
3598                 } else {
3599                     fp->vars[slot] = rval;
3600                 }
3601             }
3603             PUSH_OPND(rval);
3604             obj = NULL;
3605             break;
3606           }
3608           case JSOP_ZERO:
3609             PUSH_OPND(JSVAL_ZERO);
3610             obj = NULL;
3611             break;
3613           case JSOP_ONE:
3614             PUSH_OPND(JSVAL_ONE);
3615             obj = NULL;
3616             break;
3618           case JSOP_NULL:
3619             PUSH_OPND(JSVAL_NULL);
3620             obj = NULL;
3621             break;
3623           case JSOP_THIS:
3624             PUSH_OPND(OBJECT_TO_JSVAL(fp->thisp));
3625             obj = NULL;
3626             break;
3628           case JSOP_FALSE:
3629             PUSH_OPND(JSVAL_FALSE);
3630             obj = NULL;
3631             break;
3633           case JSOP_TRUE:
3634             PUSH_OPND(JSVAL_TRUE);
3635             obj = NULL;
3636             break;
3638 #if JS_HAS_SWITCH_STATEMENT
3639           case JSOP_TABLESWITCH:
3640             pc2 = pc;
3641             len = GET_JUMP_OFFSET(pc2);
3643             /*
3644              * ECMAv2 forbids conversion of discriminant, so we will skip to
3645              * the default case if the discriminant isn't already an int jsval.
3646              * (This opcode is emitted only for dense jsint-domain switches.)
3647              */
3648             if (cx->version == JSVERSION_DEFAULT ||
3649                 cx->version >= JSVERSION_1_4) {
3650                 rval = POP_OPND();
3651                 if (!JSVAL_IS_INT(rval))
3652                     break;
3653                 i = JSVAL_TO_INT(rval);
3654             } else {
3655                 FETCH_INT(cx, -1, i);
3656                 sp--;
3657             }
3659             pc2 += JUMP_OFFSET_LEN;
3660             low = GET_JUMP_OFFSET(pc2);
3661             pc2 += JUMP_OFFSET_LEN;
3662             high = GET_JUMP_OFFSET(pc2);
3664             i -= low;
3665             if ((jsuint)i < (jsuint)(high - low + 1)) {
3666                 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
3667                 off = (jsint) GET_JUMP_OFFSET(pc2);
3668                 if (off)
3669                     len = off;
3670             }
3671             break;
3673           case JSOP_LOOKUPSWITCH:
3674             lval = POP_OPND();
3675             pc2 = pc;
3676             len = GET_JUMP_OFFSET(pc2);
3678             if (!JSVAL_IS_NUMBER(lval) &&
3679                 !JSVAL_IS_STRING(lval) &&
3680                 !JSVAL_IS_BOOLEAN(lval)) {
3681                 goto advance_pc;
3682             }
3684             pc2 += JUMP_OFFSET_LEN;
3685             npairs = (jsint) GET_ATOM_INDEX(pc2);
3686             pc2 += ATOM_INDEX_LEN;
3688 #define SEARCH_PAIRS(MATCH_CODE)                                              \
3689     while (npairs) {                                                          \
3690         atom = GET_ATOM(cx, script, pc2);                                     \
3691         rval = ATOM_KEY(atom);                                                \
3692         MATCH_CODE                                                            \
3693         if (match) {                                                          \
3694             pc2 += ATOM_INDEX_LEN;                                            \
3695             len = GET_JUMP_OFFSET(pc2);                                       \
3696             goto advance_pc;                                                  \
3697         }                                                                     \
3698         pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN;                              \
3699         npairs--;                                                             \
3700     }
3701             if (JSVAL_IS_STRING(lval)) {
3702                 str  = JSVAL_TO_STRING(lval);
3703                 SEARCH_PAIRS(
3704                     match = (JSVAL_IS_STRING(rval) &&
3705                              ((str2 = JSVAL_TO_STRING(rval)) == str ||
3706                               !js_CompareStrings(str2, str)));
3707                 )
3708             } else if (JSVAL_IS_DOUBLE(lval)) {
3709                 d = *JSVAL_TO_DOUBLE(lval);
3710                 SEARCH_PAIRS(
3711                     match = (JSVAL_IS_DOUBLE(rval) &&
3712                              *JSVAL_TO_DOUBLE(rval) == d);
3713                 )
3714             } else {
3715                 SEARCH_PAIRS(
3716                     match = (lval == rval);
3717                 )
3718             }
3719 #undef SEARCH_PAIRS
3720             break;
3722           case JSOP_TABLESWITCHX:
3723             pc2 = pc;
3724             len = GET_JUMPX_OFFSET(pc2);
3726             /*
3727              * ECMAv2 forbids conversion of discriminant, so we will skip to
3728              * the default case if the discriminant isn't already an int jsval.
3729              * (This opcode is emitted only for dense jsint-domain switches.)
3730              */
3731             if (cx->version == JSVERSION_DEFAULT ||
3732                 cx->version >= JSVERSION_1_4) {
3733                 rval = POP_OPND();
3734                 if (!JSVAL_IS_INT(rval))
3735                     break;
3736                 i = JSVAL_TO_INT(rval);
3737             } else {
3738                 FETCH_INT(cx, -1, i);
3739                 sp--;
3740             }
3742             pc2 += JUMPX_OFFSET_LEN;
3743             low = GET_JUMP_OFFSET(pc2);
3744             pc2 += JUMP_OFFSET_LEN;
3745             high = GET_JUMP_OFFSET(pc2);
3747             i -= low;
3748             if ((jsuint)i < (jsuint)(high - low + 1)) {
3749                 pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i;
3750                 off = (jsint) GET_JUMPX_OFFSET(pc2);
3751                 if (off)
3752                     len = off;
3753             }
3754             break;
3756           case JSOP_LOOKUPSWITCHX:
3757             lval = POP_OPND();
3758             pc2 = pc;
3759             len = GET_JUMPX_OFFSET(pc2);
3761             if (!JSVAL_IS_NUMBER(lval) &&
3762                 !JSVAL_IS_STRING(lval) &&
3763                 !JSVAL_IS_BOOLEAN(lval)) {
3764                 goto advance_pc;
3765             }
3767             pc2 += JUMPX_OFFSET_LEN;
3768             npairs = (jsint) GET_ATOM_INDEX(pc2);
3769             pc2 += ATOM_INDEX_LEN;
3771 #define SEARCH_EXTENDED_PAIRS(MATCH_CODE)                                     \
3772     while (npairs) {                                                          \
3773         atom = GET_ATOM(cx, script, pc2);                                     \
3774         rval = ATOM_KEY(atom);                                                \
3775         MATCH_CODE                                                            \
3776         if (match) {                                                          \
3777             pc2 += ATOM_INDEX_LEN;                                            \
3778             len = GET_JUMPX_OFFSET(pc2);                                      \
3779             goto advance_pc;                                                  \
3780         }                                                                     \
3781         pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN;                             \
3782         npairs--;                                                             \
3783     }
3784             if (JSVAL_IS_STRING(lval)) {
3785                 str  = JSVAL_TO_STRING(lval);
3786                 SEARCH_EXTENDED_PAIRS(
3787                     match = (JSVAL_IS_STRING(rval) &&
3788                              ((str2 = JSVAL_TO_STRING(rval)) == str ||
3789                               !js_CompareStrings(str2, str)));
3790                 )
3791             } else if (JSVAL_IS_DOUBLE(lval)) {
3792                 d = *JSVAL_TO_DOUBLE(lval);
3793                 SEARCH_EXTENDED_PAIRS(
3794                     match = (JSVAL_IS_DOUBLE(rval) &&
3795                              *JSVAL_TO_DOUBLE(rval) == d);
3796                 )
3797             } else {
3798                 SEARCH_EXTENDED_PAIRS(
3799                     match = (lval == rval);
3800                 )
3801             }
3802 #undef SEARCH_EXTENDED_PAIRS
3803             break;
3805           case JSOP_CONDSWITCH:
3806             break;
3808 #endif /* JS_HAS_SWITCH_STATEMENT */
3810 #if JS_HAS_EXPORT_IMPORT
3811           case JSOP_EXPORTALL:
3812             obj = fp->varobj;
3813             ida = JS_Enumerate(cx, obj);
3814             if (!ida) {
3815                 ok = JS_FALSE;
3816             } else {
3817                 for (i = 0, j = ida->length; i < j; i++) {
3818                     id = ida->vector[i];
3819                     ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
3820                     if (!ok)
3821                         break;
3822                     if (!prop)
3823                         continue;
3824                     ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
3825                     if (ok) {
3826                         attrs |= JSPROP_EXPORTED;
3827                         ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
3828                     }
3829                     OBJ_DROP_PROPERTY(cx, obj2, prop);
3830                     if (!ok)
3831                         break;
3832                 }
3833                 JS_DestroyIdArray(cx, ida);
3834             }
3835             break;
3837           case JSOP_EXPORTNAME:
3838             atom = GET_ATOM(cx, script, pc);
3839             id   = (jsid)atom;
3840             obj  = fp->varobj;
3841             ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
3842             if (!ok)
3843                 goto out;
3844             if (!prop) {
3845                 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
3846                                          JSPROP_EXPORTED, NULL);
3847             } else {
3848                 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
3849                 if (ok) {
3850                     attrs |= JSPROP_EXPORTED;
3851                     ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs);
3852                 }
3853                 OBJ_DROP_PROPERTY(cx, obj2, prop);
3854             }
3855             if (!ok)
3856                 goto out;
3857             break;
3859           case JSOP_IMPORTALL:
3860             id = (jsid)JSVAL_VOID;
3861             PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
3862             sp--;
3863             break;
3865           case JSOP_IMPORTPROP:
3866             /* Get an immediate atom naming the property. */
3867             atom = GET_ATOM(cx, script, pc);
3868             id   = (jsid)atom;
3869             PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id));
3870             sp--;
3871             break;
3873           case JSOP_IMPORTELEM:
3874             ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id));
3875             sp -= 2;
3876             break;
3877 #endif /* JS_HAS_EXPORT_IMPORT */
3879           case JSOP_TRAP:
3880             switch (JS_HandleTrap(cx, script, pc, &rval)) {
3881               case JSTRAP_ERROR:
3882                 ok = JS_FALSE;
3883                 goto out;
3884               case JSTRAP_CONTINUE:
3885                 JS_ASSERT(JSVAL_IS_INT(rval));
3886                 op = (JSOp) JSVAL_TO_INT(rval);
3887                 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
3888                 LOAD_INTERRUPT_HANDLER(rt);
3889                 goto do_op;
3890               case JSTRAP_RETURN:
3891                 fp->rval = rval;
3892                 goto out;
3893 #if JS_HAS_EXCEPTIONS
3894               case JSTRAP_THROW:
3895                 cx->throwing = JS_TRUE;
3896                 cx->exception = rval;
3897                 ok = JS_FALSE;
3898                 goto out;
3899 #endif /* JS_HAS_EXCEPTIONS */
3900               default:;
3901             }
3902             LOAD_INTERRUPT_HANDLER(rt);
3903             break;
3905           case JSOP_ARGUMENTS:
3906             SAVE_SP(fp);
3907             ok = js_GetArgsValue(cx, fp, &rval);
3908             if (!ok)
3909                 goto out;
3910             PUSH_OPND(rval);
3911             break;
3913           case JSOP_ARGSUB:
3914             id = (jsid) INT_TO_JSVAL(GET_ARGNO(pc));
3915             SAVE_SP(fp);
3916             ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
3917             if (!ok)
3918                 goto out;
3919             if (!obj) {
3920                 /*
3921                  * If arguments was not overridden by eval('arguments = ...'),
3922                  * set obj to the magic cookie respected by JSOP_PUSHOBJ, just
3923                  * in case this bytecode is part of an 'arguments[i](j, k)' or
3924                  * similar such invocation sequence, where the function that
3925                  * is invoked expects its 'this' parameter to be the caller's
3926                  * arguments object.
3927                  */
3928                 obj = LAZY_ARGS_THISP;
3929             }
3930             PUSH_OPND(rval);
3931             break;
3933 #undef LAZY_ARGS_THISP
3935           case JSOP_ARGCNT:
3936             id = (jsid) rt->atomState.lengthAtom;
3937             SAVE_SP(fp);
3938             ok = js_GetArgsProperty(cx, fp, id, &obj, &rval);
3939             if (!ok)
3940                 goto out;
3941             PUSH_OPND(rval);
3942             break;
3944           case JSOP_GETARG:
3945             slot = GET_ARGNO(pc);
3946             JS_ASSERT(slot < fp->fun->nargs);
3947             PUSH_OPND(fp->argv[slot]);
3948             obj = NULL;
3949             break;
3951           case JSOP_SETARG:
3952             slot = GET_ARGNO(pc);
3953             JS_ASSERT(slot < fp->fun->nargs);
3954             vp = &fp->argv[slot];
3955             GC_POKE(cx, *vp);
3956             *vp = FETCH_OPND(-1);
3957             obj = NULL;
3958             break;
3960           case JSOP_GETVAR:
3961             slot = GET_VARNO(pc);
3962             JS_ASSERT(slot < fp->fun->nvars);
3963             PUSH_OPND(fp->vars[slot]);
3964             obj = NULL;
3965             break;
3967           case JSOP_SETVAR:
3968             slot = GET_VARNO(pc);
3969             JS_ASSERT(slot < fp->fun->nvars);
3970             vp = &fp->vars[slot];
3971             GC_POKE(cx, *vp);
3972             *vp = FETCH_OPND(-1);
3973             obj = NULL;
3974             break;
3976           case JSOP_GETGVAR:
3977             slot = GET_VARNO(pc);
3978             JS_ASSERT(slot < fp->nvars);
3979             lval = fp->vars[slot];
3980             if (JSVAL_IS_NULL(lval)) {
3981                 op = JSOP_NAME;
3982                 goto do_op;
3983             }
3984             slot = JSVAL_TO_INT(lval);
3985             obj = fp->varobj;
3986             rval = OBJ_GET_SLOT(cx, obj, slot);
3987             PUSH_OPND(rval);
3988             break;
3990           case JSOP_SETGVAR:
3991             slot = GET_VARNO(pc);
3992             JS_ASSERT(slot < fp->nvars);
3993             rval = FETCH_OPND(-1);
3994             lval = fp->vars[slot];
3995             obj = fp->varobj;
3996             if (JSVAL_IS_NULL(lval)) {
3997                 /*
3998                  * Inline-clone and specialize JSOP_SETNAME code here because
3999                  * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
4000                  * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
4001                  */
4002                 atom = GET_ATOM(cx, script, pc);
4003                 id = (jsid)atom;
4004                 SAVE_SP(fp);
4005                 CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval));
4006                 if (!ok)
4007                     goto out;
4008                 STORE_OPND(-1, rval);
4009             } else {
4010                 slot = JSVAL_TO_INT(lval);
4011                 GC_POKE(cx, obj->slots[slot]);
4012                 OBJ_SET_SLOT(cx, obj, slot, rval);
4013             }
4014             break;
4016           case JSOP_DEFCONST:
4017           case JSOP_DEFVAR:
4018           {
4019             jsatomid atomIndex;
4021             atomIndex = GET_ATOM_INDEX(pc);
4022             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4023             obj = fp->varobj;
4024             attrs = JSPROP_ENUMERATE;
4025             if (!(fp->flags & JSFRAME_EVAL))
4026                 attrs |= JSPROP_PERMANENT;
4027             if (op == JSOP_DEFCONST)
4028                 attrs |= JSPROP_READONLY;
4030             /* Lookup id in order to check for redeclaration problems. */
4031             id = (jsid)atom;
4032             ok = js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop);
4033             if (!ok)
4034                 goto out;
4036             /* Bind a variable only if it's not yet defined. */
4037             if (!prop) {
4038                 ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL,
4039                                          attrs, &prop);
4040                 if (!ok)
4041                     goto out;
4042                 JS_ASSERT(prop);
4043                 obj2 = obj;
4044             }
4046             /*
4047              * Try to optimize a property we either just created, or found
4048              * directly in the global object, that is permanent, has a slot,
4049              * and has stub getter and setter, into a "fast global" accessed
4050              * by the JSOP_*GVAR opcodes.
4051              */
4052             if (script->numGlobalVars &&
4053                 (attrs & JSPROP_PERMANENT) &&
4054                 obj2 == obj &&
4055                 OBJ_IS_NATIVE(obj)) {
4056                 sprop = (JSScopeProperty *) prop;
4057                 if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
4058                     SPROP_HAS_STUB_GETTER(sprop) &&
4059                     SPROP_HAS_STUB_SETTER(sprop)) {
4060                     /*
4061                      * Fast globals use fp->vars to map the global name's
4062                      * atomIndex to the permanent fp->varobj slot number,
4063                      * tagged as a jsval.  The atomIndex for the global's
4064                      * name literal is identical to its fp->vars index.
4065                      */
4066                     fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
4067                 }
4068             }
4070             OBJ_DROP_PROPERTY(cx, obj2, prop);
4071             break;
4072           }
4074           case JSOP_DEFFUN:
4075           {
4076             jsatomid atomIndex;
4077             uintN flags;
4079             atomIndex = GET_ATOM_INDEX(pc);
4080             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4081             obj = ATOM_TO_OBJECT(atom);
4082             fun = (JSFunction *) JS_GetPrivate(cx, obj);
4083             id = (jsid) fun->atom;
4085             /*
4086              * We must be at top-level (either outermost block that forms a
4087              * function's body, or a global) scope, not inside an expression
4088              * (JSOP_{ANON,NAMED}FUNOBJ) or compound statement (JSOP_CLOSURE)
4089              * in the same compilation unit (ECMA Program).
4090              *
4091              * However, we could be in a Program being eval'd from inside a
4092              * with statement, so we need to distinguish variables object from
4093              * scope chain head.  Hence the two assignments to parent below.
4094              * First we make sure the function object we're defining has the
4095              * right scope chain.  Then we define its name in fp->varobj.
4096              *
4097              * If static link is not current scope, clone fun's object to link
4098              * to the current scope via parent.  This clause exists to enable
4099              * sharing of compiled functions among multiple equivalent scopes,
4100              * splitting the cost of compilation evenly among the scopes and
4101              * amortizing it over a number of executions.  Examples include XUL
4102              * scripts and event handlers shared among Mozilla chrome windows,
4103              * and server-side JS user-defined functions shared among requests.
4104              *
4105              * NB: The Script object exposes compile and exec in the language,
4106              * such that this clause introduces an incompatible change from old
4107              * JS versions that supported Script.  Such a JS version supported
4108              * executing a script that defined and called functions scoped by
4109              * the compile-time static link, not by the exec-time scope chain.
4110              *
4111              * We sacrifice compatibility, breaking such scripts, in order to
4112              * promote compile-cost sharing and amortizing, and because Script
4113              * is not and will not be standardized.
4114              */
4115             parent = fp->scopeChain;
4116             if (OBJ_GET_PARENT(cx, obj) != parent) {
4117                 obj = js_CloneFunctionObject(cx, obj, parent);
4118                 if (!obj) {
4119                     ok = JS_FALSE;
4120                     goto out;
4121                 }
4122             }
4124             /*
4125              * ECMA requires functions defined when entering Global code to be
4126              * permanent, and functions defined when entering Eval code to be
4127              * impermanent.
4128              */
4129             attrs = JSPROP_ENUMERATE;
4130             if (!(fp->flags & JSFRAME_EVAL))
4131                 attrs |= JSPROP_PERMANENT;
4133             /*
4134              * Load function flags that are also property attributes.  Getters
4135              * and setters do not need a slot, their value is stored elsewhere
4136              * in the property itself, not in obj->slots.
4137              */
4138             flags = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
4139             if (flags)
4140                 attrs |= flags | JSPROP_SHARED;
4142             /*
4143              * Check for a const property of the same name -- or any kind
4144              * of property if executing with the strict option.  We check
4145              * here at runtime as well as at compile-time, to handle eval
4146              * as well as multiple HTML script tags.
4147              */
4148             parent = fp->varobj;
4149             ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
4150             if (!ok)
4151                 goto out;
4153             ok = OBJ_DEFINE_PROPERTY(cx, parent, id,
4154                                      flags ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),
4155                                      (flags & JSFUN_GETTER)
4156                                      ? (JSPropertyOp) obj
4157                                      : NULL,
4158                                      (flags & JSFUN_SETTER)
4159                                      ? (JSPropertyOp) obj
4160                                      : NULL,
4161                                      attrs,
4162                                      &prop);
4163             if (!ok)
4164                 goto out;
4165             if (attrs == (JSPROP_ENUMERATE | JSPROP_PERMANENT) &&
4166                 script->numGlobalVars) {
4167                 /*
4168                  * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals
4169                  * use fp->vars to map the global function name's atomIndex to
4170                  * its permanent fp->varobj slot number, tagged as a jsval.
4171                  */
4172                 sprop = (JSScopeProperty *) prop;
4173                 fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
4174             }
4175             OBJ_DROP_PROPERTY(cx, parent, prop);
4176             break;
4177           }
4179 #if JS_HAS_LEXICAL_CLOSURE
4180           case JSOP_DEFLOCALFUN:
4181             /*
4182              * Define a local function (i.e., one nested at the top level of
4183              * another function), parented by the current scope chain, and
4184              * stored in a local variable slot that the compiler allocated.
4185              * This is an optimization over JSOP_DEFFUN that avoids requiring
4186              * a call object for the outer function's activation.
4187              */
4188             pc2 = pc;
4189             slot = GET_VARNO(pc2);
4190             pc2 += VARNO_LEN;
4191             atom = GET_ATOM(cx, script, pc2);
4192             obj = ATOM_TO_OBJECT(atom);
4193             fun = (JSFunction *) JS_GetPrivate(cx, obj);
4195             parent = fp->scopeChain;
4196             if (OBJ_GET_PARENT(cx, obj) != parent) {
4197                 obj = js_CloneFunctionObject(cx, obj, parent);
4198                 if (!obj) {
4199                     ok = JS_FALSE;
4200                     goto out;
4201                 }
4202             }
4203             fp->vars[slot] = OBJECT_TO_JSVAL(obj);
4204             break;
4206           case JSOP_ANONFUNOBJ:
4207             /* Push the specified function object literal. */
4208             atom = GET_ATOM(cx, script, pc);
4209             obj = ATOM_TO_OBJECT(atom);
4211             /* If re-parenting, push a clone of the function object. */
4212             parent = fp->scopeChain;
4213             if (OBJ_GET_PARENT(cx, obj) != parent) {
4214                 obj = js_CloneFunctionObject(cx, obj, parent);
4215                 if (!obj) {
4216                     ok = JS_FALSE;
4217                     goto out;
4218                 }
4219             }
4220             PUSH_OPND(OBJECT_TO_JSVAL(obj));
4221             break;
4223           case JSOP_NAMEDFUNOBJ:
4224             /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */
4225             atom = GET_ATOM(cx, script, pc);
4226             rval = ATOM_KEY(atom);
4227             JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval));
4229             /*
4230              * 1. Create a new object as if by the expression new Object().
4231              * 2. Add Result(1) to the front of the scope chain.
4232              *
4233              * Step 2 is achieved by making the new object's parent be the
4234              * current scope chain, and then making the new object the parent
4235              * of the Function object clone.
4236              */
4237             SAVE_SP(fp);
4238             parent = js_ConstructObject(cx, &js_ObjectClass, NULL,
4239                                         fp->scopeChain, 0, NULL);
4240             if (!parent) {
4241                 ok = JS_FALSE;
4242                 goto out;
4243             }
4245             /*
4246              * 3. Create a new Function object as specified in section 13.2
4247              * with [parameters and body specified by the function expression
4248              * that was parsed by the compiler into a Function object, and
4249              * saved in the script's atom map].
4250              */
4251             obj = js_CloneFunctionObject(cx, JSVAL_TO_OBJECT(rval), parent);
4252             if (!obj) {
4253                 ok = JS_FALSE;
4254                 goto out;
4255             }
4257             /*
4258              * 4. Create a property in the object Result(1).  The property's
4259              * name is [fun->atom, the identifier parsed by the compiler],
4260              * value is Result(3), and attributes are { DontDelete, ReadOnly }.
4261              */
4262             fun = (JSFunction *) JS_GetPrivate(cx, obj);
4263             attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
4264             if (attrs)
4265                 attrs |= JSPROP_SHARED;
4266             ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom,
4267                                      attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),
4268                                      (attrs & JSFUN_GETTER)
4269                                      ? (JSPropertyOp) obj
4270                                      : NULL,
4271                                      (attrs & JSFUN_SETTER)
4272                                      ? (JSPropertyOp) obj
4273                                      : NULL,
4274                                      attrs |
4275                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |
4276                                      JSPROP_READONLY,
4277                                      NULL);
4278             if (!ok) {
4279                 cx->newborn[GCX_OBJECT] = NULL;
4280                 goto out;
4281             }
4283             /*
4284              * 5. Remove Result(1) from the front of the scope chain [no-op].
4285              * 6. Return Result(3).
4286              */
4287             PUSH_OPND(OBJECT_TO_JSVAL(obj));
4288             break;
4290           case JSOP_CLOSURE:
4291           {
4292             jsatomid atomIndex;
4294             /*
4295              * ECMA ed. 3 extension: a named function expression in a compound
4296              * statement (not at the top statement level of global code, or at
4297              * the top level of a function body).
4298              *
4299              * Get immediate operand atom, which is a function object literal.
4300              * From it, get the function to close.
4301              */
4302             atomIndex = GET_ATOM_INDEX(pc);
4303             atom = js_GetAtom(cx, &script->atomMap, atomIndex);
4304             JS_ASSERT(JSVAL_IS_FUNCTION(cx, ATOM_KEY(atom)));
4305             obj = ATOM_TO_OBJECT(atom);
4307             /*
4308              * Clone the function object with the current scope chain as the
4309              * clone's parent.  The original function object is the prototype
4310              * of the clone.  Do this only if re-parenting; the compiler may
4311              * have seen the right parent already and created a sufficiently
4312              * well-scoped function object.
4313              */
4314             parent = fp->scopeChain;
4315             if (OBJ_GET_PARENT(cx, obj) != parent) {
4316                 obj = js_CloneFunctionObject(cx, obj, parent);
4317                 if (!obj) {
4318                     ok = JS_FALSE;
4319                     goto out;
4320                 }
4321             }
4323             /*
4324              * Make a property in fp->varobj with id fun->atom and value obj,
4325              * unless fun is a getter or setter (in which case, obj is cast to
4326              * a JSPropertyOp and passed accordingly).
4327              */
4328             fun = (JSFunction *) JS_GetPrivate(cx, obj);
4329             attrs = fun->flags & (JSFUN_GETTER | JSFUN_SETTER);
4330             if (attrs)
4331                 attrs |= JSPROP_SHARED;
4332             parent = fp->varobj;
4333             ok = OBJ_DEFINE_PROPERTY(cx, parent, (jsid)fun->atom,
4334                                      attrs ? JSVAL_VOID : OBJECT_TO_JSVAL(obj),
4335                                      (attrs & JSFUN_GETTER)
4336                                      ? (JSPropertyOp) obj
4337                                      : NULL,
4338                                      (attrs & JSFUN_SETTER)
4339                                      ? (JSPropertyOp) obj
4340                                      : NULL,
4341                                      attrs | JSPROP_ENUMERATE
4342                                            | JSPROP_PERMANENT,
4343                                      &prop);
4344             if (!ok) {
4345                 cx->newborn[GCX_OBJECT] = NULL;
4346                 goto out;
4347             }
4348             if (attrs == 0 && script->numGlobalVars) {
4349                 /*
4350                  * As with JSOP_DEFVAR and JSOP_DEFCONST (above), fast globals
4351                  * use fp->vars to map the global function name's atomIndex to
4352                  * its permanent fp->varobj slot number, tagged as a jsval.
4353                  */
4354                 sprop = (JSScopeProperty *) prop;
4355                 fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot);
4356             }
4357             OBJ_DROP_PROPERTY(cx, parent, prop);
4358             break;
4359           }
4360 #endif /* JS_HAS_LEXICAL_CLOSURE */
4362 #if JS_HAS_GETTER_SETTER
4363           case JSOP_GETTER:
4364           case JSOP_SETTER:
4365             JS_ASSERT(len == 1);
4366             op2 = (JSOp) *++pc;
4367             cs = &js_CodeSpec[op2];
4368             len = cs->length;
4369             switch (op2) {
4370               case JSOP_SETNAME:
4371               case JSOP_SETPROP:
4372                 atom = GET_ATOM(cx, script, pc);
4373                 id   = (jsid)atom;
4374                 i = -1;
4375                 rval = FETCH_OPND(i);
4376                 goto gs_pop_lval;
4378               case JSOP_SETELEM:
4379                 rval = FETCH_OPND(-1);
4380                 i = -2;
4381                 FETCH_ELEMENT_ID(i, id);
4382               gs_pop_lval:
4383                 lval = FETCH_OPND(i-1);
4384                 VALUE_TO_OBJECT(cx, lval, obj);
4385                 break;
4387 #if JS_HAS_INITIALIZERS
4388               case JSOP_INITPROP:
4389                 JS_ASSERT(sp - fp->spbase >= 2);
4390                 i = -1;
4391                 rval = FETCH_OPND(i);
4392                 atom = GET_ATOM(cx, script, pc);
4393                 id   = (jsid)atom;
4394                 goto gs_get_lval;
4396               case JSOP_INITELEM:
4397                 JS_ASSERT(sp - fp->spbase >= 3);
4398                 rval = FETCH_OPND(-1);
4399                 i = -2;
4400                 FETCH_ELEMENT_ID(i, id);
4401               gs_get_lval:
4402                 lval = FETCH_OPND(i-1);
4403                 JS_ASSERT(JSVAL_IS_OBJECT(lval));
4404                 obj = JSVAL_TO_OBJECT(lval);
4405                 break;
4406 #endif /* JS_HAS_INITIALIZERS */
4408               default:
4409                 JS_ASSERT(0);
4410             }
4412             if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
4413                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4414                                      JSMSG_BAD_GETTER_OR_SETTER,
4415                                      (op == JSOP_GETTER)
4416                                      ? js_getter_str
4417                                      : js_setter_str);
4418                 ok = JS_FALSE;
4419                 goto out;
4420             }
4422             /*
4423              * Getters and setters are just like watchpoints from an access
4424              * control point of view.
4425              */
4426             ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs);
4427             if (!ok)
4428                 goto out;
4430             if (op == JSOP_GETTER) {
4431                 getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval);
4432                 setter = NULL;
4433                 attrs = JSPROP_GETTER;
4434             } else {
4435                 getter = NULL;
4436                 setter = (JSPropertyOp) JSVAL_TO_OBJECT(rval);
4437                 attrs = JSPROP_SETTER;
4438             }
4439             attrs |= JSPROP_ENUMERATE | JSPROP_SHARED;
4441             /* Check for a readonly or permanent property of the same name. */
4442             ok = js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL);
4443             if (!ok)
4444                 goto out;
4446             ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,
4447                                      attrs, NULL);
4448             if (!ok)
4449                 goto out;
4451             sp += i;
4452             if (cs->ndefs)
4453                 STORE_OPND(-1, rval);
4454             break;
4455 #endif /* JS_HAS_GETTER_SETTER */
4457 #if JS_HAS_INITIALIZERS
4458           case JSOP_NEWINIT:
4459             argc = 0;
4460             fp->sharpDepth++;
4461             goto do_new;
4463           case JSOP_ENDINIT:
4464             if (--fp->sharpDepth == 0)
4465                 fp->sharpArray = NULL;
4467             /* Re-set the newborn root to the top of this object tree. */
4468             JS_ASSERT(sp - fp->spbase >= 1);
4469             lval = FETCH_OPND(-1);
4470             JS_ASSERT(JSVAL_IS_OBJECT(lval));
4471             cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval);
4472             break;
4474           case JSOP_INITPROP:
4475             /* Pop the property's value into rval. */
4476             JS_ASSERT(sp - fp->spbase >= 2);
4477             rval = FETCH_OPND(-1);
4479             /* Get the immediate property name into id. */
4480             atom = GET_ATOM(cx, script, pc);
4481             id   = (jsid)atom;
4482             i = -1;
4483             goto do_init;
4485           case JSOP_INITELEM:
4486             /* Pop the element's value into rval. */
4487             JS_ASSERT(sp - fp->spbase >= 3);
4488             rval = FETCH_OPND(-1);
4490             /* Pop and conditionally atomize the element id. */
4491             FETCH_ELEMENT_ID(-2, id);
4492             i = -2;
4494           do_init:
4495             /* Find the object being initialized at top of stack. */
4496             lval = FETCH_OPND(i-1);
4497             JS_ASSERT(JSVAL_IS_OBJECT(lval));
4498             obj = JSVAL_TO_OBJECT(lval);
4500             /* Set the property named by obj[id] to rval. */
4501             ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
4502             if (!ok)
4503                 goto out;
4504             sp += i;
4505             break;
4507 #if JS_HAS_SHARP_VARS
4508           case JSOP_DEFSHARP:
4509             obj = fp->sharpArray;
4510             if (!obj) {
4511                 obj = js_NewArrayObject(cx, 0, NULL);
4512                 if (!obj) {
4513                     ok = JS_FALSE;
4514                     goto out;
4515                 }
4516                 fp->sharpArray = obj;
4517             }
4518             i = (jsint) GET_ATOM_INDEX(pc);
4519             id = (jsid) INT_TO_JSVAL(i);
4520             rval = FETCH_OPND(-1);
4521             if (JSVAL_IS_PRIMITIVE(rval)) {
4522                 char numBuf[12];
4523                 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
4524                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4525                                      JSMSG_BAD_SHARP_DEF, numBuf);
4526                 ok = JS_FALSE;
4527                 goto out;
4528             }
4529             ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);
4530             if (!ok)
4531                 goto out;
4532             break;
4534           case JSOP_USESHARP:
4535             i = (jsint) GET_ATOM_INDEX(pc);
4536             id = (jsid) INT_TO_JSVAL(i);
4537             obj = fp->sharpArray;
4538             if (!obj) {
4539                 rval = JSVAL_VOID;
4540             } else {
4541                 ok = OBJ_GET_PROPERTY(cx, obj, id, &rval);
4542                 if (!ok)
4543                     goto out;
4544             }
4545             if (!JSVAL_IS_OBJECT(rval)) {
4546                 char numBuf[12];
4547                 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
4548                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4549                                      JSMSG_BAD_SHARP_USE, numBuf);
4550                 ok = JS_FALSE;
4551                 goto out;
4552             }
4553             PUSH_OPND(rval);
4554             break;
4555 #endif /* JS_HAS_SHARP_VARS */
4556 #endif /* JS_HAS_INITIALIZERS */
4558 #if JS_HAS_EXCEPTIONS
4559           /* No-ops for ease of decompilation and jit'ing. */
4560           case JSOP_TRY:
4561           case JSOP_FINALLY:
4562             break;
4564           /* Reset the stack to the given depth. */
4565           case JSOP_SETSP:
4566             i = (jsint) GET_ATOM_INDEX(pc);
4567             JS_ASSERT(i >= 0);
4568             sp = fp->spbase + i;
4569             break;
4571           case JSOP_GOSUB:
4572             i = PTRDIFF(pc, script->main, jsbytecode) + len;
4573             len = GET_JUMP_OFFSET(pc);
4574             PUSH(INT_TO_JSVAL(i));
4575             break;
4577           case JSOP_GOSUBX:
4578             i = PTRDIFF(pc, script->main, jsbytecode) + len;
4579             len = GET_JUMPX_OFFSET(pc);
4580             PUSH(INT_TO_JSVAL(i));
4581             break;
4583           case JSOP_RETSUB:
4584             rval = POP();
4585             JS_ASSERT(JSVAL_IS_INT(rval));
4586             i = JSVAL_TO_INT(rval);
4587             pc = script->main + i;
4588             len = 0;
4589             break;
4591           case JSOP_EXCEPTION:
4592             PUSH(cx->exception);
4593             break;
4595           case JSOP_THROW:
4596             cx->throwing = JS_TRUE;
4597             cx->exception = POP_OPND();
4598             ok = JS_FALSE;
4599             /* let the code at out try to catch the exception. */
4600             goto out;
4602           case JSOP_INITCATCHVAR:
4603             /* Pop the property's value into rval. */
4604             JS_ASSERT(sp - fp->spbase >= 2);
4605             rval = POP_OPND();
4607             /* Get the immediate catch variable name into id. */
4608             atom = GET_ATOM(cx, script, pc);
4609             id   = (jsid)atom;
4611             /* Find the object being initialized at top of stack. */
4612             lval = FETCH_OPND(-1);
4613             JS_ASSERT(JSVAL_IS_OBJECT(lval));
4614             obj = JSVAL_TO_OBJECT(lval);
4616             /* Define obj[id] to contain rval and to be permanent. */
4617             ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL,
4618                                      JSPROP_PERMANENT, NULL);
4619             if (!ok)
4620                 goto out;
4621             break;
4622 #endif /* JS_HAS_EXCEPTIONS */
4624 #if JS_HAS_INSTANCEOF
4625           case JSOP_INSTANCEOF:
4626             rval = FETCH_OPND(-1);
4627             if (JSVAL_IS_PRIMITIVE(rval)) {
4628                 SAVE_SP(fp);
4629                 str = js_DecompileValueGenerator(cx, -1, rval, NULL);
4630                 if (str) {
4631                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4632                                          JSMSG_BAD_INSTANCEOF_RHS,
4633                                          JS_GetStringBytes(str));
4634                 }
4635                 ok = JS_FALSE;
4636                 goto out;
4637             }
4638             obj = JSVAL_TO_OBJECT(rval);
4639             lval = FETCH_OPND(-2);
4640             cond = JS_FALSE;
4641             if (obj->map->ops->hasInstance) {
4642                 SAVE_SP(fp);
4643                 ok = obj->map->ops->hasInstance(cx, obj, lval, &cond);
4644                 if (!ok)
4645                     goto out;
4646             }
4647             sp--;
4648             STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
4649             break;
4650 #endif /* JS_HAS_INSTANCEOF */
4652 #if JS_HAS_DEBUGGER_KEYWORD
4653           case JSOP_DEBUGGER:
4654           {
4655             JSTrapHandler handler = rt->debuggerHandler;
4656             if (handler) {
4657                 SAVE_SP(fp);
4658                 switch (handler(cx, script, pc, &rval,
4659                                 rt->debuggerHandlerData)) {
4660                   case JSTRAP_ERROR:
4661                     ok = JS_FALSE;
4662                     goto out;
4663                   case JSTRAP_CONTINUE:
4664                     break;
4665                   case JSTRAP_RETURN:
4666                     fp->rval = rval;
4667                     goto out;
4668 #if JS_HAS_EXCEPTIONS
4669                   case JSTRAP_THROW:
4670                     cx->throwing = JS_TRUE;
4671                     cx->exception = rval;
4672                     ok = JS_FALSE;
4673                     goto out;
4674 #endif /* JS_HAS_EXCEPTIONS */
4675                   default:;
4676                 }
4677                 LOAD_INTERRUPT_HANDLER(rt);
4678             }
4679             break;
4680           }
4681 #endif /* JS_HAS_DEBUGGER_KEYWORD */
4683           default: {
4684             char numBuf[12];
4685             JS_snprintf(numBuf, sizeof numBuf, "%d", op);
4686             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4687                                  JSMSG_BAD_BYTECODE, numBuf);
4688             ok = JS_FALSE;
4689             goto out;
4690           }
4691         }
4693     advance_pc:
4694         pc += len;
4696 #ifdef DEBUG
4697         if (tracefp) {
4698             intN ndefs, n;
4699             jsval *siter;
4701             ndefs = cs->ndefs;
4702             if (ndefs) {
4703                 SAVE_SP(fp);
4704                 for (n = -ndefs; n < 0; n++) {
4705                     str = js_DecompileValueGenerator(cx, n, sp[n], NULL);
4706                     if (str) {
4707                         fprintf(tracefp, "%s %s",
4708                                 (n == -ndefs) ? "  output:" : ",",
4709                                 JS_GetStringBytes(str));
4710                     }
4711                 }
4712                 fprintf(tracefp, " @ %d\n", sp - fp->spbase);
4713             }
4714             fprintf(tracefp, "  stack: ");
4715             for (siter = fp->spbase; siter < sp; siter++) {
4716                 str = js_ValueToSource(cx, *siter);
4717                 fprintf(tracefp, "%s ",
4718                         str ? JS_GetStringBytes(str) : "<null>");
4719             }
4720             fputc('\n', tracefp);
4721         }
4722 #endif
4723     }
4724 out:
4726 #if JS_HAS_EXCEPTIONS
4727     /*
4728      * Has an exception been raised?
4729      */
4730     if (!ok && cx->throwing) {
4731         /*
4732          * Call debugger throw hook if set (XXX thread safety?).
4733          */
4734         JSTrapHandler handler = rt->throwHook;
4735         if (handler) {
4736             SAVE_SP(fp);
4737             switch (handler(cx, script, pc, &rval, rt->throwHookData)) {
4738               case JSTRAP_ERROR:
4739                 cx->throwing = JS_FALSE;
4740                 goto no_catch;
4741               case JSTRAP_RETURN:
4742                 ok = JS_TRUE;
4743                 cx->throwing = JS_FALSE;
4744                 fp->rval = rval;
4745                 goto no_catch;
4746               case JSTRAP_THROW:
4747                 cx->exception = rval;
4748               case JSTRAP_CONTINUE:
4749               default:;
4750             }
4751             LOAD_INTERRUPT_HANDLER(rt);
4752         }
4754         /*
4755          * Look for a try block within this frame that can catch the exception.
4756          */
4757         SCRIPT_FIND_CATCH_START(script, pc, pc);
4758         if (pc) {
4759             len = 0;
4760             cx->throwing = JS_FALSE;    /* caught */
4761             ok = JS_TRUE;
4762             goto advance_pc;
4763         }
4764     }
4765 no_catch:
4766 #endif
4768     /*
4769      * Check whether control fell off the end of a lightweight function, or an
4770      * exception thrown under such a function was not caught by it.  If so, go
4771      * to the inline code under JSOP_RETURN.
4772      */
4773     if (inlineCallCount)
4774         goto inline_return;
4776     /*
4777      * Reset sp before freeing stack slots, because our caller may GC soon.
4778      * Clear spbase to indicate that we've popped the 2 * depth operand slots.
4779      * Restore the previous frame's execution state.
4780      */
4781     fp->sp = fp->spbase;
4782     fp->spbase = NULL;
4783     js_FreeRawStack(cx, mark);
4784     if (cx->version == currentVersion && currentVersion != originalVersion)
4785         JS_SetVersion(cx, originalVersion);
4786     cx->interpLevel--;
4787     return ok;
4789 atom_not_defined:
4790     {
4791         const char *printable = js_AtomToPrintableString(cx, atom);
4792         if (printable)
4793             js_ReportIsNotDefined(cx, printable);
4794         ok = JS_FALSE;
4795         goto out;
4796     }