/[jscoverage]/trunk/js/jsinterp.cpp
ViewVC logotype

Diff of /trunk/js/jsinterp.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 332 by siliconforks, Thu Oct 23 19:03:33 2008 UTC revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC
# Line 1  Line 1 
1  /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-  /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2   * vim: set ts=8 sw=4 et tw=79:   * vim: set ts=8 sw=4 et tw=79:
3   *   *
4   * ***** BEGIN LICENSE BLOCK *****   * ***** BEGIN LICENSE BLOCK *****
# Line 41  Line 41 
41  /*  /*
42   * JavaScript bytecode interpreter.   * JavaScript bytecode interpreter.
43   */   */
 #include "jsstddef.h"  
44  #include <stdio.h>  #include <stdio.h>
45  #include <string.h>  #include <string.h>
46  #include <math.h>  #include <math.h>
47  #include "jstypes.h"  #include "jstypes.h"
48    #include "jsstdint.h"
49  #include "jsarena.h" /* Added by JSIFY */  #include "jsarena.h" /* Added by JSIFY */
50  #include "jsutil.h" /* Added by JSIFY */  #include "jsutil.h" /* Added by JSIFY */
51  #include "jsprf.h"  #include "jsprf.h"
# Line 54  Line 54 
54  #include "jsatom.h"  #include "jsatom.h"
55  #include "jsbool.h"  #include "jsbool.h"
56  #include "jscntxt.h"  #include "jscntxt.h"
57    #include "jsdate.h"
58  #include "jsversion.h"  #include "jsversion.h"
59  #include "jsdbgapi.h"  #include "jsdbgapi.h"
60  #include "jsfun.h"  #include "jsfun.h"
# Line 70  Line 71 
71  #include "jsstr.h"  #include "jsstr.h"
72  #include "jsstaticcheck.h"  #include "jsstaticcheck.h"
73  #include "jstracer.h"  #include "jstracer.h"
74    #include "jslibmath.h"
75    #include "jsvector.h"
76    #include "jsstrinlines.h"
77    
78  #ifdef INCLUDE_MOZILLA_DTRACE  #ifdef INCLUDE_MOZILLA_DTRACE
79  #include "jsdtracef.h"  #include "jsdtracef.h"
# Line 79  Line 83 
83  #include "jsxml.h"  #include "jsxml.h"
84  #endif  #endif
85    
86    #include "jsatominlines.h"
87    #include "jsscriptinlines.h"
88    
89  #include "jsautooplen.h"  #include "jsautooplen.h"
90    
91  /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */  /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
92  #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___  #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
93    
94  uint32  JS_REQUIRES_STACK JSPropCacheEntry *
95  js_GenerateShape(JSContext *cx, JSBool gcLocked)  js_FillPropertyCache(JSContext *cx, JSObject *obj,
96  {                       uintN scopeIndex, uintN protoIndex, JSObject *pobj,
97      JSRuntime *rt;                       JSScopeProperty *sprop, JSBool adding)
     uint32 shape;  
   
     rt = cx->runtime;  
     shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);  
     JS_ASSERT(shape != 0);  
     if (shape & SHAPE_OVERFLOW_BIT) {  
         rt->gcPoke = JS_TRUE;  
         js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);  
         shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);  
         JS_ASSERT(shape != 0);  
         JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,  
                      JS_PROPERTY_CACHE(cx).disabled);  
     }  
     return shape;  
 }  
   
 void  
 js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,  
                      uintN scopeIndex, uintN protoIndex,  
                      JSObject *pobj, JSScopeProperty *sprop,  
                      JSPropCacheEntry **entryp)  
98  {  {
99      JSPropertyCache *cache;      JSPropertyCache *cache;
100      jsbytecode *pc;      jsbytecode *pc;
101      JSScope *scope;      JSScope *scope;
102        jsuword kshape, vshape, khash;
103      JSOp op;      JSOp op;
104      const JSCodeSpec *cs;      const JSCodeSpec *cs;
105      jsuword vword;      jsuword vword;
106      ptrdiff_t pcoff;      ptrdiff_t pcoff;
     jsuword khash;  
107      JSAtom *atom;      JSAtom *atom;
108      JSPropCacheEntry *entry;      JSPropCacheEntry *entry;
109    
110      JS_ASSERT(!cx->runtime->gcRunning);      JS_ASSERT(!cx->runtime->gcRunning);
111      cache = &JS_PROPERTY_CACHE(cx);      cache = &JS_PROPERTY_CACHE(cx);
112      pc = cx->fp->regs->pc;  
113      if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {      /* FIXME bug 489098: consider enabling the property cache for eval. */
114        if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
115          PCMETER(cache->disfills++);          PCMETER(cache->disfills++);
116          *entryp = NULL;          return JS_NO_PROP_CACHE_FILL;
         return;  
117      }      }
118    
119      /*      /*
# Line 135  Line 121 
121       * from pobj's scope (via unwatch or delete, e.g.).       * from pobj's scope (via unwatch or delete, e.g.).
122       */       */
123      scope = OBJ_SCOPE(pobj);      scope = OBJ_SCOPE(pobj);
124      JS_ASSERT(scope->object == pobj);      if (!scope->has(sprop)) {
     if (!SCOPE_HAS_PROPERTY(scope, sprop)) {  
125          PCMETER(cache->oddfills++);          PCMETER(cache->oddfills++);
126          *entryp = NULL;          return JS_NO_PROP_CACHE_FILL;
         return;  
127      }      }
128    
129      /*      /*
# Line 147  Line 131 
131       * and setter hooks can change the prototype chain using JS_SetPrototype       * and setter hooks can change the prototype chain using JS_SetPrototype
132       * after js_LookupPropertyWithFlags has returned the nominal protoIndex,       * after js_LookupPropertyWithFlags has returned the nominal protoIndex,
133       * we have to validate protoIndex if it is non-zero. If it is zero, then       * we have to validate protoIndex if it is non-zero. If it is zero, then
134       * we know thanks to the SCOPE_HAS_PROPERTY test above, and from the fact       * we know thanks to the scope->has test above, combined with the fact that
135       * that obj == pobj, that protoIndex is invariant.       * obj == pobj, that protoIndex is invariant.
136       *       *
137       * The scopeIndex can't be wrong. We require JS_SetParent calls to happen       * The scopeIndex can't be wrong. We require JS_SetParent calls to happen
138       * before any running script might consult a parent-linked scope chain. If       * before any running script might consult a parent-linked scope chain. If
# Line 156  Line 140 
140       * but vcap vs. scope shape tests ensure nothing malfunctions.       * but vcap vs. scope shape tests ensure nothing malfunctions.
141       */       */
142      JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);      JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
143    
144      if (protoIndex != 0) {      if (protoIndex != 0) {
145          JSObject *tmp;          JSObject *tmp = obj;
146    
147            for (uintN i = 0; i != scopeIndex; i++)
148                tmp = OBJ_GET_PARENT(cx, tmp);
149            JS_ASSERT(tmp != pobj);
150    
         JS_ASSERT(pobj != obj);  
151          protoIndex = 1;          protoIndex = 1;
         tmp = obj;  
152          for (;;) {          for (;;) {
153              tmp = OBJ_GET_PROTO(cx, tmp);              tmp = OBJ_GET_PROTO(cx, tmp);
154              if (!tmp) {  
155                /*
156                 * We cannot cache properties coming from native objects behind
157                 * non-native ones on the prototype chain. The non-natives can
158                 * mutate in arbitrary way without changing any shapes.
159                 */
160                if (!tmp || !OBJ_IS_NATIVE(tmp)) {
161                  PCMETER(cache->noprotos++);                  PCMETER(cache->noprotos++);
162                  *entryp = NULL;                  return JS_NO_PROP_CACHE_FILL;
                 return;  
163              }              }
164              if (tmp == pobj)              if (tmp == pobj)
165                  break;                  break;
166              ++protoIndex;              ++protoIndex;
167          }          }
168      }      }
169    
170      if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {      if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
171          PCMETER(cache->longchains++);          PCMETER(cache->longchains++);
172          *entryp = NULL;          return JS_NO_PROP_CACHE_FILL;
         return;  
173      }      }
174    
175      /*      /*
176       * Optimize the cached vword based on our parameters and the current pc's       * Optimize the cached vword based on our parameters and the current pc's
177       * opcode format flags.       * opcode format flags.
178       */       */
179      op = (JSOp) *pc;      pc = cx->fp->regs->pc;
180        op = js_GetOpcode(cx, cx->fp->script, pc);
181      cs = &js_CodeSpec[op];      cs = &js_CodeSpec[op];
182        kshape = 0;
183    
184      do {      do {
185          /*          /*
186           * Check for a prototype "plain old method" callee computation. What           * Check for a prototype "plain old method" callee computation. What
187           * is a plain old method? It's a function-valued property with stub           * is a plain old method? It's a function-valued property with stub
188           * getter and setter, so get of a function is idempotent and set is           * getter, so get of a function is idempotent.
          * transparent.  
189           */           */
190          if (cs->format & JOF_CALLOP) {          if ((cs->format & JOF_CALLOP) &&
191              if (SPROP_HAS_STUB_GETTER(sprop) &&              SPROP_HAS_STUB_GETTER(sprop) &&
192                  SPROP_HAS_VALID_SLOT(sprop, scope)) {              SPROP_HAS_VALID_SLOT(sprop, scope)) {
193                  jsval v;              jsval v;
194    
195                  v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);              v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
196                  if (VALUE_IS_FUNCTION(cx, v)) {              if (VALUE_IS_FUNCTION(cx, v)) {
197                      /*                  /*
198                       * Great, we have a function-valued prototype property                   * Great, we have a function-valued prototype property where
199                       * where the getter is JS_PropertyStub. The type id in                   * the getter is JS_PropertyStub. The type id in pobj's scope
200                       * pobj's scope does not evolve with changes to property                   * does not evolve with changes to property values, however.
201                       * values, however.                   *
202                       *                   * So here, on first cache fill for this method, we brand the
203                       * So here, on first cache fill for this method, we brand                   * scope with a new shape and set the SCOPE_BRANDED flag. Once
204                       * the scope with a new shape and set the SCOPE_BRANDED                   * this scope flag is set, any write to a function-valued plain
205                       * flag.  Once this scope flag is set, any write that adds                   * old property in pobj will result in shape being regenerated.
206                       * or deletes a function-valued plain old property in                   */
207                       * scope->object will result in shape being regenerated.                  if (!scope->branded()) {
208                       */                      PCMETER(cache->brandfills++);
                     if (!SCOPE_IS_BRANDED(scope)) {  
                         PCMETER(cache->brandfills++);  
209  #ifdef DEBUG_notme  #ifdef DEBUG_notme
210                          fprintf(stderr,                      fprintf(stderr,
211                              "branding %p (%s) for funobj %p (%s), kshape %lu\n",                              "branding %p (%s) for funobj %p (%s), shape %lu\n",
212                              pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,                              pobj, pobj->getClass()->name,
213                              JSVAL_TO_OBJECT(v),                              JSVAL_TO_OBJECT(v),
214                              JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,                              JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v))),
215                                                   JSVAL_TO_OBJECT(v))),                              OBJ_SHAPE(obj));
216                              kshape);  #endif
217  #endif                      scope->brandingShapeChange(cx, sprop->slot, v);
218                          SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);                      if (js_IsPropertyCacheDisabled(cx))  /* check for rt->shapeGen overflow */
219                          SCOPE_SET_BRANDED(scope);                          return JS_NO_PROP_CACHE_FILL;
220                          kshape = scope->shape;                      scope->setBranded();
                     }  
                     vword = JSVAL_OBJECT_TO_PCVAL(v);  
                     break;  
221                  }                  }
222                    vword = JSVAL_OBJECT_TO_PCVAL(v);
223                    break;
224              }              }
225          }          }
226    
227          /* If getting a value via a stub getter, we can cache the slot. */          /* If getting a value via a stub getter, we can cache the slot. */
228          if (!(cs->format & JOF_SET) &&          if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
229              SPROP_HAS_STUB_GETTER(sprop) &&              SPROP_HAS_STUB_GETTER(sprop) &&
230              SPROP_HAS_VALID_SLOT(sprop, scope)) {              SPROP_HAS_VALID_SLOT(sprop, scope)) {
231              /* Great, let's cache sprop's slot and use it on cache hit. */              /* Great, let's cache sprop's slot and use it on cache hit. */
# Line 243  Line 233 
233          } else {          } else {
234              /* Best we can do is to cache sprop (still a nice speedup). */              /* Best we can do is to cache sprop (still a nice speedup). */
235              vword = SPROP_TO_PCVAL(sprop);              vword = SPROP_TO_PCVAL(sprop);
236                if (adding &&
237                    sprop == scope->lastProp &&
238                    scope->shape == sprop->shape) {
239                    /*
240                     * Our caller added a new property. We also know that a setter
241                     * that js_NativeSet could have run has not mutated the scope
242                     * so the added property is still the last one added and the
243                     * scope is not branded.
244                     *
245                     * We want to cache under scope's shape before the property
246                     * addition to bias for the case when the mutator opcode
247                     * always adds the same property. It allows to optimize
248                     * periodic execution of object initializers or explicit
249                     * initialization sequences like
250                     *
251                     *   obj = {}; obj.x = 1; obj.y = 2;
252                     *
253                     * We assume that on average the win from this optimization is
254                     * bigger that the cost of an extra mismatch per loop due to
255                     * the bias for the following case:
256                     *
257                     *   obj = {}; ... for (...) { ... obj.x = ... }
258                     *
259                     * On the first iteration JSOP_SETPROP fills the cache with
260                     * the shape of newly created object, not the shape after
261                     * obj.x is assigned. That mismatches obj's shape on the
262                     * second iteration. Note that on third and the following
263                     * iterations the cache will be hit since the shape no longer
264                     * mutates.
265                     */
266                    JS_ASSERT(scope->owned());
267                    if (sprop->parent) {
268                        kshape = sprop->parent->shape;
269                    } else {
270                        /*
271                         * If obj had its own empty scope before, with a unique
272                         * shape, that is lost. Here we only attempt to find a
273                         * matching empty scope. In unusual cases involving
274                         * __proto__ assignment we may not find one.
275                         */
276                        JSObject *proto = STOBJ_GET_PROTO(obj);
277                        if (!proto || !OBJ_IS_NATIVE(proto))
278                            return JS_NO_PROP_CACHE_FILL;
279                        JSScope *protoscope = OBJ_SCOPE(proto);
280                        if (!protoscope->emptyScope ||
281                            protoscope->emptyScope->clasp != obj->getClass()) {
282                            return JS_NO_PROP_CACHE_FILL;
283                        }
284                        kshape = protoscope->emptyScope->shape;
285                    }
286    
287                    /*
288                     * When adding we predict no prototype object will later gain a
289                     * readonly property or setter.
290                     */
291                    vshape = cx->runtime->protoHazardShape;
292                }
293          }          }
294      } while (0);      } while (0);
295    
296      /*      if (kshape == 0) {
297       * Our caller preserved the scope shape prior to the js_GetPropertyHelper          kshape = OBJ_SHAPE(obj);
298       * or similar call out of the interpreter. We want to cache under that          vshape = scope->shape;
299       * shape if op is overtly mutating, to bias for the case where the mutator      }
      * udpates shape predictably.  
      *  
      * Note that an apparently non-mutating op such as JSOP_NAME may still  
      * mutate the base object via, e.g., lazy standard class initialization,  
      * but that is a one-time event and we'll have to miss the old shape and  
      * re-fill under the new one.  
      */  
     if (!(cs->format & (JOF_SET | JOF_INCDEC)) && obj == pobj)  
         kshape = scope->shape;  
300    
301      khash = PROPERTY_CACHE_HASH_PC(pc, kshape);      khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
302      if (obj == pobj) {      if (obj == pobj) {
         JS_ASSERT(kshape != 0 || scope->shape != 0);  
303          JS_ASSERT(scopeIndex == 0 && protoIndex == 0);          JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
         JS_ASSERT(OBJ_SCOPE(obj)->object == obj);  
304      } else {      } else {
305          if (op == JSOP_LENGTH) {          if (op == JSOP_LENGTH) {
306              atom = cx->runtime->atomState.lengthAtom;              atom = cx->runtime->atomState.lengthAtom;
307          } else {          } else {
308              pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;              pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
309              GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);              GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
310          }          }
311          JS_ASSERT_IF(scopeIndex == 0,  
312                       protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);  #ifdef DEBUG
313            if (scopeIndex == 0) {
314                JS_ASSERT(protoIndex != 0);
315                JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj));
316            }
317    #endif
318    
319          if (scopeIndex != 0 || protoIndex != 1) {          if (scopeIndex != 0 || protoIndex != 1) {
320              khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);              khash = PROPERTY_CACHE_HASH_ATOM(atom, obj);
321              PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)              PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
322                          cache->pcrecycles++);                          cache->pcrecycles++);
323              pc = (jsbytecode *) atom;              pc = (jsbytecode *) atom;
324              kshape = (jsuword) obj;              kshape = (jsuword) obj;
325    
326                /*
327                 * Make sure that a later shadowing assignment will enter
328                 * PurgeProtoChain and invalidate this entry, bug 479198.
329                 *
330                 * This is thread-safe even though obj is not locked. Only the
331                 * DELEGATE bit of obj->classword can change at runtime, given that
332                 * obj is native; and the bit is only set, never cleared. And on
333                 * platforms where another CPU can fail to see this write, it's OK
334                 * because the property cache and JIT cache are thread-local.
335                 */
336                obj->setDelegate();
337          }          }
338      }      }
339    
340      entry = &cache->table[khash];      entry = &cache->table[khash];
341      PCMETER(if (entry != *entryp) cache->modfills++);      PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++);
     PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);  
342      entry->kpc = pc;      entry->kpc = pc;
343      entry->kshape = kshape;      entry->kshape = kshape;
344      entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);      entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex);
345      entry->vword = vword;      entry->vword = vword;
     *entryp = entry;  
346    
347      cache->empty = JS_FALSE;      cache->empty = JS_FALSE;
348      PCMETER(cache->fills++);      PCMETER(cache->fills++);
349    
350        /*
351         * The modfills counter is not exact. It increases if a getter or setter
352         * recurse into the interpreter.
353         */
354        PCMETER(entry == cache->pctestentry || cache->modfills++);
355        PCMETER(cache->pctestentry = NULL);
356        return entry;
357  }  }
358    
359  JSAtom *  JS_REQUIRES_STACK JSAtom *
360  js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,  js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
361                           JSObject **objp, JSObject **pobjp,                           JSObject **objp, JSObject **pobjp,
362                           JSPropCacheEntry **entryp)                           JSPropCacheEntry **entryp)
# Line 309  Line 369 
369      JSPropCacheEntry *entry;      JSPropCacheEntry *entry;
370      uint32 vcap;      uint32 vcap;
371    
372      JS_ASSERT(JS_UPTRDIFF(pc, cx->fp->script->code) < cx->fp->script->length);      JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
373                  < cx->fp->script->length);
374    
375      op = (JSOp) *pc;      op = js_GetOpcode(cx, cx->fp->script, pc);
376      cs = &js_CodeSpec[op];      cs = &js_CodeSpec[op];
377      if (op == JSOP_LENGTH) {      if (op == JSOP_LENGTH) {
378          atom = cx->runtime->atomState.lengthAtom;          atom = cx->runtime->atomState.lengthAtom;
379      } else {      } else {
380          pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;          pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
381          GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);          GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
382      }      }
383    
384      obj = *objp;      obj = *objp;
385      JS_ASSERT(OBJ_IS_NATIVE(obj));      JS_ASSERT(OBJ_IS_NATIVE(obj));
386      entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj, NULL)];      entry = &JS_PROPERTY_CACHE(cx).table[PROPERTY_CACHE_HASH_ATOM(atom, obj)];
387      *entryp = entry;      *entryp = entry;
388      vcap = entry->vcap;      vcap = entry->vcap;
389    
# Line 342  Line 403 
403                  entry->kshape,                  entry->kshape,
404                  OBJ_SHAPE(obj));                  OBJ_SHAPE(obj));
405                  js_Disassemble1(cx, cx->fp->script, pc,                  js_Disassemble1(cx, cx->fp->script, pc,
406                                  PTRDIFF(pc, cx->fp->script->code, jsbytecode),                                  pc - cx->fp->script->code,
407                                  JS_FALSE, stderr);                                  JS_FALSE, stderr);
408  #endif  #endif
409    
# Line 355  Line 416 
416      }      }
417    
418      pobj = obj;      pobj = obj;
     JS_LOCK_OBJ(cx, pobj);  
419    
420      if (JOF_MODE(cs->format) == JOF_NAME) {      if (JOF_MODE(cs->format) == JOF_NAME) {
421          while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {          while (vcap & (PCVCAP_SCOPEMASK << PCVCAP_PROTOBITS)) {
422              tmp = LOCKED_OBJ_GET_PARENT(pobj);              tmp = OBJ_GET_PARENT(cx, pobj);
423              if (!tmp || !OBJ_IS_NATIVE(tmp))              if (!tmp || !OBJ_IS_NATIVE(tmp))
424                  break;                  break;
             JS_UNLOCK_OBJ(cx, pobj);  
425              pobj = tmp;              pobj = tmp;
             JS_LOCK_OBJ(cx, pobj);  
426              vcap -= PCVCAP_PROTOSIZE;              vcap -= PCVCAP_PROTOSIZE;
427          }          }
428    
# Line 372  Line 430 
430      }      }
431    
432      while (vcap & PCVCAP_PROTOMASK) {      while (vcap & PCVCAP_PROTOMASK) {
433          tmp = LOCKED_OBJ_GET_PROTO(pobj);          tmp = OBJ_GET_PROTO(cx, pobj);
434          if (!tmp || !OBJ_IS_NATIVE(tmp))          if (!tmp || !OBJ_IS_NATIVE(tmp))
435              break;              break;
         JS_UNLOCK_OBJ(cx, pobj);  
436          pobj = tmp;          pobj = tmp;
         JS_LOCK_OBJ(cx, pobj);  
437          --vcap;          --vcap;
438      }      }
439    
440      if (PCVCAP_SHAPE(vcap) == OBJ_SHAPE(pobj)) {      if (JS_LOCK_OBJ_IF_SHAPE(cx, pobj, PCVCAP_SHAPE(vcap))) {
441  #ifdef DEBUG  #ifdef DEBUG
442          jsid id = ATOM_TO_JSID(atom);          jsid id = ATOM_TO_JSID(atom);
443    
444          CHECK_FOR_STRING_INDEX(id);          id = js_CheckForStringIndex(id);
445          JS_ASSERT(SCOPE_GET_PROPERTY(OBJ_SCOPE(pobj), id));          JS_ASSERT(OBJ_SCOPE(pobj)->lookup(id));
446          JS_ASSERT(OBJ_SCOPE(pobj)->object == pobj);          JS_ASSERT_IF(OBJ_SCOPE(pobj)->object, OBJ_SCOPE(pobj)->object == pobj);
447  #endif  #endif
448          *pobjp = pobj;          *pobjp = pobj;
449          return NULL;          return NULL;
450      }      }
451    
452      PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);      PCMETER(JS_PROPERTY_CACHE(cx).vcmisses++);
     JS_UNLOCK_OBJ(cx, pobj);  
453      return atom;      return atom;
454  }  }
455    
# Line 418  Line 473 
473  JS_STATIC_ASSERT(PCVAL_NULL == 0);  JS_STATIC_ASSERT(PCVAL_NULL == 0);
474    
475  void  void
476  js_FlushPropertyCache(JSContext *cx)  js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
477  {  {
     JSPropertyCache *cache;  
   
     cache = &JS_PROPERTY_CACHE(cx);  
478      if (cache->empty) {      if (cache->empty) {
479          ASSERT_CACHE_IS_EMPTY(cache);          ASSERT_CACHE_IS_EMPTY(cache);
480          return;          return;
# Line 490  Line 542 
542  }  }
543    
544  void  void
545  js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)  js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
546  {  {
547      JSPropertyCache *cache;      JSPropertyCache *cache;
548      JSPropCacheEntry *entry;      JSPropCacheEntry *entry;
# Line 508  Line 560 
560      }      }
561  }  }
562    
 void  
 js_DisablePropertyCache(JSContext *cx)  
 {  
     JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);  
     ++JS_PROPERTY_CACHE(cx).disabled;  
 }  
   
 void  
 js_EnablePropertyCache(JSContext *cx)  
 {  
     --JS_PROPERTY_CACHE(cx).disabled;  
     JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);  
 }  
   
563  /*  /*
564   * Check if the current arena has enough space to fit nslots after sp and, if   * Check if the current arena has enough space to fit nslots after sp and, if
565   * so, reserve the necessary space.   * so, reserve the necessary space.
566   */   */
567  static JSBool  static JS_REQUIRES_STACK JSBool
568  AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)  AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
569  {  {
570      uintN surplus;      uintN surplus;
# Line 551  Line 589 
589      return JS_TRUE;      return JS_TRUE;
590  }  }
591    
592  JS_STATIC_INTERPRET jsval *  JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval *
593  js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)  js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
594  {  {
595      jsval *sp;      jsval *sp;
596    
597        JS_ASSERT(nslots != 0);
598        JS_ASSERT_NOT_ON_TRACE(cx);
599    
600      if (!cx->stackPool.first.next) {      if (!cx->stackPool.first.next) {
601          int64 *timestamp;          int64 *timestamp;
602    
# Line 576  Line 617 
617      return sp;      return sp;
618  }  }
619    
620  JS_STATIC_INTERPRET void  JS_STATIC_INTERPRET JS_REQUIRES_STACK void
621  js_FreeRawStack(JSContext *cx, void *mark)  js_FreeRawStack(JSContext *cx, void *mark)
622  {  {
623      JS_ARENA_RELEASE(&cx->stackPool, mark);      JS_ARENA_RELEASE(&cx->stackPool, mark);
624  }  }
625    
626  JS_FRIEND_API(jsval *)  JS_REQUIRES_STACK JS_FRIEND_API(jsval *)
627  js_AllocStack(JSContext *cx, uintN nslots, void **markp)  js_AllocStack(JSContext *cx, uintN nslots, void **markp)
628  {  {
629      jsval *sp;      jsval *sp;
# Line 628  Line 669 
669      return sp;      return sp;
670  }  }
671    
672  JS_FRIEND_API(void)  JS_REQUIRES_STACK JS_FRIEND_API(void)
673  js_FreeStack(JSContext *cx, void *mark)  js_FreeStack(JSContext *cx, void *mark)
674  {  {
675      JSStackHeader *sh;      JSStackHeader *sh;
# Line 656  Line 697 
697  JSObject *  JSObject *
698  js_GetScopeChain(JSContext *cx, JSStackFrame *fp)  js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
699  {  {
700      JSObject *obj, *cursor, *clonedChild, *parent;      JSObject *sharedBlock = fp->blockChain;
     JSTempValueRooter tvr;  
701    
702      obj = fp->blockChain;      if (!sharedBlock) {
     if (!obj) {  
703          /*          /*
704           * Don't force a call object for a lightweight function call, but do           * Don't force a call object for a lightweight function call, but do
705           * insist that there is a call object for a heavyweight function call.           * insist that there is a call object for a heavyweight function call.
# Line 672  Line 711 
711          return fp->scopeChain;          return fp->scopeChain;
712      }      }
713    
714        /* We don't handle cloning blocks on trace.  */
715        js_LeaveTrace(cx);
716    
717      /*      /*
718       * We have one or more lexical scopes to reflect into fp->scopeChain, so       * We have one or more lexical scopes to reflect into fp->scopeChain, so
719       * make sure there's a call object at the current head of the scope chain,       * make sure there's a call object at the current head of the scope chain,
720       * if this frame is a call frame.       * if this frame is a call frame.
721         *
722         * Also, identify the innermost compiler-allocated block we needn't clone.
723       */       */
724        JSObject *limitBlock, *limitClone;
725      if (fp->fun && !fp->callobj) {      if (fp->fun && !fp->callobj) {
726          JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||          JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
727                    OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);                    fp->scopeChain->getPrivate() != fp);
728          if (!js_GetCallObject(cx, fp, fp->scopeChain))          if (!js_GetCallObject(cx, fp))
729              return NULL;              return NULL;
730    
731            /* We know we must clone everything on blockChain. */
732            limitBlock = limitClone = NULL;
733        } else {
734            /*
735             * scopeChain includes all blocks whose static scope we're within that
736             * have already been cloned.  Find the innermost such block.  Its
737             * prototype should appear on blockChain; we'll clone blockChain up
738             * to, but not including, that prototype.
739             */
740            limitClone = fp->scopeChain;
741            while (OBJ_GET_CLASS(cx, limitClone) == &js_WithClass)
742                limitClone = OBJ_GET_PARENT(cx, limitClone);
743            JS_ASSERT(limitClone);
744    
745            /*
746             * It may seem like we don't know enough about limitClone to be able
747             * to just grab its prototype as we do here, but it's actually okay.
748             *
749             * If limitClone is a block object belonging to this frame, then its
750             * prototype is the innermost entry in blockChain that we have already
751             * cloned, and is thus the place to stop when we clone below.
752             *
753             * Otherwise, there are no blocks for this frame on scopeChain, and we
754             * need to clone the whole blockChain.  In this case, limitBlock can
755             * point to any object known not to be on blockChain, since we simply
756             * loop until we hit limitBlock or NULL.  If limitClone is a block, it
757             * isn't a block from this function, since blocks can't be nested
758             * within themselves on scopeChain (recursion is dynamic nesting, not
759             * static nesting).  If limitClone isn't a block, its prototype won't
760             * be a block either.  So we can just grab limitClone's prototype here
761             * regardless of its type or which frame it belongs to.
762             */
763            limitBlock = OBJ_GET_PROTO(cx, limitClone);
764    
765            /* If the innermost block has already been cloned, we are done. */
766            if (limitBlock == sharedBlock)
767                return fp->scopeChain;
768      }      }
769    
770      /*      /*
771       * Clone the block chain. To avoid recursive cloning we set the parent of       * Special-case cloning the innermost block; this doesn't have enough in
772       * the cloned child after we clone the parent. In the following loop when       * common with subsequent steps to include in the loop.
773       * clonedChild is null it indicates the first iteration when no special GC       *
774       * rooting is necessary. On the second and the following iterations we       * js_CloneBlockObject leaves the clone's parent slot uninitialized. We
775       * have to protect cloned so far chain against the GC during cloning of       * populate it below.
      * the cursor object.  
776       */       */
777      cursor = obj;      JSObject *innermostNewChild = js_CloneBlockObject(cx, sharedBlock, fp);
778      clonedChild = NULL;      if (!innermostNewChild)
779            return NULL;
780        JSAutoTempValueRooter tvr(cx, innermostNewChild);
781    
782        /*
783         * Clone our way towards outer scopes until we reach the innermost
784         * enclosing function, or the innermost block we've already cloned.
785         */
786        JSObject *newChild = innermostNewChild;
787      for (;;) {      for (;;) {
788          parent = OBJ_GET_PARENT(cx, cursor);          JS_ASSERT(OBJ_GET_PROTO(cx, newChild) == sharedBlock);
789            sharedBlock = OBJ_GET_PARENT(cx, sharedBlock);
790    
791            /* Sometimes limitBlock will be NULL, so check that first.  */
792            if (sharedBlock == limitBlock || !sharedBlock)
793                break;
794    
795            /* As in the call above, we don't know the real parent yet.  */
796            JSObject *clone
797                = js_CloneBlockObject(cx, sharedBlock, fp);
798            if (!clone)
799                return NULL;
800    
801          /*          /*
802           * We pass fp->scopeChain and not null even if we override the parent           * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to
803           * slot later as null triggers useless calculations of slot's value in           * other threads.
          * js_NewObject that js_CloneBlockObject calls.  
804           */           */
805          cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);          STOBJ_SET_PARENT(newChild, clone);
806          if (!cursor) {          newChild = clone;
             if (clonedChild)  
                 JS_POP_TEMP_ROOT(cx, &tvr);  
             return NULL;  
         }  
         if (!clonedChild) {  
             /*  
              * The first iteration. Check if other follow and root obj if so  
              * to protect the whole cloned chain against GC.  
              */  
             obj = cursor;  
             if (!parent)  
                 break;  
             JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);  
         } else {  
             /*  
              * Avoid OBJ_SET_PARENT overhead as clonedChild cannot escape to  
              * other threads.  
              */  
             STOBJ_SET_PARENT(clonedChild, cursor);  
             if (!parent) {  
                 JS_ASSERT(tvr.u.value == OBJECT_TO_JSVAL(obj));  
                 JS_POP_TEMP_ROOT(cx, &tvr);  
                 break;  
             }  
         }  
         clonedChild = cursor;  
         cursor = parent;  
807      }      }
808      fp->flags |= JSFRAME_POP_BLOCKS;      STOBJ_SET_PARENT(newChild, fp->scopeChain);
809      fp->scopeChain = obj;  
810      fp->blockChain = NULL;  
811      return obj;      /*
812         * If we found a limit block belonging to this frame, then we should have
813         * found it in blockChain.
814         */
815        JS_ASSERT_IF(limitBlock &&
816                     OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
817                     limitClone->getPrivate() == fp,
818                     sharedBlock);
819    
820        /* Place our newly cloned blocks at the head of the scope chain.  */
821        fp->scopeChain = innermostNewChild;
822        return fp->scopeChain;
823  }  }
824    
825  JSBool  JSBool
# Line 749  Line 833 
833          obj = JS_THIS_OBJECT(cx, vp);          obj = JS_THIS_OBJECT(cx, vp);
834          if (!JS_InstanceOf(cx, obj, clasp, vp + 2))          if (!JS_InstanceOf(cx, obj, clasp, vp + 2))
835              return JS_FALSE;              return JS_FALSE;
836          v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);          v = obj->fslots[JSSLOT_PRIMITIVE_THIS];
837      }      }
838      *thisvp = v;      *thisvp = v;
839      return JS_TRUE;      return JS_TRUE;
840  }  }
841    
842    /* Some objects (e.g., With) delegate 'this' to another object. */
843    static inline JSObject *
844    CallThisObjectHook(JSContext *cx, JSObject *obj, jsval *argv)
845    {
846        JSObject *thisp = obj->thisObject(cx);
847        if (!thisp)
848            return NULL;
849        argv[-1] = OBJECT_TO_JSVAL(thisp);
850        return thisp;
851    }
852    
853  /*  /*
854   * ECMA requires "the global object", but in embeddings such as the browser,   * ECMA requires "the global object", but in embeddings such as the browser,
855   * which have multiple top-level objects (windows, frames, etc. in the DOM),   * which have multiple top-level objects (windows, frames, etc. in the DOM),
# Line 796  Line 891 
891           * imposes a performance penalty on all js_ComputeGlobalThis calls,           * imposes a performance penalty on all js_ComputeGlobalThis calls,
892           * and it represents a maintenance hazard.           * and it represents a maintenance hazard.
893           */           */
894          fp = cx->fp;    /* quell GCC overwarning */          fp = js_GetTopStackFrame(cx);    /* quell GCC overwarning */
895          if (lazy) {          if (lazy) {
896              JS_ASSERT(fp->argv == argv);              JS_ASSERT(fp->argv == argv);
897              fp->dormantNext = cx->dormantFrameChain;              fp->dormantNext = cx->dormantFrameChain;
# Line 807  Line 902 
902          thisp = JSVAL_TO_OBJECT(argv[-2]);          thisp = JSVAL_TO_OBJECT(argv[-2]);
903          id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);          id = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
904    
905          ok = OBJ_CHECK_ACCESS(cx, thisp, id, JSACC_PARENT, &v, &attrs);          ok = thisp->checkAccess(cx, id, JSACC_PARENT, &v, &attrs);
906          if (lazy) {          if (lazy) {
907              cx->dormantFrameChain = fp->dormantNext;              cx->dormantFrameChain = fp->dormantNext;
908              fp->dormantNext = NULL;              fp->dormantNext = NULL;
# Line 824  Line 919 
919              thisp = parent;              thisp = parent;
920      }      }
921    
922      OBJ_TO_OUTER_OBJECT(cx, thisp);      return CallThisObjectHook(cx, thisp, argv);
     if (!thisp)  
         return NULL;  
     argv[-1] = OBJECT_TO_JSVAL(thisp);  
     return thisp;  
923  }  }
924    
925  static JSObject *  static JSObject *
# Line 841  Line 932 
932          if (!js_PrimitiveToObject(cx, &argv[-1]))          if (!js_PrimitiveToObject(cx, &argv[-1]))
933              return NULL;              return NULL;
934          thisp = JSVAL_TO_OBJECT(argv[-1]);          thisp = JSVAL_TO_OBJECT(argv[-1]);
935      } else {          return thisp;
936          thisp = JSVAL_TO_OBJECT(argv[-1]);      }
         if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)  
             return js_ComputeGlobalThis(cx, lazy, argv);  
937    
938          if (thisp->map->ops->thisObject) {      thisp = JSVAL_TO_OBJECT(argv[-1]);
939              /* Some objects (e.g., With) delegate 'this' to another object. */      if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass || OBJ_GET_CLASS(cx, thisp) == &js_BlockClass)
940              thisp = thisp->map->ops->thisObject(cx, thisp);          return js_ComputeGlobalThis(cx, lazy, argv);
941              if (!thisp)  
942                  return NULL;      return CallThisObjectHook(cx, thisp, argv);
         }  
         OBJ_TO_OUTER_OBJECT(cx, thisp);  
         if (!thisp)  
             return NULL;  
         argv[-1] = OBJECT_TO_JSVAL(thisp);  
     }  
     return thisp;  
943  }  }
944    
945  JSObject *  JSObject *
946  js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)  js_ComputeThis(JSContext *cx, JSBool lazy, jsval *argv)
947  {  {
948        JS_ASSERT(argv[-1] != JSVAL_HOLE);  // check for SynthesizeFrame poisoning
949      if (JSVAL_IS_NULL(argv[-1]))      if (JSVAL_IS_NULL(argv[-1]))
950          return js_ComputeGlobalThis(cx, lazy, argv);          return js_ComputeGlobalThis(cx, lazy, argv);
951      return ComputeThis(cx, lazy, argv);      return ComputeThis(cx, lazy, argv);
# Line 870  Line 953 
953    
954  #if JS_HAS_NO_SUCH_METHOD  #if JS_HAS_NO_SUCH_METHOD
955    
956  #define JSSLOT_FOUND_FUNCTION   JSSLOT_PRIVATE  const uint32 JSSLOT_FOUND_FUNCTION  = JSSLOT_PRIVATE;
957  #define JSSLOT_SAVED_ID         (JSSLOT_PRIVATE + 1)  const uint32 JSSLOT_SAVED_ID        = JSSLOT_PRIVATE + 1;
958    
959  JSClass js_NoSuchMethodClass = {  JSClass js_NoSuchMethodClass = {
960      "NoSuchMethod",      "NoSuchMethod",
961      JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |      JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
     JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),  
962      JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,      JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,
963      JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,      JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    NULL,
964      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
965  };  };
966    
 JS_BEGIN_EXTERN_C  
   
 JSObject*  
 js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj);  
   
 JS_END_EXTERN_C  
   
 JSObject*  
 js_InitNoSuchMethodClass(JSContext *cx, JSObject* obj)  
 {  
     JSObject *proto;  
   
     proto = JS_InitClass(cx, obj, NULL, &js_NoSuchMethodClass, NULL, 0, NULL,  
                          NULL, NULL, NULL);  
     if (!proto)  
         return NULL;  
   
     OBJ_CLEAR_PROTO(cx, proto);  
     return proto;  
 }  
   
967  /*  /*
968   * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of   * When JSOP_CALLPROP or JSOP_CALLELEM does not find the method property of
969   * the base object, we search for the __noSuchMethod__ method in the base.   * the base object, we search for the __noSuchMethod__ method in the base.
# Line 920  Line 981 
981  JS_STATIC_INTERPRET JSBool  JS_STATIC_INTERPRET JSBool
982  js_OnUnknownMethod(JSContext *cx, jsval *vp)  js_OnUnknownMethod(JSContext *cx, jsval *vp)
983  {  {
     JSObject *obj;  
     jsid id;  
     JSTempValueRooter tvr;  
     JSBool ok;  
   
984      JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));      JS_ASSERT(!JSVAL_IS_PRIMITIVE(vp[1]));
     obj = JSVAL_TO_OBJECT(vp[1]);  
     JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);  
985    
986      MUST_FLOW_THROUGH("out");      JSObject *obj = JSVAL_TO_OBJECT(vp[1]);
987      id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);      jsid id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
988  #if JS_HAS_XML_SUPPORT      JSAutoTempValueRooter tvr(cx, JSVAL_NULL);
989      if (OBJECT_IS_XML(cx, obj)) {      if (!js_GetMethod(cx, obj, id, false, tvr.addr()))
990          JSXMLObjectOps *ops;          return false;
991        if (JSVAL_IS_PRIMITIVE(tvr.value())) {
992          ops = (JSXMLObjectOps *) obj->map->ops;          vp[0] = tvr.value();
         obj = ops->getMethod(cx, obj, id, &tvr.u.value);  
         if (!obj) {  
             ok = JS_FALSE;  
             goto out;  
         }  
         vp[1] = OBJECT_TO_JSVAL(obj);  
     } else  
 #endif  
     {  
         ok = OBJ_GET_PROPERTY(cx, obj, id, &tvr.u.value);  
         if (!ok)  
             goto out;  
     }  
     if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {  
         vp[0] = tvr.u.value;  
993      } else {      } else {
994  #if JS_HAS_XML_SUPPORT  #if JS_HAS_XML_SUPPORT
995          /* Extract the function name from function::name qname. */          /* Extract the function name from function::name qname. */
996          if (!JSVAL_IS_PRIMITIVE(vp[0])) {          if (!JSVAL_IS_PRIMITIVE(vp[0])) {
997              obj = JSVAL_TO_OBJECT(vp[0]);              obj = JSVAL_TO_OBJECT(vp[0]);
998              ok = js_IsFunctionQName(cx, obj, &id);              if (!js_IsFunctionQName(cx, obj, &id))
999              if (!ok)                  return false;
                 goto out;  
1000              if (id != 0)              if (id != 0)
1001                  vp[0] = ID_TO_VALUE(id);                  vp[0] = ID_TO_VALUE(id);
1002          }          }
1003  #endif  #endif
1004          obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);          obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass,
1005          if (!obj) {                                           NULL, NULL);
1006              ok = JS_FALSE;          if (!obj)
1007              goto out;              return false;
1008          }          obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.value();
         obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.u.value;  
1009          obj->fslots[JSSLOT_SAVED_ID] = vp[0];          obj->fslots[JSSLOT_SAVED_ID] = vp[0];
1010          vp[0] = OBJECT_TO_JSVAL(obj);          vp[0] = OBJECT_TO_JSVAL(obj);
1011      }      }
1012      ok = JS_TRUE;      return true;
   
   out:  
     JS_POP_TEMP_ROOT(cx, &tvr);  
     return ok;  
1013  }  }
1014    
1015  static JSBool  static JS_REQUIRES_STACK JSBool
1016  NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)  NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
1017  {  {
1018      jsval *invokevp;      jsval *invokevp;
# Line 1022  Line 1055 
1055  JS_STATIC_ASSERT(JSVAL_INT == 1);  JS_STATIC_ASSERT(JSVAL_INT == 1);
1056  JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);  JS_STATIC_ASSERT(JSVAL_DOUBLE == 2);
1057  JS_STATIC_ASSERT(JSVAL_STRING == 4);  JS_STATIC_ASSERT(JSVAL_STRING == 4);
1058  JS_STATIC_ASSERT(JSVAL_BOOLEAN == 6);  JS_STATIC_ASSERT(JSVAL_SPECIAL == 6);
1059    
1060  const uint16 js_PrimitiveTestFlags[] = {  const uint16 js_PrimitiveTestFlags[] = {
1061      JSFUN_THISP_NUMBER,     /* INT     */      JSFUN_THISP_NUMBER,     /* INT     */
# Line 1040  Line 1073 
1073   * required arguments, allocate declared local variables, and pop everything   * required arguments, allocate declared local variables, and pop everything
1074   * when done.  Then push the return value.   * when done.  Then push the return value.
1075   */   */
1076  JS_FRIEND_API(JSBool)  JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
1077  js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)  js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
1078  {  {
1079      void *mark;      void *mark;
# Line 1050  Line 1083 
1083      JSObject *funobj, *parent;      JSObject *funobj, *parent;
1084      JSBool ok;      JSBool ok;
1085      JSClass *clasp;      JSClass *clasp;
1086      JSObjectOps *ops;      const JSObjectOps *ops;
1087      JSNative native;      JSNative native;
1088      JSFunction *fun;      JSFunction *fun;
1089      JSScript *script;      JSScript *script;
# Line 1059  Line 1092 
1092      JSInterpreterHook hook;      JSInterpreterHook hook;
1093      void *hookData;      void *hookData;
1094    
1095        JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
1096    
1097      /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */      /* [vp .. vp + 2 + argc) must belong to the last JS stack arena. */
1098      JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);      JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
1099      JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);      JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
1100    
1101      /*      /* Mark the top of stack and load frequently-used registers. */
      * Mark the top of stack and load frequently-used registers. After this  
      * point the control should flow through label out2: to return.  
      */  
1102      mark = JS_ARENA_MARK(&cx->stackPool);      mark = JS_ARENA_MARK(&cx->stackPool);
1103        MUST_FLOW_THROUGH("out2");
1104      v = *vp;      v = *vp;
1105    
1106      if (JSVAL_IS_PRIMITIVE(v))      if (JSVAL_IS_PRIMITIVE(v))
# Line 1090  Line 1123 
1123          /*          /*
1124           * XXX this makes no sense -- why convert to function if clasp->call?           * XXX this makes no sense -- why convert to function if clasp->call?
1125           * XXX better to call that hook without converting           * XXX better to call that hook without converting
          * XXX the only thing that needs fixing is liveconnect  
1126           *           *
1127           * Try converting to function, for closure and API compatibility.           * FIXME bug 408416: try converting to function, for API compatibility
1128           * We attempt the conversion under all circumstances for 1.2, but           * if there is a call op defined.
          * only if there is a call op defined otherwise.  
1129           */           */
1130          if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {          if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1131              ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);              ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
# Line 1135  Line 1166 
1166          if (FUN_INTERPRETED(fun)) {          if (FUN_INTERPRETED(fun)) {
1167              native = NULL;              native = NULL;
1168              script = fun->u.i.script;              script = fun->u.i.script;
1169                JS_ASSERT(script);
1170          } else {          } else {
1171              native = fun->u.n.native;              native = fun->u.n.native;
1172              script = NULL;              script = NULL;
# Line 1247  Line 1279 
1279       */       */
1280      frame.thisp = (JSObject *)vp[1];      frame.thisp = (JSObject *)vp[1];
1281      frame.varobj = NULL;      frame.varobj = NULL;
1282      frame.callobj = frame.argsobj = NULL;      frame.callobj = NULL;
1283        frame.argsobj = NULL;
1284      frame.script = script;      frame.script = script;
     frame.callee = funobj;  
1285      frame.fun = fun;      frame.fun = fun;
1286      frame.argc = argc;      frame.argc = argc;
1287      frame.argv = argv;      frame.argv = argv;
# Line 1259  Line 1291 
1291      frame.down = cx->fp;      frame.down = cx->fp;
1292      frame.annotation = NULL;      frame.annotation = NULL;
1293      frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */      frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
1294        frame.blockChain = NULL;
1295      frame.regs = NULL;      frame.regs = NULL;
1296        frame.imacpc = NULL;
1297      frame.slots = NULL;      frame.slots = NULL;
1298      frame.sharpDepth = 0;      frame.sharpDepth = 0;
1299      frame.sharpArray = NULL;      frame.sharpArray = NULL;
1300      frame.flags = flags | rootedArgsFlag;      frame.flags = flags | rootedArgsFlag;
1301      frame.dormantNext = NULL;      frame.dormantNext = NULL;
1302      frame.xmlNamespace = NULL;      frame.displaySave = NULL;
     frame.blockChain = NULL;  
1303    
1304      MUST_FLOW_THROUGH("out");      MUST_FLOW_THROUGH("out");
1305      cx->fp = &frame;      cx->fp = &frame;
# Line 1275  Line 1308 
1308      hook = cx->debugHooks->callHook;      hook = cx->debugHooks->callHook;
1309      hookData = NULL;      hookData = NULL;
1310    
     /* call the hook if present */  
     if (hook && (native || script))  
         hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);  
   
     /* Call the function, either a native method or an interpreted script. */  
1311      if (native) {      if (native) {
 #ifdef DEBUG_NOT_THROWING  
         JSBool alreadyThrowing = cx->throwing;  
 #endif  
   
 #if JS_HAS_LVALUE_RETURN  
         /* Set by JS_SetCallReturnValue2, used to return reference types. */  
         cx->rval2set = JS_FALSE;  
 #endif  
   
1312          /* If native, use caller varobj and scopeChain for eval. */          /* If native, use caller varobj and scopeChain for eval. */
1313          JS_ASSERT(!frame.varobj);          JS_ASSERT(!frame.varobj);
1314          JS_ASSERT(!frame.scopeChain);          JS_ASSERT(!frame.scopeChain);
# Line 1301  Line 1320 
1320          /* But ensure that we have a scope chain. */          /* But ensure that we have a scope chain. */
1321          if (!frame.scopeChain)          if (!frame.scopeChain)
1322              frame.scopeChain = parent;              frame.scopeChain = parent;
1323        } else {
         frame.displaySave = NULL;  
         ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);  
         JS_RUNTIME_METER(cx->runtime, nativeCalls);  
 #ifdef DEBUG_NOT_THROWING  
         if (ok && !alreadyThrowing)  
             ASSERT_NOT_THROWING(cx);  
 #endif  
     } else if (script) {  
1324          /* Use parent scope so js_GetCallObject can find the right "Call". */          /* Use parent scope so js_GetCallObject can find the right "Call". */
1325          frame.scopeChain = parent;          frame.scopeChain = parent;
1326          if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {          if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1327              /* Scope with a call object parented by the callee's parent. */              /* Scope with a call object parented by the callee's parent. */
1328              if (!js_GetCallObject(cx, &frame, parent)) {              if (!js_GetCallObject(cx, &frame)) {
1329                  ok = JS_FALSE;                  ok = JS_FALSE;
1330                  goto out;                  goto out;
1331              }              }
1332          }          }
1333          frame.slots = sp - fun->u.i.nvars;          frame.slots = sp - fun->u.i.nvars;
1334        }
1335    
1336          ok = js_Interpret(cx);      /* Call the hook if present after we fully initialized the frame. */
1337        if (hook)
1338            hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1339    
1340    #ifdef INCLUDE_MOZILLA_DTRACE
1341        /* DTrace function entry, non-inlines */
1342        if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
1343            jsdtrace_function_entry(cx, &frame, fun);
1344        if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
1345            jsdtrace_function_info(cx, &frame, frame.down, fun);
1346        if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
1347            jsdtrace_function_args(cx, &frame, fun, frame.argc, frame.argv);
1348    #endif
1349    
1350        /* Call the function, either a native method or an interpreted script. */
1351        if (native) {
1352    #ifdef DEBUG_NOT_THROWING
1353            JSBool alreadyThrowing = cx->throwing;
1354    #endif
1355    
1356    #if JS_HAS_LVALUE_RETURN
1357            /* Set by JS_SetCallReturnValue2, used to return reference types. */
1358            cx->rval2set = JS_FALSE;
1359    #endif
1360            ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1361            JS_RUNTIME_METER(cx->runtime, nativeCalls);
1362    #ifdef DEBUG_NOT_THROWING
1363            if (ok && !alreadyThrowing)
1364                ASSERT_NOT_THROWING(cx);
1365    #endif
1366      } else {      } else {
1367          /* fun might be onerror trying to report a syntax error in itself. */          JS_ASSERT(script);
1368          frame.scopeChain = NULL;          ok = js_Interpret(cx);
         frame.displaySave = NULL;  
         ok = JS_TRUE;  
1369      }      }
1370    
1371    #ifdef INCLUDE_MOZILLA_DTRACE
1372        /* DTrace function return, non-inlines */
1373        if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
1374            jsdtrace_function_rval(cx, &frame, fun, &frame.rval);
1375        if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
1376            jsdtrace_function_return(cx, &frame, fun);
1377    #endif
1378    
1379  out:  out:
1380      if (hookData) {      if (hookData) {
1381          hook = cx->debugHooks->callHook;          hook = cx->debugHooks->callHook;
# Line 1336  Line 1383 
1383              hook(cx, &frame, JS_FALSE, &ok, hookData);              hook(cx, &frame, JS_FALSE, &ok, hookData);
1384      }      }
1385    
1386      /* If frame has a call object, sync values and clear back-pointer. */      frame.putActivationObjects(cx);
     if (frame.callobj)  
         ok &= js_PutCallObject(cx, &frame);  
   
     /* If frame has an arguments object, sync values and clear back-pointer. */  
     if (frame.argsobj)  
         ok &= js_PutArgsObject(cx, &frame);  
1387    
1388      *vp = frame.rval;      *vp = frame.rval;
1389    
# Line 1370  Line 1411 
1411      void *mark;      void *mark;
1412      JSBool ok;      JSBool ok;
1413    
1414        js_LeaveTrace(cx);
1415      invokevp = js_AllocStack(cx, 2 + argc, &mark);      invokevp = js_AllocStack(cx, 2 + argc, &mark);
1416      if (!invokevp)      if (!invokevp)
1417          return JS_FALSE;          return JS_FALSE;
# Line 1408  Line 1450 
1450  {  {
1451      JSSecurityCallbacks *callbacks;      JSSecurityCallbacks *callbacks;
1452    
1453        js_LeaveTrace(cx);
1454    
1455      /*      /*
1456       * js_InternalInvoke could result in another try to get or set the same id       * js_InternalInvoke could result in another try to get or set the same id
1457       * again, see bug 355497.       * again, see bug 355497.
# Line 1452  Line 1496 
1496      JSObject *obj, *tmp;      JSObject *obj, *tmp;
1497      JSBool ok;      JSBool ok;
1498    
1499        js_LeaveTrace(cx);
1500    
1501  #ifdef INCLUDE_MOZILLA_DTRACE  #ifdef INCLUDE_MOZILLA_DTRACE
1502      if (JAVASCRIPT_EXECUTE_START_ENABLED())      if (JAVASCRIPT_EXECUTE_START_ENABLED())
1503          jsdtrace_execute_start(script);          jsdtrace_execute_start(script);
# Line 1459  Line 1505 
1505    
1506      hook = cx->debugHooks->executeHook;      hook = cx->debugHooks->executeHook;
1507      hookData = mark = NULL;      hookData = mark = NULL;
1508      oldfp = cx->fp;      oldfp = js_GetTopStackFrame(cx);
1509      frame.script = script;      frame.script = script;
1510      if (down) {      if (down) {
1511          /* Propagate arg state for eval and the debugger API. */          /* Propagate arg state for eval and the debugger API. */
1512          frame.callobj = down->callobj;          frame.callobj = down->callobj;
1513          frame.argsobj = down->argsobj;          frame.argsobj = down->argsobj;
1514          frame.varobj = down->varobj;          frame.varobj = down->varobj;
         frame.callee = down->callee;  
1515          frame.fun = down->fun;          frame.fun = down->fun;
1516          frame.thisp = down->thisp;          frame.thisp = down->thisp;
1517          if (down->flags & JSFRAME_COMPUTED_THIS)          if (down->flags & JSFRAME_COMPUTED_THIS)
# Line 1477  Line 1522 
1522          frame.sharpArray = down->sharpArray;          frame.sharpArray = down->sharpArray;
1523          JS_ASSERT(script->nfixed == 0);          JS_ASSERT(script->nfixed == 0);
1524      } else {      } else {
1525          frame.callobj = frame.argsobj = NULL;          frame.callobj = NULL;
1526            frame.argsobj = NULL;
1527          obj = chain;          obj = chain;
1528          if (cx->options & JSOPTION_VAROBJFIX) {          if (cx->options & JSOPTION_VAROBJFIX) {
1529              while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)              while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
1530                  obj = tmp;                  obj = tmp;
1531          }          }
1532          frame.varobj = obj;          frame.varobj = obj;
         frame.callee = NULL;  
1533          frame.fun = NULL;          frame.fun = NULL;
1534          frame.thisp = chain;          frame.thisp = chain;
1535          frame.argc = 0;          frame.argc = 0;
# Line 1492  Line 1537 
1537          frame.annotation = NULL;          frame.annotation = NULL;
1538          frame.sharpArray = NULL;          frame.sharpArray = NULL;
1539      }      }
1540    
1541        frame.imacpc = NULL;
1542      if (script->nslots != 0) {      if (script->nslots != 0) {
1543          frame.slots = js_AllocRawStack(cx, script->nslots, &mark);          frame.slots = js_AllocRawStack(cx, script->nslots, &mark);
1544          if (!frame.slots) {          if (!frame.slots) {
# Line 1510  Line 1557 
1557      frame.sharpDepth = 0;      frame.sharpDepth = 0;
1558      frame.flags = flags;      frame.flags = flags;
1559      frame.dormantNext = NULL;      frame.dormantNext = NULL;
     frame.xmlNamespace = NULL;  
1560      frame.blockChain = NULL;      frame.blockChain = NULL;
1561    
1562      /*      /*
# Line 1534  Line 1580 
1580    
1581      cx->fp = &frame;      cx->fp = &frame;
1582      if (!down) {      if (!down) {
1583          OBJ_TO_OUTER_OBJECT(cx, frame.thisp);          OBJ_TO_INNER_OBJECT(cx, chain);
1584            if (!chain)
1585                return JS_FALSE;
1586            frame.scopeChain = chain;
1587    
1588            frame.thisp = frame.thisp->thisObject(cx);
1589          if (!frame.thisp) {          if (!frame.thisp) {
1590              ok = JS_FALSE;              ok = JS_FALSE;
1591              goto out2;              goto out2;
# Line 1583  Line 1634 
1634      JSObject *obj2;      JSObject *obj2;
1635      JSProperty *prop;      JSProperty *prop;
1636      uintN oldAttrs, report;      uintN oldAttrs, report;
1637      JSBool isFunction;      bool isFunction;
1638      jsval value;      jsval value;
1639      const char *type, *name;      const char *type, *name;
1640    
1641      if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))      /*
1642         * Both objp and propp must be either null or given. When given, *propp
1643         * must be null. This way we avoid an extra "if (propp) *propp = NULL" for
1644         * the common case of a non-existing property.
1645         */
1646        JS_ASSERT(!objp == !propp);
1647        JS_ASSERT_IF(propp, !*propp);
1648    
1649        /* The JSPROP_INITIALIZER case below may generate a warning. Since we must
1650         * drop the property before reporting it, we insists on !propp to avoid
1651         * looking up the property again after the reporting is done.
1652         */
1653        JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER);
1654        JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp);
1655    
1656        if (!obj->lookupProperty(cx, id, &obj2, &prop))
1657          return JS_FALSE;          return JS_FALSE;
     if (propp) {  
         *objp = obj2;  
         *propp = prop;  
     }  
1658      if (!prop)      if (!prop)
1659          return JS_TRUE;          return JS_TRUE;
1660    
1661      /*      /* Use prop as a speedup hint to obj->getAttributes. */
1662       * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.      if (!obj2->getAttributes(cx, id, prop, &oldAttrs)) {
1663       * An assertion at label bad: will insist that it is null.          obj2->dropProperty(cx, prop);
1664       */          return JS_FALSE;
     if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {  
         OBJ_DROP_PROPERTY(cx, obj2, prop);  
 #ifdef DEBUG  
         prop = NULL;  
 #endif  
         goto bad;  
1665      }      }
1666    
1667      /*      /*
      * From here, return true, or else goto bad on failure to null out params.  
1668       * If our caller doesn't want prop, drop it (we don't need it any longer).       * If our caller doesn't want prop, drop it (we don't need it any longer).
1669       */       */
1670      if (!propp) {      if (!propp) {
1671          OBJ_DROP_PROPERTY(cx, obj2, prop);          obj2->dropProperty(cx, prop);
1672          prop = NULL;          prop = NULL;
1673        } else {
1674            *objp = obj2;
1675            *propp = prop;
1676      }      }
1677    
1678      if (attrs == JSPROP_INITIALIZER) {      if (attrs == JSPROP_INITIALIZER) {
1679          /* Allow the new object to override properties. */          /* Allow the new object to override properties. */
1680          if (obj2 != obj)          if (obj2 != obj)
1681              return JS_TRUE;              return JS_TRUE;
1682    
1683            /* The property must be dropped already. */
1684            JS_ASSERT(!prop);
1685          report = JSREPORT_WARNING | JSREPORT_STRICT;          report = JSREPORT_WARNING | JSREPORT_STRICT;
1686    
1687    #ifdef __GNUC__
1688            isFunction = false;     /* suppress bogus gcc warnings */
1689    #endif
1690      } else {      } else {
1691          /* We allow redeclaring some non-readonly properties. */          /* We allow redeclaring some non-readonly properties. */
1692          if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {          if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1693              /*              /* Allow redeclaration of variables and functions. */
              * Allow redeclaration of variables and functions, but insist that  
              * the new value is not a getter if the old value was, ditto for  
              * setters -- unless prop is impermanent (in which case anyone  
              * could delete it and redefine it, willy-nilly).  
              */  
1694              if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))              if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1695                  return JS_TRUE;                  return JS_TRUE;
1696    
1697                /*
1698                 * Allow adding a getter only if a property already has a setter
1699                 * but no getter and similarly for adding a setter. That is, we
1700                 * allow only the following transitions:
1701                 *
1702                 *   no-property --> getter --> getter + setter
1703                 *   no-property --> setter --> getter + setter
1704                 */
1705              if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)              if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1706                  return JS_TRUE;                  return JS_TRUE;
1707    
1708                /*
1709                 * Allow redeclaration of an impermanent property (in which case
1710                 * anyone could delete it and redefine it, willy-nilly).
1711                 */
1712              if (!(oldAttrs & JSPROP_PERMANENT))              if (!(oldAttrs & JSPROP_PERMANENT))
1713                  return JS_TRUE;                  return JS_TRUE;
1714          }          }
1715            if (prop)
1716                obj2->dropProperty(cx, prop);
1717    
1718          report = JSREPORT_ERROR;          report = JSREPORT_ERROR;
1719          isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;          isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1720          if (!isFunction) {          if (!isFunction) {
1721              if (!OBJ_GET_PROPERTY(cx, obj, id, &value))              if (!obj->getProperty(cx, id, &value))
1722                  goto bad;                  return JS_FALSE;
1723              isFunction = VALUE_IS_FUNCTION(cx, value);              isFunction = VALUE_IS_FUNCTION(cx, value);
1724          }          }
1725      }      }
# Line 1661  Line 1737 
1737             : js_var_str;             : js_var_str;
1738      name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));      name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1739      if (!name)      if (!name)
1740          goto bad;          return JS_FALSE;
1741      return JS_ReportErrorFlagsAndNumber(cx, report,      return JS_ReportErrorFlagsAndNumber(cx, report,
1742                                          js_GetErrorMessage, NULL,                                          js_GetErrorMessage, NULL,
1743                                          JSMSG_REDECLARED_VAR,                                          JSMSG_REDECLARED_VAR,
1744                                          type, name);                                          type, name);
   
 bad:  
     if (propp) {  
         *objp = NULL;  
         *propp = NULL;  
     }  
     JS_ASSERT(!prop);  
     return JS_FALSE;  
1745  }  }
1746    
1747  JSBool  JSBool
# Line 1719  Line 1787 
1787      return lval == rval;      return lval == rval;
1788  }  }
1789    
1790    static inline bool
1791    IsNegativeZero(jsval v)
1792    {
1793        return JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NEGZERO(*JSVAL_TO_DOUBLE(v));
1794    }
1795    
1796    static inline bool
1797    IsNaN(jsval v)
1798    {
1799        return JSVAL_IS_DOUBLE(v) && JSDOUBLE_IS_NaN(*JSVAL_TO_DOUBLE(v));
1800    }
1801    
1802  JSBool  JSBool
1803    js_SameValue(jsval v1, jsval v2, JSContext *cx)
1804    {
1805        if (IsNegativeZero(v1))
1806            return IsNegativeZero(v2);
1807        if (IsNegativeZero(v2))
1808            return JS_FALSE;
1809        if (IsNaN(v1) && IsNaN(v2))
1810            return JS_TRUE;
1811        return js_StrictlyEqual(cx, v1, v2);
1812    }
1813    
1814    JS_REQUIRES_STACK JSBool
1815  js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)  js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
1816  {  {
1817      JSFunction *fun, *fun2;      JSFunction *fun, *fun2;
# Line 1752  Line 1844 
1844           * root to protect this prototype, in case it has no other           * root to protect this prototype, in case it has no other
1845           * strong refs.           * strong refs.
1846           */           */
1847          if (!OBJ_GET_PROPERTY(cx, obj2,          if (!obj2->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
1848                                ATOM_TO_JSID(cx->runtime->atomState                                 &vp[1])) {
                                            .classPrototypeAtom),  
                               &vp[1])) {  
1849              return JS_FALSE;              return JS_FALSE;
1850          }          }
1851          rval = vp[1];          rval = vp[1];
# Line 1768  Line 1858 
1858                  clasp = fun2->u.n.clasp;                  clasp = fun2->u.n.clasp;
1859          }          }
1860      }      }
1861      obj = js_NewObject(cx, clasp, proto, parent, 0);      obj = js_NewObject(cx, clasp, proto, parent);
1862      if (!obj)      if (!obj)
1863          return JS_FALSE;          return JS_FALSE;
1864    
1865      /* Now we have an object with a constructor method; call it. */      /* Now we have an object with a constructor method; call it. */
1866      vp[1] = OBJECT_TO_JSVAL(obj);      vp[1] = OBJECT_TO_JSVAL(obj);
1867      if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT)) {      if (!js_Invoke(cx, argc, vp, JSINVOKE_CONSTRUCT))
         cx->weakRoots.newborn[GCX_OBJECT] = NULL;  
1868          return JS_FALSE;          return JS_FALSE;
     }  
1869    
1870      /* Check the return value and if it's primitive, force it to be obj. */      /* Check the return value and if it's primitive, force it to be obj. */
1871      rval = *vp;      rval = *vp;
# Line 1821  Line 1909 
1909   * Enter the new with scope using an object at sp[-1] and associate the depth   * Enter the new with scope using an object at sp[-1] and associate the depth
1910   * of the with block with sp + stackIndex.   * of the with block with sp + stackIndex.
1911   */   */
1912  JS_STATIC_INTERPRET JSBool  JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1913  js_EnterWith(JSContext *cx, jsint stackIndex)  js_EnterWith(JSContext *cx, jsint stackIndex)
1914  {  {
1915      JSStackFrame *fp;      JSStackFrame *fp;
# Line 1856  Line 1944 
1944          return JS_FALSE;          return JS_FALSE;
1945    
1946      fp->scopeChain = withobj;      fp->scopeChain = withobj;
     js_DisablePropertyCache(cx);  
1947      return JS_TRUE;      return JS_TRUE;
1948  }  }
1949    
1950  JS_STATIC_INTERPRET void  JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1951  js_LeaveWith(JSContext *cx)  js_LeaveWith(JSContext *cx)
1952  {  {
1953      JSObject *withobj;      JSObject *withobj;
1954    
1955      withobj = cx->fp->scopeChain;      withobj = cx->fp->scopeChain;
1956      JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);      JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass);
1957      JS_ASSERT(OBJ_GET_PRIVATE(cx, withobj) == cx->fp);      JS_ASSERT(withobj->getPrivate() == cx->fp);
1958      JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);      JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1959      cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);      cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
1960      JS_SetPrivate(cx, withobj, NULL);      withobj->setPrivate(NULL);
     js_EnablePropertyCache(cx);  
1961  }  }
1962    
1963  JSClass *  JS_REQUIRES_STACK JSClass *
1964  js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)  js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1965  {  {
1966      JSClass *clasp;      JSClass *clasp;
1967    
1968      clasp = OBJ_GET_CLASS(cx, obj);      clasp = OBJ_GET_CLASS(cx, obj);
1969      if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&      if ((clasp == &js_WithClass || clasp == &js_BlockClass) &&
1970          OBJ_GET_PRIVATE(cx, obj) == cx->fp &&          obj->getPrivate() == cx->fp &&
1971          OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {          OBJ_BLOCK_DEPTH(cx, obj) >= stackDepth) {
1972          return clasp;          return clasp;
1973      }      }
1974      return NULL;      return NULL;
1975  }  }
1976    
 JS_STATIC_INTERPRET jsint  
 js_CountWithBlocks(JSContext *cx, JSStackFrame *fp)  
 {  
     jsint n;  
     JSObject *obj;  
     JSClass *clasp;  
   
     n = 0;  
     for (obj = fp->scopeChain;  
          (clasp = js_IsActiveWithOrBlock(cx, obj, 0)) != NULL;  
          obj = OBJ_GET_PARENT(cx, obj)) {  
         if (clasp == &js_WithClass)  
             ++n;  
     }  
     return n;  
 }  
   
1977  /*  /*
1978   * Unwind block and scope chains to match the given depth. The function sets   * Unwind block and scope chains to match the given depth. The function sets
1979   * fp->sp on return to stackDepth.   * fp->sp on return to stackDepth.
1980   */   */
1981  JSBool  JS_REQUIRES_STACK JSBool
1982  js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,  js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
1983                 JSBool normalUnwind)                 JSBool normalUnwind)
1984  {  {
# Line 1977  Line 2046 
2046      return JS_TRUE;      return JS_TRUE;
2047  }  }
2048    
2049    jsval&
2050    js_GetUpvar(JSContext *cx, uintN level, uintN cookie)
2051    {
2052        level -= UPVAR_FRAME_SKIP(cookie);
2053        JS_ASSERT(level < JS_DISPLAY_SIZE);
2054    
2055        JSStackFrame *fp = cx->display[level];
2056        JS_ASSERT(fp->script);
2057    
2058        uintN slot = UPVAR_FRAME_SLOT(cookie);
2059        jsval *vp;
2060    
2061        if (!fp->fun) {
2062            vp = fp->slots + fp->script->nfixed;
2063        } else if (slot < fp->fun->nargs) {
2064            vp = fp->argv;
2065        } else if (slot == CALLEE_UPVAR_SLOT) {
2066            vp = &fp->argv[-2];
2067            slot = 0;
2068        } else {
2069            slot -= fp->fun->nargs;
2070            JS_ASSERT(slot < fp->script->nslots);
2071            vp = fp->slots;
2072        }
2073    
2074        return vp[slot];
2075    }
2076    
2077  #ifdef DEBUG  #ifdef DEBUG
2078    
2079  JS_STATIC_INTERPRET void  JS_STATIC_INTERPRET JS_REQUIRES_STACK void
2080  js_TraceOpcode(JSContext *cx, jsint len)  js_TraceOpcode(JSContext *cx)
2081  {  {
2082      FILE *tracefp;      FILE *tracefp;
2083      JSStackFrame *fp;      JSStackFrame *fp;
2084      JSFrameRegs *regs;      JSFrameRegs *regs;
     JSOp prevop;  
2085      intN ndefs, n, nuses;      intN ndefs, n, nuses;
2086      jsval *siter;      jsval *siter;
2087      JSString *str;      JSString *str;
# Line 1995  Line 2091 
2091      JS_ASSERT(tracefp);      JS_ASSERT(tracefp);
2092      fp = cx->fp;      fp = cx->fp;
2093      regs = fp->regs;      regs = fp->regs;
2094      if (len != 0) {  
2095          prevop = (JSOp) regs->pc[-len];      /*
2096          ndefs = js_CodeSpec[prevop].ndefs;       * Operations in prologues don't produce interesting values, and
2097          if (ndefs != 0) {       * js_DecompileValueGenerator isn't set up to handle them anyway.
2098              if (prevop == JSOP_FORELEM && regs->sp[-1] == JSVAL_FALSE)       */
2099                  --ndefs;      if (cx->tracePrevPc && regs->pc >= fp->script->main) {
2100            JSOp tracePrevOp = JSOp(*cx->tracePrevPc);
2101            ndefs = js_GetStackDefs(cx, &js_CodeSpec[tracePrevOp], tracePrevOp,
2102                                    fp->script, cx->tracePrevPc);
2103    
2104            /*
2105             * If there aren't that many elements on the stack, then
2106             * we have probably entered a new frame, and printing output
2107             * would just be misleading.
2108             */
2109            if (ndefs != 0 &&
2110                ndefs < regs->sp - fp->slots) {
2111              for (n = -ndefs; n < 0; n++) {              for (n = -ndefs; n < 0; n++) {
2112                  char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],                  char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2113                                                           NULL);                                                           NULL);
# Line 2008  Line 2115 
2115                      fprintf(tracefp, "%s %s",                      fprintf(tracefp, "%s %s",
2116                              (n == -ndefs) ? "  output:" : ",",                              (n == -ndefs) ? "  output:" : ",",
2117                              bytes);                              bytes);
2118                      JS_free(cx, bytes);                      cx->free(bytes);
2119                  }                  }
2120              }              }
2121              fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));              fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
# Line 2025  Line 2132 
2132          fputc('\n', tracefp);          fputc('\n', tracefp);
2133      }      }
2134    
2135      fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, fp->script, regs->pc));      fprintf(tracefp, "%4u: ",
2136                js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : regs->pc));
2137      js_Disassemble1(cx, fp->script, regs->pc,      js_Disassemble1(cx, fp->script, regs->pc,
2138                      PTRDIFF(regs->pc, fp->script->code, jsbytecode),                      regs->pc - fp->script->code,
2139                      JS_FALSE, tracefp);                      JS_FALSE, tracefp);
2140      op = (JSOp) *regs->pc;      op = (JSOp) *regs->pc;
2141      nuses = js_CodeSpec[op].nuses;      nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
2142      if (nuses != 0) {      if (nuses != 0) {
2143          for (n = -nuses; n < 0; n++) {          for (n = -nuses; n < 0; n++) {
2144              char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],              char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
# Line 2039  Line 2147 
2147                  fprintf(tracefp, "%s %s",                  fprintf(tracefp, "%s %s",
2148                          (n == -nuses) ? "  inputs:" : ",",                          (n == -nuses) ? "  inputs:" : ",",
2149                          bytes);                          bytes);
2150                  JS_free(cx, bytes);                  cx->free(bytes);
2151              }              }
2152          }          }
2153          fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));          fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2154      }      }
2155        cx->tracePrevPc = regs->pc;
2156    
2157        /* It's nice to have complete traces when debugging a crash.  */
2158        fflush(tracefp);
2159  }  }
2160    
2161  #endif /* DEBUG */  #endif /* DEBUG */
# Line 2122  Line 2234 
2234    
2235  # define SIGNIFICANT(count,total) (200. * (count) >= (total))  # define SIGNIFICANT(count,total) (200. * (count) >= (total))
2236    
2237      graph = (Edge *) calloc(nedges, sizeof graph[0]);      graph = (Edge *) js_calloc(nedges * sizeof graph[0]);
2238      for (i = nedges = 0; i < JSOP_LIMIT; i++) {      for (i = nedges = 0; i < JSOP_LIMIT; i++) {
2239          from = js_CodeName[i];          from = js_CodeName[i];
2240          for (j = 0; j < JSOP_LIMIT; j++) {          for (j = 0; j < JSOP_LIMIT; j++) {
# Line 2151  Line 2263 
2263                  graph[i].from, graph[i].to,                  graph[i].from, graph[i].to,
2264                  (unsigned long)graph[i].count, style);                  (unsigned long)graph[i].count, style);
2265      }      }
2266      free(graph);      js_free(graph);
2267      fputs("}\n", fp);      fputs("}\n", fp);
2268      fclose(fp);      fclose(fp);
2269    
# Line 2321  Line 2433 
2433      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2434          JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));                                    \          JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));                                    \
2435          JS_ASSERT(v == regs.sp[n]);                                           \          JS_ASSERT(v == regs.sp[n]);                                           \
2436          if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &regs.sp[n]))    \          if (!JSVAL_TO_OBJECT(v)->defaultValue(cx, hint, &regs.sp[n]))         \
2437              goto error;                                                       \              goto error;                                                       \
2438          v = regs.sp[n];                                                       \          v = regs.sp[n];                                                       \
2439      JS_END_MACRO      JS_END_MACRO
# Line 2335  Line 2447 
2447  #define CAN_DO_FAST_INC_DEC(v)     (((((v) << 1) ^ v) & 0x80000001) == 1)  #define CAN_DO_FAST_INC_DEC(v)     (((((v) << 1) ^ v) & 0x80000001) == 1)
2448    
2449  JS_STATIC_ASSERT(JSVAL_INT == 1);  JS_STATIC_ASSERT(JSVAL_INT == 1);
2450  JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MIN)));  JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MIN)));
2451  JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));  JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL_CONSTEXPR(JSVAL_INT_MAX)));
2452    
2453  /*  /*
2454   * Conditional assert to detect failure to clear a pending exception that is   * Conditional assert to detect failure to clear a pending exception that is
# Line 2400  Line 2512 
2512  #endif  #endif
2513    
2514  /*  /*
2515   * Interpreter assumes the following to implement condition-free interrupt   * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2516   * implementation when !JS_THREADED_INTERP.   * single-thread DEBUG js shell testing to verify property cache hits.
2517   */   */
2518  JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);  #if defined DEBUG && !defined JS_THREADSAFE
2519    
2520    # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \
2521        JS_BEGIN_MACRO                                                            \
2522            if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj,  \
2523                                             entry)) {                            \
2524                goto error;                                                       \
2525            }                                                                     \
2526        JS_END_MACRO
2527    
2528    static bool
2529    AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
2530                                ptrdiff_t pcoff, JSObject *start, JSObject *found,
2531                                JSPropCacheEntry *entry)
2532    {
2533        uint32 sample = cx->runtime->gcNumber;
2534    
2535        JSAtom *atom;
2536        if (pcoff >= 0)
2537            GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
2538        else
2539            atom = cx->runtime->atomState.lengthAtom;
2540    
2541        JSObject *obj, *pobj;
2542        JSProperty *prop;
2543        bool ok;
2544    
2545        if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
2546            ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
2547        } else {
2548            obj = start;
2549            ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
2550        }
2551        if (!ok)
2552            return false;
2553        if (!prop)
2554            return true;
2555        if (cx->runtime->gcNumber != sample ||
2556            PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) {
2557            pobj->dropProperty(cx, prop);
2558            return true;
2559        }
2560        JS_ASSERT(prop);
2561        JS_ASSERT(pobj == found);
2562    
2563        JSScopeProperty *sprop = (JSScopeProperty *) prop;
2564        if (PCVAL_IS_SLOT(entry->vword)) {
2565            JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot);
2566        } else if (PCVAL_IS_SPROP(entry->vword)) {
2567            JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop);
2568        } else {
2569            jsval v;
2570            JS_ASSERT(PCVAL_IS_OBJECT(entry->vword));
2571            JS_ASSERT(entry->vword != PCVAL_NULL);
2572            JS_ASSERT(OBJ_SCOPE(pobj)->branded());
2573            JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop));
2574            JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
2575            v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
2576            JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
2577            JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v));
2578        }
2579    
2580        pobj->dropProperty(cx, prop);
2581        return true;
2582    }
2583    
2584    #else
2585    # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2586    #endif
2587    
2588  /*  /*
2589   * Ensure that the intrepreter switch can close call-bytecode cases in the   * Ensure that the intrepreter switch can close call-bytecode cases in the
# Line 2412  Line 2592 
2592  JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);  JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2593  JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);  JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
2594  JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);  JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
2595    JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
2596    JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
2597    JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH);
2598  JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);  JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2599  JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);  JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2600  JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);  JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2601    
2602  /*  /*
2603     * Same for debuggable flat closures defined at top level in another function
2604     * or program fragment.
2605     */
2606    JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
2607    
2608    /*
2609   * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but   * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2610   * remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.   * remain distinct for the decompiler.
2611   */   */
2612  JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);  JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
 JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH);  
2613    
2614  /* See TRY_BRANCH_AFTER_COND. */  /* See TRY_BRANCH_AFTER_COND. */
2615  JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);  JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
# Line 2432  Line 2620 
2620  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2621  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2622    
2623  JSBool  #ifdef JS_TRACER
2624    # define ABORT_RECORDING(cx, reason)                                          \
2625        JS_BEGIN_MACRO                                                            \
2626            if (TRACE_RECORDER(cx))                                               \
2627                js_AbortRecording(cx, reason);                                    \
2628        JS_END_MACRO
2629    #else
2630    # define ABORT_RECORDING(cx, reason)    ((void) 0)
2631    #endif
2632    
2633    JS_REQUIRES_STACK JSBool
2634  js_Interpret(JSContext *cx)  js_Interpret(JSContext *cx)
2635  {  {
2636    #ifdef MOZ_TRACEVIS
2637        TraceVisStateObj tvso(cx, S_INTERP);
2638    #endif
2639    
2640      JSRuntime *rt;      JSRuntime *rt;
2641      JSStackFrame *fp;      JSStackFrame *fp;
2642      JSScript *script;      JSScript *script;
# Line 2461  Line 2663 
2663      JSClass *clasp;      JSClass *clasp;
2664      JSFunction *fun;      JSFunction *fun;
2665      JSType type;      JSType type;
 #if JS_THREADED_INTERP  
     register void * const *jumpTable;  
 #else  
     register uint32 switchMask;  
     uintN switchOp;  
 #endif  
2666      jsint low, high, off, npairs;      jsint low, high, off, npairs;
2667      JSBool match;      JSBool match;
2668  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
# Line 2482  Line 2678 
2678  # define JS_EXTENSION_(s) s  # define JS_EXTENSION_(s) s
2679  #endif  #endif
2680    
2681    # ifdef DEBUG
2682        /*
2683         * We call this macro from BEGIN_CASE in threaded interpreters,
2684         * and before entering the switch in non-threaded interpreters.
2685         * However, reaching such points doesn't mean we've actually
2686         * fetched an OP from the instruction stream: some opcodes use
2687         * 'op=x; DO_OP()' to let another opcode's implementation finish
2688         * their work, and many opcodes share entry points with a run of
2689         * consecutive BEGIN_CASEs.
2690         *
2691         * Take care to trace OP only when it is the opcode fetched from
2692         * the instruction stream, so the trace matches what one would
2693         * expect from looking at the code.  (We do omit POPs after SETs;
2694         * unfortunate, but not worth fixing.)
2695         */
2696    #  define TRACE_OPCODE(OP)  JS_BEGIN_MACRO                                    \
2697                                    if (JS_UNLIKELY(cx->tracefp != NULL) &&       \
2698                                        (OP) == *regs.pc)                         \
2699                                        js_TraceOpcode(cx);                       \
2700                                JS_END_MACRO
2701    # else
2702    #  define TRACE_OPCODE(OP)  ((void) 0)
2703    # endif
2704    
2705  #if JS_THREADED_INTERP  #if JS_THREADED_INTERP
2706      static void *const normalJumpTable[] = {      static void *const normalJumpTable[] = {
2707  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
# Line 2490  Line 2710 
2710  # undef OPDEF  # undef OPDEF
2711      };      };
2712    
 #ifdef JS_TRACER  
     static void *const recordingJumpTable[] = {  
 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \  
         JS_EXTENSION &&R_##op,  
 # include "jsopcode.tbl"  
 # undef OPDEF  
     };  
 #endif /* JS_TRACER */  
   
2713      static void *const interruptJumpTable[] = {      static void *const interruptJumpTable[] = {
2714  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \
2715          JS_EXTENSION &&L_JSOP_INTERRUPT,          JS_EXTENSION &&interrupt,
2716  # include "jsopcode.tbl"  # include "jsopcode.tbl"
2717  # undef OPDEF  # undef OPDEF
2718      };      };
2719    
2720        register void * const *jumpTable = normalJumpTable;
2721    
2722      METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */      METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */
2723    
2724    # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2725    
2726  # ifdef JS_TRACER  # ifdef JS_TRACER
2727  #  define CHECK_RECORDER()  JS_BEGIN_MACRO                                    \  #  define CHECK_RECORDER()                                                    \
2728                                  JS_ASSERT(!TRACE_RECORDER(cx) ^               \      JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
                                           (jumpTable == recordingJumpTable)); \  
                             JS_END_MACRO  
2729  # else  # else
2730  #  define CHECK_RECORDER()  ((void)0)  #  define CHECK_RECORDER()  ((void)0)
2731  # endif  # endif
# Line 2527  Line 2740 
2740                                  DO_OP();                                      \                                  DO_OP();                                      \
2741                              JS_END_MACRO                              JS_END_MACRO
2742    
2743  # define BEGIN_CASE(OP)     L_##OP:                                           \  # define BEGIN_CASE(OP)     L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER();
                                 CHECK_RECORDER();  
2744  # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);  # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
2745  # define END_VARLEN_CASE    DO_NEXT_OP(len);  # define END_VARLEN_CASE    DO_NEXT_OP(len);
2746  # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \  # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \
# Line 2540  Line 2752 
2752    
2753  #else /* !JS_THREADED_INTERP */  #else /* !JS_THREADED_INTERP */
2754    
2755        register intN switchMask = 0;
2756        intN switchOp;
2757    
2758    # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2759    
2760    # ifdef JS_TRACER
2761    #  define CHECK_RECORDER()                                                    \
2762        JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2763    # else
2764    #  define CHECK_RECORDER()  ((void)0)
2765    # endif
2766    
2767  # define DO_OP()            goto do_op  # define DO_OP()            goto do_op
2768  # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \  # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
2769                                  JS_ASSERT((n) == len);                        \                                  JS_ASSERT((n) == len);                        \
2770                                  goto advance_pc;                              \                                  goto advance_pc;                              \
2771                              JS_END_MACRO                              JS_END_MACRO
2772    
2773  # define BEGIN_CASE(OP)     case OP:  # define BEGIN_CASE(OP)     case OP: CHECK_RECORDER();
2774  # define END_CASE(OP)       END_CASE_LEN(OP##_LENGTH)  # define END_CASE(OP)       END_CASE_LEN(OP##_LENGTH)
2775  # define END_CASE_LEN(n)    END_CASE_LENX(n)  # define END_CASE_LEN(n)    END_CASE_LENX(n)
2776  # define END_CASE_LENX(n)   END_CASE_LEN##n  # define END_CASE_LENX(n)   END_CASE_LEN##n
# Line 2567  Line 2791 
2791  #endif /* !JS_THREADED_INTERP */  #endif /* !JS_THREADED_INTERP */
2792    
2793  #ifdef JS_TRACER  #ifdef JS_TRACER
2794      /* We had better not be entering the interpreter from JIT-compiled code. */      /* We cannot reenter the interpreter while recording. */
2795      TraceRecorder *tr = NULL;      if (TRACE_RECORDER(cx))
2796      if (JS_ON_TRACE(cx)) {          js_AbortRecording(cx, "attempt to reenter interpreter while recording");
         tr = TRACE_RECORDER(cx);  
         SET_TRACE_RECORDER(cx, NULL);  
         JS_TRACE_MONITOR(cx).onTrace = JS_FALSE;  
     }  
2797  #endif  #endif
2798    
2799      /* Check for too deep of a native thread stack. */      /* Check for too deep of a native thread stack. */
# Line 2600  Line 2820 
2820    
2821  #define LOAD_ATOM(PCOFF)                                                      \  #define LOAD_ATOM(PCOFF)                                                      \
2822      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2823          JS_ASSERT((size_t)(atoms - script->atomMap.vector) <                  \          JS_ASSERT(fp->imacpc                                                  \
2824                    (size_t)(script->atomMap.length -                           \                    ? atoms == COMMON_ATOMS_START(&rt->atomState) &&            \
2825                             GET_INDEX(regs.pc + PCOFF)));                      \                      GET_INDEX(regs.pc + PCOFF) < js_common_atom_count         \
2826                      : (size_t)(atoms - script->atomMap.vector) <                \
2827                        (size_t)(script->atomMap.length -                         \
2828                                 GET_INDEX(regs.pc + PCOFF)));                    \
2829          atom = atoms[GET_INDEX(regs.pc + PCOFF)];                             \          atom = atoms[GET_INDEX(regs.pc + PCOFF)];                             \
2830      JS_END_MACRO      JS_END_MACRO
2831    
# Line 2610  Line 2833 
2833      (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))      (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
2834    
2835  #define LOAD_OBJECT(PCOFF)                                                    \  #define LOAD_OBJECT(PCOFF)                                                    \
2836      JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj)      (obj = script->getObject(GET_FULL_INDEX(PCOFF)))
2837    
2838  #define LOAD_FUNCTION(PCOFF)                                                  \  #define LOAD_FUNCTION(PCOFF)                                                  \
2839      JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)      (fun = script->getFunction(GET_FULL_INDEX(PCOFF)))
2840    
2841  #ifdef JS_TRACER  #ifdef JS_TRACER
2842    
2843  #define MONITOR_BRANCH(oldpc)                                                 \  #ifdef MOZ_TRACEVIS
2844    #if JS_THREADED_INTERP
2845    #define MONITOR_BRANCH_TRACEVIS                                               \
2846        JS_BEGIN_MACRO                                                            \
2847            if (jumpTable != interruptJumpTable)                                  \
2848                js_EnterTraceVisState(cx, S_RECORD, R_NONE);                      \
2849        JS_END_MACRO
2850    #else /* !JS_THREADED_INTERP */
2851    #define MONITOR_BRANCH_TRACEVIS                                               \
2852        JS_BEGIN_MACRO                                                            \
2853            js_EnterTraceVisState(cx, S_RECORD, R_NONE);                          \
2854        JS_END_MACRO
2855    #endif
2856    #else
2857    #define MONITOR_BRANCH_TRACEVIS
2858    #endif
2859    
2860    #define MONITOR_BRANCH()                                                      \
2861      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2862          if (TRACING_ENABLED(cx)) {                                            \          if (TRACING_ENABLED(cx)) {                                            \
2863              ENABLE_TRACER(js_MonitorLoopEdge(cx, oldpc, inlineCallCount));    \              if (js_MonitorLoopEdge(cx, inlineCallCount)) {                    \
2864                    JS_ASSERT(TRACE_RECORDER(cx));                                \
2865                    MONITOR_BRANCH_TRACEVIS;                                      \
2866                    ENABLE_INTERRUPTS();                                          \
2867                }                                                                 \
2868              fp = cx->fp;                                                      \              fp = cx->fp;                                                      \
2869              script = fp->script;                                              \              script = fp->script;                                              \
2870              atoms = script->atomMap.vector;                                   \              atoms = FrameAtomBase(cx, fp);                                    \
2871              currentVersion = (JSVersion) script->version;                     \              currentVersion = (JSVersion) script->version;                     \
2872              JS_ASSERT(fp->regs == &regs);                                     \              JS_ASSERT(fp->regs == &regs);                                     \
2873                if (cx->throwing)                                                 \
2874                    goto error;                                                   \
2875          }                                                                     \          }                                                                     \
2876      JS_END_MACRO      JS_END_MACRO
2877    
2878  #else /* !JS_TRACER */  #else /* !JS_TRACER */
2879    
2880  #define MONITOR_BRANCH(oldpc) ((void) 0)  #define MONITOR_BRANCH() ((void) 0)
2881    
2882  #endif /* !JS_TRACER */  #endif /* !JS_TRACER */
2883    
# Line 2641  Line 2887 
2887       */       */
2888  #define CHECK_BRANCH()                                                        \  #define CHECK_BRANCH()                                                        \
2889      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2890          if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) {                  \          if (!JS_CHECK_OPERATION_LIMIT(cx))                                    \
2891              if (!js_ResetOperationCount(cx))                                  \              goto error;                                                       \
                 goto error;                                                   \  
         }                                                                     \  
2892      JS_END_MACRO      JS_END_MACRO
2893    
2894    #ifndef TRACE_RECORDER
2895    #define TRACE_RECORDER(cx) (false)
2896    #endif
2897    
2898  #define BRANCH(n)                                                             \  #define BRANCH(n)                                                             \
2899      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2900          regs.pc += n;                                                         \          regs.pc += (n);                                                       \
2901          if (n <= 0) {                                                         \          op = (JSOp) *regs.pc;                                                 \
2902            if ((n) <= 0) {                                                       \
2903              CHECK_BRANCH();                                                   \              CHECK_BRANCH();                                                   \
2904              MONITOR_BRANCH(regs.pc - n);                                      \              if (op == JSOP_NOP) {                                             \
2905                    if (TRACE_RECORDER(cx)) {                                     \
2906                        MONITOR_BRANCH();                                         \
2907                        op = (JSOp) *regs.pc;                                     \
2908                    } else {                                                      \
2909                        op = (JSOp) *++regs.pc;                                   \
2910                    }                                                             \
2911                } else if (op == JSOP_TRACE) {                                    \
2912                    MONITOR_BRANCH();                                             \
2913                    op = (JSOp) *regs.pc;                                         \
2914                }                                                                 \
2915          }                                                                     \          }                                                                     \
         op = (JSOp) *regs.pc;                                                 \  
2916          DO_OP();                                                              \          DO_OP();                                                              \
2917      JS_END_MACRO      JS_END_MACRO
2918    
# Line 2676  Line 2934 
2934          js_SetVersion(cx, currentVersion);          js_SetVersion(cx, currentVersion);
2935    
2936      /* Update the static-link display. */      /* Update the static-link display. */
2937      if (script->staticDepth < JS_DISPLAY_SIZE) {      if (script->staticLevel < JS_DISPLAY_SIZE) {
2938          JSStackFrame **disp = &cx->display[script->staticDepth];          JSStackFrame **disp = &cx->display[script->staticLevel];
2939          fp->displaySave = *disp;          fp->displaySave = *disp;
2940          *disp = fp;          *disp = fp;
2941      }      }
2942  #ifdef DEBUG  
2943      fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;  # define CHECK_INTERRUPT_HANDLER()                                            \
2944  #endif      JS_BEGIN_MACRO                                                            \
2945            if (cx->debugHooks->interruptHandler)                                 \
2946                ENABLE_INTERRUPTS();                                              \
2947        JS_END_MACRO
2948    
2949      /*      /*
2950       * Load the debugger's interrupt hook here and after calling out to native       * Load the debugger's interrupt hook here and after calling out to native
# Line 2691  Line 2952 
2952       * not have to reload it each time through the interpreter loop -- we hope       * not have to reload it each time through the interpreter loop -- we hope
2953       * the compiler can keep it in a register when it is non-null.       * the compiler can keep it in a register when it is non-null.
2954       */       */
2955  #if JS_THREADED_INTERP      CHECK_INTERRUPT_HANDLER();
 #ifdef JS_TRACER  
 # define LOAD_INTERRUPT_HANDLER(cx)                                           \  
     ((void) (jumpTable = (cx)->debugHooks->interruptHandler                   \  
                          ? interruptJumpTable                                 \  
                          : TRACE_RECORDER(cx)                                 \  
                          ? recordingJumpTable                                 \  
                          : normalJumpTable))  
 # define ENABLE_TRACER(flag)                                                  \  
     JS_BEGIN_MACRO                                                            \  
         bool flag_ = (flag);                                                  \  
         JS_ASSERT(flag_ == !!TRACE_RECORDER(cx));                             \  
         jumpTable = flag_ ? recordingJumpTable : normalJumpTable;             \  
     JS_END_MACRO  
 #else /* !JS_TRACER */  
 # define LOAD_INTERRUPT_HANDLER(cx)                                           \  
     ((void) (jumpTable = (cx)->debugHooks->interruptHandler                   \  
                          ? interruptJumpTable                                 \  
                          : normalJumpTable))  
 # define ENABLE_TRACER(flag) ((void)0)  
 #endif /* !JS_TRACER */  
 #else /* !JS_THREADED_INTERP */  
 #ifdef JS_TRACER  
 # define LOAD_INTERRUPT_HANDLER(cx)                                           \  
     ((void) (switchMask = ((cx)->debugHooks->interruptHandler ||              \  
                            TRACE_RECORDER(cx)) ? 0 : 255))  
 # define ENABLE_TRACER(flag)                                                  \  
     JS_BEGIN_MACRO                                                            \  
         bool flag_ = (flag);                                                  \  
         JS_ASSERT(flag_ == !!TRACE_RECORDER(cx));                             \  
         switchMask = flag_ ? 0 : 255;                                         \  
     JS_END_MACRO  
 #else /* !JS_TRACER */  
 # define LOAD_INTERRUPT_HANDLER(cx)                                           \  
     ((void) (switchMask = ((cx)->debugHooks->interruptHandler                 \  
                            ? 0 : 255)))  
 # define ENABLE_TRACER(flag) ((void)0)  
 #endif /* !JS_TRACER */  
 #endif /* !JS_THREADED_INTERP */  
   
     LOAD_INTERRUPT_HANDLER(cx);  
2956    
2957      /* Initialize the pc and pc registers unless we're resuming a generator. */  #if !JS_HAS_GENERATORS
2958        JS_ASSERT(!fp->regs);
2959    #else
2960        /* Initialize the pc and sp registers unless we're resuming a generator. */
2961      if (JS_LIKELY(!fp->regs)) {      if (JS_LIKELY(!fp->regs)) {
2962    #endif
2963          ASSERT_NOT_THROWING(cx);          ASSERT_NOT_THROWING(cx);
2964          regs.pc = script->code;          regs.pc = script->code;
2965          regs.sp = StackBase(fp);          regs.sp = StackBase(fp);
2966          fp->regs = &regs;          fp->regs = &regs;
2967    #if JS_HAS_GENERATORS
2968      } else {      } else {
2969          JSGenerator *gen;          JSGenerator *gen;
2970    
# Line 2749  Line 2975 
2975          fp->regs = &regs;          fp->regs = &regs;
2976          JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);          JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2977          JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));          JS_ASSERT((size_t) (regs.sp - StackBase(fp)) <= StackDepth(script));
         JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);  
         JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp);  
2978    
2979          /*          /*
2980           * To support generator_throw and to catch ignored exceptions,           * To support generator_throw and to catch ignored exceptions,
# Line 2766  Line 2990 
2990              goto error;              goto error;
2991          }          }
2992      }      }
2993    #endif /* JS_HAS_GENERATORS */
2994    
2995      /*      /*
2996       * It is important that "op" be initialized before calling DO_OP because       * It is important that "op" be initialized before calling DO_OP because
# Line 2781  Line 3006 
3006       * This is a loop, but it does not look like a loop. The loop-closing       * This is a loop, but it does not look like a loop. The loop-closing
3007       * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.       * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
3008       * When interrupts are enabled, jumpTable is set to interruptJumpTable       * When interrupts are enabled, jumpTable is set to interruptJumpTable
3009       * where all jumps point to the JSOP_INTERRUPT case. The latter, after       * where all jumps point to the interrupt label. The latter, after
3010       * calling the interrupt handler, dispatches through normalJumpTable to       * calling the interrupt handler, dispatches through normalJumpTable to
3011       * continue the normal bytecode processing.       * continue the normal bytecode processing.
3012       */       */
3013  #else  
3014    #else /* !JS_THREADED_INTERP */
3015      for (;;) {      for (;;) {
3016        advance_pc_by_one:        advance_pc_by_one:
3017          JS_ASSERT(js_CodeSpec[op].length == 1);          JS_ASSERT(js_CodeSpec[op].length == 1);
# Line 2793  Line 3019 
3019        advance_pc:        advance_pc:
3020          regs.pc += len;          regs.pc += len;
3021          op = (JSOp) *regs.pc;          op = (JSOp) *regs.pc;
 #ifdef DEBUG  
         if (cx->tracefp)  
             js_TraceOpcode(cx, len);  
 #endif  
3022    
3023        do_op:        do_op:
3024          switchOp = op & switchMask;          CHECK_RECORDER();
3025            TRACE_OPCODE(op);
3026            switchOp = intN(op) | switchMask;
3027        do_switch:        do_switch:
3028          switch (switchOp) {          switch (switchOp) {
 #endif /* !JS_THREADED_INTERP */  
   
           BEGIN_CASE(JSOP_INTERRUPT)  
           {  
             JSTrapHandler handler;  
   
             handler = cx->debugHooks->interruptHandler;  
             if (handler) {  
                 switch (handler(cx, script, regs.pc, &rval,  
                                 cx->debugHooks->interruptHandlerData)) {  
                   case JSTRAP_ERROR:  
                     goto error;  
                   case JSTRAP_CONTINUE:  
                     break;  
                   case JSTRAP_RETURN:  
                     fp->rval = rval;  
                     ok = JS_TRUE;  
                     goto forced_return;  
                   case JSTRAP_THROW:  
                     cx->throwing = JS_TRUE;  
                     cx->exception = rval;  
                     goto error;  
                   default:;  
                 }  
 #if !JS_THREADED_INTERP  
             } else {  
                 /* this was not a real interrupt, the tracer is trying to  
                    record a trace */  
                 switchOp = op + 256;  
                 goto do_switch;  
 #endif  
             }  
             LOAD_INTERRUPT_HANDLER(cx);  
   
 #if JS_THREADED_INTERP  
             JS_EXTENSION_(goto *normalJumpTable[op]);  
 #else  
             switchOp = op;  
             goto do_switch;  
 #endif  
           }  
   
           /* No-ops for ease of decompilation. */  
           ADD_EMPTY_CASE(JSOP_NOP)  
           ADD_EMPTY_CASE(JSOP_GROUP)  
           ADD_EMPTY_CASE(JSOP_CONDSWITCH)  
           ADD_EMPTY_CASE(JSOP_TRY)  
           ADD_EMPTY_CASE(JSOP_FINALLY)  
 #if JS_HAS_XML_SUPPORT  
           ADD_EMPTY_CASE(JSOP_STARTXML)  
           ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)  
 #endif  
           END_EMPTY_CASES  
   
           /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */  
           BEGIN_CASE(JSOP_LINENO)  
           END_CASE(JSOP_LINENO)  
   
           BEGIN_CASE(JSOP_PUSH)  
             PUSH_OPND(JSVAL_VOID);  
           END_CASE(JSOP_PUSH)  
   
           BEGIN_CASE(JSOP_POP)  
             regs.sp--;  
           END_CASE(JSOP_POP)  
   
           BEGIN_CASE(JSOP_POPN)  
             regs.sp -= GET_UINT16(regs.pc);  
 #ifdef DEBUG  
             JS_ASSERT(StackBase(fp) <= regs.sp);  
             obj = fp->blockChain;  
             JS_ASSERT_IF(obj,  
                          OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)  
                          <= (size_t) (regs.sp - StackBase(fp)));  
             for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {  
                 clasp = OBJ_GET_CLASS(cx, obj);  
                 if (clasp != &js_BlockClass && clasp != &js_WithClass)  
                     continue;  
                 if (OBJ_GET_PRIVATE(cx, obj) != fp)  
                     break;  
                 JS_ASSERT(StackBase(fp) + OBJ_BLOCK_DEPTH(cx, obj)  
                                      + ((clasp == &js_BlockClass)  
                                         ? OBJ_BLOCK_COUNT(cx, obj)  
                                         : 1)  
                           <= regs.sp);  
             }  
 #endif  
           END_CASE(JSOP_POPN)  
   
           BEGIN_CASE(JSOP_SETRVAL)  
           BEGIN_CASE(JSOP_POPV)  
             ASSERT_NOT_THROWING(cx);  
             fp->rval = POP_OPND();  
           END_CASE(JSOP_POPV)  
   
           BEGIN_CASE(JSOP_ENTERWITH)  
             if (!js_EnterWith(cx, -1))  
                 goto error;  
   
             /*  
              * We must ensure that different "with" blocks have different  
              * stack depth associated with them. This allows the try handler  
              * search to properly recover the scope chain. Thus we must keep  
              * the stack at least at the current level.  
              *  
              * We set sp[-1] to the current "with" object to help asserting  
              * the enter/leave balance in [leavewith].  
              */  
             regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);  
           END_CASE(JSOP_ENTERWITH)  
   
           BEGIN_CASE(JSOP_LEAVEWITH)  
             JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));  
             regs.sp--;  
             js_LeaveWith(cx);  
           END_CASE(JSOP_LEAVEWITH)  
   
           BEGIN_CASE(JSOP_RETURN)  
             CHECK_BRANCH();  
             fp->rval = POP_OPND();  
             /* FALL THROUGH */  
   
           BEGIN_CASE(JSOP_RETRVAL)    /* fp->rval already set */  
           BEGIN_CASE(JSOP_STOP)  
             /*  
              * When the inlined frame exits with an exception or an error, ok  
              * will be false after the inline_return label.  
              */  
             ASSERT_NOT_THROWING(cx);  
             JS_ASSERT(regs.sp == StackBase(fp));  
             if ((fp->flags & JSFRAME_CONSTRUCTING) &&  
                 JSVAL_IS_PRIMITIVE(fp->rval)) {  
                 if (!fp->fun) {  
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,  
                                          JSMSG_BAD_NEW_RESULT,  
                                          js_ValueToPrintableString(cx, rval));  
                     goto error;  
                 }  
                 fp->rval = OBJECT_TO_JSVAL(fp->thisp);  
             }  
             ok = JS_TRUE;  
             if (inlineCallCount)  
           inline_return:  
             {  
                 JSInlineFrame *ifp = (JSInlineFrame *) fp;  
                 void *hookData = ifp->hookData;  
   
                 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);  
                 JS_ASSERT(!fp->blockChain);  
                 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));  
   
                 if (script->staticDepth < JS_DISPLAY_SIZE)  
                     cx->display[script->staticDepth] = fp->displaySave;  
   
                 if (hookData) {  
                     JSInterpreterHook hook;  
                     JSBool status;  
   
                     hook = cx->debugHooks->callHook;  
                     if (hook) {  
                         /*  
                          * Do not pass &ok directly as exposing the address  
                          * inhibits optimizations and uninitialised warnings.  
                          */  
                         status = ok;  
                         hook(cx, fp, JS_FALSE, &status, hookData);  
                         ok = status;  
                         LOAD_INTERRUPT_HANDLER(cx);  
                     }  
                 }  
   
                 /*  
                  * If fp has a call object, sync values and clear the back-  
                  * pointer. This can happen for a lightweight function if it  
                  * calls eval unexpectedly (in a way that is hidden from the  
                  * compiler). See bug 325540.  
                  */  
                 if (fp->callobj)  
                     ok &= js_PutCallObject(cx, fp);  
   
                 if (fp->argsobj)  
                     ok &= js_PutArgsObject(cx, fp);  
   
 #ifdef INCLUDE_MOZILLA_DTRACE  
                 /* DTrace function return, inlines */  
                 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())  
                     jsdtrace_function_rval(cx, fp, fp->fun);  
                 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())  
                     jsdtrace_function_return(cx, fp, fp->fun);  
 #endif  
   
                 /* Restore context version only if callee hasn't set version. */  
                 if (JS_LIKELY(cx->version == currentVersion)) {  
                     currentVersion = ifp->callerVersion;  
                     if (currentVersion != cx->version)  
                         js_SetVersion(cx, currentVersion);  
                 }  
   
                 /*  
                  * If inline-constructing, replace primitive rval with the new  
                  * object passed in via |this|, and instrument this constructor  
                  * invocation  
                  */  
                 if (fp->flags & JSFRAME_CONSTRUCTING) {  
                     if (JSVAL_IS_PRIMITIVE(fp->rval))  
                         fp->rval = OBJECT_TO_JSVAL(fp->thisp);  
                     JS_RUNTIME_METER(cx->runtime, constructs);  
                 }  
   
                 /* Restore caller's registers. */  
                 regs = ifp->callerRegs;  
   
                 /* Store the return value in the caller's operand frame. */  
                 regs.sp -= 1 + (size_t) ifp->frame.argc;  
                 regs.sp[-1] = fp->rval;  
   
                 /* Restore cx->fp and release the inline frame's space. */  
                 cx->fp = fp = fp->down;  
                 JS_ASSERT(fp->regs == &ifp->callerRegs);  
                 fp->regs = &regs;  
                 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);  
   
                 /* Restore the calling script's interpreter registers. */  
                 script = fp->script;  
                 atoms = script->atomMap.vector;  
   
                 /* Resume execution in the calling frame. */  
                 inlineCallCount--;  
                 if (JS_LIKELY(ok)) {  
 #ifdef JS_TRACER  
                     if (TRACE_RECORDER(cx))  
                         RECORD(LeaveFrame);  
3029  #endif  #endif
                     JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);  
                     len = JSOP_CALL_LENGTH;  
                     DO_NEXT_OP(len);  
                 }  
                 goto error;  
             }  
             goto exit;  
   
           BEGIN_CASE(JSOP_DEFAULT)  
             (void) POP();  
             /* FALL THROUGH */  
           BEGIN_CASE(JSOP_GOTO)  
             len = GET_JUMP_OFFSET(regs.pc);  
             BRANCH(len);  
           END_CASE(JSOP_GOTO)  
   
           BEGIN_CASE(JSOP_IFEQ)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond == JS_FALSE) {  
                 len = GET_JUMP_OFFSET(regs.pc);  
                 BRANCH(len);  
             }  
           END_CASE(JSOP_IFEQ)  
   
           BEGIN_CASE(JSOP_IFNE)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond != JS_FALSE) {  
                 len = GET_JUMP_OFFSET(regs.pc);  
                 BRANCH(len);  
             }  
           END_CASE(JSOP_IFNE)  
   
           BEGIN_CASE(JSOP_OR)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond == JS_TRUE) {  
                 len = GET_JUMP_OFFSET(regs.pc);  
                 PUSH_OPND(rval);  
                 DO_NEXT_OP(len);  
             }  
           END_CASE(JSOP_OR)  
   
           BEGIN_CASE(JSOP_AND)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond == JS_FALSE) {  
                 len = GET_JUMP_OFFSET(regs.pc);  
                 PUSH_OPND(rval);  
                 DO_NEXT_OP(len);  
             }  
           END_CASE(JSOP_AND)  
   
           BEGIN_CASE(JSOP_DEFAULTX)  
             (void) POP();  
             /* FALL THROUGH */  
           BEGIN_CASE(JSOP_GOTOX)  
             len = GET_JUMPX_OFFSET(regs.pc);  
             BRANCH(len);  
           END_CASE(JSOP_GOTOX);  
   
           BEGIN_CASE(JSOP_IFEQX)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond == JS_FALSE) {  
                 len = GET_JUMPX_OFFSET(regs.pc);  
                 BRANCH(len);  
             }  
           END_CASE(JSOP_IFEQX)  
   
           BEGIN_CASE(JSOP_IFNEX)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond != JS_FALSE) {  
                 len = GET_JUMPX_OFFSET(regs.pc);  
                 BRANCH(len);  
             }  
           END_CASE(JSOP_IFNEX)  
   
           BEGIN_CASE(JSOP_ORX)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond == JS_TRUE) {  
                 len = GET_JUMPX_OFFSET(regs.pc);  
                 PUSH_OPND(rval);  
                 DO_NEXT_OP(len);  
             }  
           END_CASE(JSOP_ORX)  
   
           BEGIN_CASE(JSOP_ANDX)  
             POP_BOOLEAN(cx, rval, cond);  
             if (cond == JS_FALSE) {  
                 len = GET_JUMPX_OFFSET(regs.pc);  
                 PUSH_OPND(rval);  
                 DO_NEXT_OP(len);  
             }  
           END_CASE(JSOP_ANDX)  
   
 /*  
  * If the index value at sp[n] is not an int that fits in a jsval, it could  
  * be an object (an XML QName, AttributeName, or AnyName), but only if we are  
  * compiling with JS_HAS_XML_SUPPORT.  Otherwise convert the index value to a  
  * string atom id.  
  */  
 #define FETCH_ELEMENT_ID(obj, n, id)                                          \  
     JS_BEGIN_MACRO                                                            \  
         jsval idval_ = FETCH_OPND(n);                                         \  
         if (JSVAL_IS_INT(idval_)) {                                           \  
             id = INT_JSVAL_TO_JSID(idval_);                                   \  
         } else {                                                              \  
             if (!js_InternNonIntElementId(cx, obj, idval_, &id))              \  
                 goto error;                                                   \  
             regs.sp[n] = ID_TO_VALUE(id);                                     \  
         }                                                                     \  
     JS_END_MACRO  
3030    
3031  #define TRY_BRANCH_AFTER_COND(cond,spdec)                                     \  /********************** Here we include the operations ***********************/
3032      JS_BEGIN_MACRO                                                            \  #include "jsops.cpp"
3033          uintN diff_;                                                          \  /*****************************************************************************/
         JS_ASSERT(js_CodeSpec[op].length == 1);                               \  
         diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ;                       \  
         if (diff_ <= 1) {                                                     \  
             regs.sp -= spdec;                                                 \  
             if (cond == (diff_ != 0)) {                                       \  
                 ++regs.pc;                                                    \  
                 len = GET_JUMP_OFFSET(regs.pc);                               \  
                 BRANCH(len);                                                  \  
             }                                                                 \  
             len = 1 + JSOP_IFEQ_LENGTH;                                       \  
             DO_NEXT_OP(len);                                                  \  
         }                                                                     \  
     JS_END_MACRO  
3034    
3035            BEGIN_CASE(JSOP_IN)  #if !JS_THREADED_INTERP
             rval = FETCH_OPND(-1);  
             if (JSVAL_IS_PRIMITIVE(rval)) {  
                 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);  
                 goto error;  
             }  
             obj = JSVAL_TO_OBJECT(rval);  
             FETCH_ELEMENT_ID(obj, -2, id);  
             if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))  
                 goto error;  
             cond = prop != NULL;  
             if (prop)  
                 OBJ_DROP_PROPERTY(cx, obj2, prop);  
             TRY_BRANCH_AFTER_COND(cond, 2);  
             regs.sp--;  
             STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));  
           END_CASE(JSOP_IN)  
   
           BEGIN_CASE(JSOP_ITER)  
             flags = regs.pc[1];  
             JS_ASSERT(regs.sp > StackBase(fp));  
             if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))  
                 goto error;  
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));  
           END_CASE(JSOP_ITER)  
   
           BEGIN_CASE(JSOP_FORPROP)  
             /*  
              * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop  
              * is not paid for the more common cases.  
              */  
             LOAD_ATOM(0);  
             id = ATOM_TO_JSID(atom);  
             i = -2;  
             goto do_forinloop;  
   
           BEGIN_CASE(JSOP_FORNAME)  
             LOAD_ATOM(0);  
             id = ATOM_TO_JSID(atom);  
             /* FALL THROUGH */  
   
           BEGIN_CASE(JSOP_FORARG)  
           BEGIN_CASE(JSOP_FORCONST)  
           BEGIN_CASE(JSOP_FORLOCAL)  
             /*  
              * These bytecodes don't require any lval computation here,  
              * because they address slots on the stack (in fp->args or  
              * fp->slots).  
              */  
             /* FALL THROUGH */  
   
           BEGIN_CASE(JSOP_FORELEM)  
             /*  
              * JSOP_FORELEM simply initializes or updates the iteration state  
              * and leaves the index expression evaluation and assignment to the  
              * enumerator until after the next property has been acquired, via  
              * a JSOP_ENUMELEM bytecode.  
              */  
             i = -1;  
   
           do_forinloop:  
             /*  
              * Reach under the top of stack to find our property iterator, a  
              * JSObject that contains the iteration state.  
              */  
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[i]));  
             if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[i]), &rval))  
                 goto error;  
             if (rval == JSVAL_HOLE) {  
                 rval = JSVAL_FALSE;  
 #ifdef JS_TRACER  
                 if (TRACE_RECORDER(cx)) {  
                     js_AbortRecording(cx, regs.pc, "Untraceable for-in loop");  
                     ENABLE_TRACER(0);  
                 }  
 #endif  
                 goto end_forinloop;  
             }  
   
             switch (op) {  
               case JSOP_FORARG:  
                 slot = GET_ARGNO(regs.pc);  
                 JS_ASSERT(slot < fp->fun->nargs);  
                 fp->argv[slot] = rval;  
                 break;  
   
               case JSOP_FORCONST:  
                 /* Don't update the const slot. */  
                 break;  
   
               case JSOP_FORLOCAL:  
                 slot = GET_SLOTNO(regs.pc);  
                 JS_ASSERT(slot < fp->script->nslots);  
                 vp = &fp->slots[slot];  
                 GC_POKE(cx, *vp);  
                 *vp = rval;  
                 break;  
   
               case JSOP_FORELEM:  
                 /* FORELEM is not a SET operation, it's more like BINDNAME. */  
                 PUSH_OPND(rval);  
                 break;  
   
               case JSOP_FORPROP:  
                 /*  
                  * We fetch object here to ensure that the iterator is called  
                  * even if lval is null or undefined that throws in  
                  * FETCH_OBJECT. See bug 372331.  
                  */  
                 FETCH_OBJECT(cx, -1, lval, obj);  
                 goto set_for_property;  
   
               default:  
                 JS_ASSERT(op == JSOP_FORNAME);  
   
                 /*  
                  * We find property here after the iterator call to ensure  
                  * that we take into account side effects of the iterator  
                  * call. See bug 372331.  
                  */  
                 if (!js_FindProperty(cx, id, &obj, &obj2, &prop))  
                     goto error;  
                 if (prop)  
                     OBJ_DROP_PROPERTY(cx, obj2, prop);  
   
               set_for_property:  
                 /* Set the variable obj[id] to refer to rval. */  
                 fp->flags |= JSFRAME_ASSIGNING;  
                 ok = OBJ_SET_PROPERTY(cx, obj, id, &rval);  
                 fp->flags &= ~JSFRAME_ASSIGNING;  
                 if (!ok)  
                     goto error;  
                 break;  
             }  
   
             /* Push true to keep looping through properties. */  
             rval = JSVAL_TRUE;  
   
           end_forinloop:  
             regs.sp += i + 1;  
             PUSH_OPND(rval);  
             len = js_CodeSpec[op].length;  
             DO_NEXT_OP(len);  
   
           BEGIN_CASE(JSOP_DUP)  
             JS_ASSERT(regs.sp > StackBase(fp));  
             rval = FETCH_OPND(-1);  
             PUSH(rval);  
           END_CASE(JSOP_DUP)  
   
           BEGIN_CASE(JSOP_DUP2)  
             JS_ASSERT(regs.sp - 2 >= StackBase(fp));  
             lval = FETCH_OPND(-2);  
             rval = FETCH_OPND(-1);  
             PUSH(lval);  
             PUSH(rval);  
           END_CASE(JSOP_DUP2)  
   
 #define PROPERTY_OP(n, call)                                                  \  
     JS_BEGIN_MACRO                                                            \  
         /* Fetch the left part and resolve it to a non-null object. */        \  
         FETCH_OBJECT(cx, n, lval, obj);                                       \  
                                                                               \  
         /* Get or set the property. */                                        \  
         if (!call)                                                            \  
             goto error;                                                       \  
     JS_END_MACRO  
   
 #define ELEMENT_OP(n, call)                                                   \  
     JS_BEGIN_MACRO                                                            \  
         /* Fetch the left part and resolve it to a non-null object. */        \  
         FETCH_OBJECT(cx, n - 1, lval, obj);                                   \  
                                                                               \  
         /* Fetch index and convert it to id suitable for use with obj. */     \  
         FETCH_ELEMENT_ID(obj, n, id);                                         \  
                                                                               \  
         /* Get or set the element. */                                         \  
         if (!call)                                                            \  
             goto error;                                                       \  
     JS_END_MACRO  
   
 #define NATIVE_GET(cx,obj,pobj,sprop,vp)                                      \  
     JS_BEGIN_MACRO                                                            \  
         if (SPROP_HAS_STUB_GETTER(sprop)) {                                   \  
             /* Fast path for Object instance properties. */                   \  
             JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT ||                  \  
                       !SPROP_HAS_STUB_SETTER(sprop));                         \  
             *vp = ((sprop)->slot != SPROP_INVALID_SLOT)                       \  
                   ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot)                  \  
                   : JSVAL_VOID;                                               \  
         } else {                                                              \  
             if (!js_NativeGet(cx, obj, pobj, sprop, vp))                      \  
                 goto error;                                                   \  
         }                                                                     \  
     JS_END_MACRO  
   
 #define NATIVE_SET(cx,obj,sprop,vp)                                           \  
     JS_BEGIN_MACRO                                                            \  
         if (SPROP_HAS_STUB_SETTER(sprop) &&                                   \  
             (sprop)->slot != SPROP_INVALID_SLOT) {                            \  
             /* Fast path for, e.g., Object instance properties. */            \  
             LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp);            \  
         } else {                                                              \  
             if (!js_NativeSet(cx, obj, sprop, vp))                            \  
                 goto error;                                                   \  
         }                                                                     \  
     JS_END_MACRO  
   
 /*  
  * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on  
  * single-thread DEBUG js shell testing to verify property cache hits.  
  */  
 #if defined DEBUG && !defined JS_THREADSAFE  
 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \  
     do {                                                                      \  
         JSAtom *atom_;                                                        \  
         JSObject *obj_, *pobj_;                                               \  
         JSProperty *prop_;                                                    \  
         JSScopeProperty *sprop_;                                              \  
         uint32 sample_ = rt->gcNumber;                                        \  
         if (pcoff >= 0)                                                       \  
             GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom_);            \  
         else                                                                  \  
             atom_ = rt->atomState.lengthAtom;                                 \  
         if (JOF_OPMODE(*regs.pc) == JOF_NAME) {                               \  
             ok = js_FindProperty(cx, ATOM_TO_JSID(atom_), &obj_, &pobj_,      \  
                                  &prop_);                                     \  
         } else {                                                              \  
             obj_ = obj;                                                       \  
             ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom_), &pobj_,      \  
                                    &prop_);                                   \  
         }                                                                     \  
         if (!ok)                                                              \  
             goto error;                                                       \  
         if (rt->gcNumber != sample_)                                          \  
             break;                                                            \  
         JS_ASSERT(prop_);                                                     \  
         JS_ASSERT(pobj_ == pobj);                                             \  
         sprop_ = (JSScopeProperty *) prop_;                                   \  
         if (PCVAL_IS_SLOT(entry->vword)) {                                    \  
             JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop_->slot);           \  
         } else if (PCVAL_IS_SPROP(entry->vword)) {                            \  
             JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop_);                \  
         } else {                                                              \  
             jsval v_;                                                         \  
             JS_ASSERT(PCVAL_IS_OBJECT(entry->vword));                         \  
             JS_ASSERT(entry->vword != PCVAL_NULL);                            \  
             JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj)));                     \  
             JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop_));                         \  
             JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop_, OBJ_SCOPE(pobj_)));        \  
             v_ = LOCKED_OBJ_GET_SLOT(pobj_, sprop_->slot);                    \  
             JS_ASSERT(VALUE_IS_FUNCTION(cx, v_));                             \  
             JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v_));  \  
         }                                                                     \  
         OBJ_DROP_PROPERTY(cx, pobj_, prop_);                                  \  
     } while (0)  
 #else  
 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)  
 #endif  
   
 /*  
  * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is  
  * the constant length of the SET opcode sequence, and spdec is the constant  
  * by which to decrease the stack pointer to pop all of the SET op's operands.  
  *  
  * NB: unlike macros that could conceivably be replaced by functions (ignoring  
  * goto error), where a call should not have to be braced in order to expand  
  * correctly (e.g., in if (cond) FOO(); else BAR()), these three macros lack  
  * JS_{BEGIN,END}_MACRO brackets. They are also indented so as to align with  
  * nearby opcode code.  
  */  
 #define SKIP_POP_AFTER_SET(oplen,spdec)                                       \  
             if (regs.pc[oplen] == JSOP_POP) {                                 \  
                 regs.sp -= spdec;                                             \  
                 regs.pc += oplen + JSOP_POP_LENGTH;                           \  
                 op = (JSOp) *regs.pc;                                         \  
                 DO_OP();                                                      \  
             }  
   
 #define END_SET_CASE(OP)                                                      \  
             SKIP_POP_AFTER_SET(OP##_LENGTH, 1);                               \  
           END_CASE(OP)  
   
 #define END_SET_CASE_STORE_RVAL(OP,spdec)                                     \  
             SKIP_POP_AFTER_SET(OP##_LENGTH, spdec);                           \  
             rval = FETCH_OPND(-1);                                            \  
             regs.sp -= (spdec) - 1;                                           \  
             STORE_OPND(-1, rval);                                             \  
           END_CASE(OP)  
   
           BEGIN_CASE(JSOP_SETCONST)  
             LOAD_ATOM(0);  
             obj = fp->varobj;  
             rval = FETCH_OPND(-1);  
             if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval,  
                                      JS_PropertyStub, JS_PropertyStub,  
                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |  
                                      JSPROP_READONLY,  
                                      NULL)) {  
                 goto error;  
             }  
           END_SET_CASE(JSOP_SETCONST);  
   
 #if JS_HAS_DESTRUCTURING  
           BEGIN_CASE(JSOP_ENUMCONSTELEM)  
             rval = FETCH_OPND(-3);  
             FETCH_OBJECT(cx, -2, lval, obj);  
             FETCH_ELEMENT_ID(obj, -1, id);  
             if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval,  
                                      JS_PropertyStub, JS_PropertyStub,  
                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |  
                                      JSPROP_READONLY,  
                                      NULL)) {  
                 goto error;  
             }  
             regs.sp -= 3;  
           END_CASE(JSOP_ENUMCONSTELEM)  
 #endif  
   
           BEGIN_CASE(JSOP_BINDNAME)  
             do {  
                 JSPropCacheEntry *entry;  
   
                 obj = fp->scopeChain;  
                 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {  
                     PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);  
                     if (!atom) {  
                         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);  
                         JS_UNLOCK_OBJ(cx, obj2);  
                         break;  
                     }  
                 } else {  
                     entry = NULL;  
                     LOAD_ATOM(0);  
                 }  
                 id = ATOM_TO_JSID(atom);  
                 obj = js_FindIdentifierBase(cx, id, entry);  
                 if (!obj)  
                     goto error;  
             } while (0);  
             PUSH_OPND(OBJECT_TO_JSVAL(obj));  
           END_CASE(JSOP_BINDNAME)  
   
 #define BITWISE_OP(OP)                                                        \  
     JS_BEGIN_MACRO                                                            \  
         FETCH_INT(cx, -2, i);                                                 \  
         FETCH_INT(cx, -1, j);                                                 \  
         i = i OP j;                                                           \  
         regs.sp--;                                                            \  
         STORE_INT(cx, -1, i);                                                 \  
     JS_END_MACRO  
   
           BEGIN_CASE(JSOP_BITOR)  
             BITWISE_OP(|);  
           END_CASE(JSOP_BITOR)  
   
           BEGIN_CASE(JSOP_BITXOR)  
             BITWISE_OP(^);  
           END_CASE(JSOP_BITXOR)  
   
           BEGIN_CASE(JSOP_BITAND)  
             BITWISE_OP(&);  
           END_CASE(JSOP_BITAND)  
   
 #define RELATIONAL_OP(OP)                                                     \  
     JS_BEGIN_MACRO                                                            \  
         rval = FETCH_OPND(-1);                                                \  
         lval = FETCH_OPND(-2);                                                \  
         /* Optimize for two int-tagged operands (typical loop control). */    \  
         if ((lval & rval) & JSVAL_INT) {                                      \  
             cond = JSVAL_TO_INT(lval) OP JSVAL_TO_INT(rval);                  \  
         } else {                                                              \  
             if (!JSVAL_IS_PRIMITIVE(lval))                                    \  
                 DEFAULT_VALUE(cx, -2, JSTYPE_NUMBER, lval);                   \  
             if (!JSVAL_IS_PRIMITIVE(rval))                                    \  
                 DEFAULT_VALUE(cx, -1, JSTYPE_NUMBER, rval);                   \  
             if (JSVAL_IS_STRING(lval) && JSVAL_IS_STRING(rval)) {             \  
                 str  = JSVAL_TO_STRING(lval);                                 \  
                 str2 = JSVAL_TO_STRING(rval);                                 \  
                 cond = js_CompareStrings(str, str2) OP 0;                     \  
             } else {                                                          \  
                 VALUE_TO_NUMBER(cx, -2, lval, d);                             \  
                 VALUE_TO_NUMBER(cx, -1, rval, d2);                            \  
                 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE);                 \  
             }                                                                 \  
         }                                                                     \  
         TRY_BRANCH_AFTER_COND(cond, 2);                                       \  
         regs.sp--;                                                            \  
         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \  
     JS_END_MACRO  
   
 /*  
  * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies  
  * because they begin if/else chains, so callers must not put semicolons after  
  * the call expressions!  
  */  
 #if JS_HAS_XML_SUPPORT  
 #define XML_EQUALITY_OP(OP)                                                   \  
     if ((ltmp == JSVAL_OBJECT &&                                              \  
          (obj2 = JSVAL_TO_OBJECT(lval)) &&                                    \  
          OBJECT_IS_XML(cx, obj2)) ||                                          \  
         (rtmp == JSVAL_OBJECT &&                                              \  
          (obj2 = JSVAL_TO_OBJECT(rval)) &&                                    \  
          OBJECT_IS_XML(cx, obj2))) {                                          \  
         JSXMLObjectOps *ops;                                                  \  
                                                                               \  
         ops = (JSXMLObjectOps *) obj2->map->ops;                              \  
         if (obj2 == JSVAL_TO_OBJECT(rval))                                    \  
             rval = lval;                                                      \  
         if (!ops->equality(cx, obj2, rval, &cond))                            \  
             goto error;                                                       \  
         cond = cond OP JS_TRUE;                                               \  
     } else  
   
 #define EXTENDED_EQUALITY_OP(OP)                                              \  
     if (ltmp == JSVAL_OBJECT &&                                               \  
         (obj2 = JSVAL_TO_OBJECT(lval)) &&                                     \  
         ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) {   \  
         JSExtendedClass *xclasp;                                              \  
                                                                               \  
         xclasp = (JSExtendedClass *) clasp;                                   \  
         if (!xclasp->equality(cx, obj2, rval, &cond))                         \  
             goto error;                                                       \  
         cond = cond OP JS_TRUE;                                               \  
     } else  
 #else  
 #define XML_EQUALITY_OP(OP)             /* nothing */  
 #define EXTENDED_EQUALITY_OP(OP)        /* nothing */  
 #endif  
   
 #define EQUALITY_OP(OP, IFNAN)                                                \  
     JS_BEGIN_MACRO                                                            \  
         rval = FETCH_OPND(-1);                                                \  
         lval = FETCH_OPND(-2);                                                \  
         ltmp = JSVAL_TAG(lval);                                               \  
         rtmp = JSVAL_TAG(rval);                                               \  
         XML_EQUALITY_OP(OP)                                                   \  
         if (ltmp == rtmp) {                                                   \  
             if (ltmp == JSVAL_STRING) {                                       \  
                 str  = JSVAL_TO_STRING(lval);                                 \  
                 str2 = JSVAL_TO_STRING(rval);                                 \  
                 cond = js_EqualStrings(str, str2) OP JS_TRUE;                 \  
             } else if (ltmp == JSVAL_DOUBLE) {                                \  
                 d  = *JSVAL_TO_DOUBLE(lval);                                  \  
                 d2 = *JSVAL_TO_DOUBLE(rval);                                  \  
                 cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN);                    \  
             } else {                                                          \  
                 EXTENDED_EQUALITY_OP(OP)                                      \  
                 /* Handle all undefined (=>NaN) and int combinations. */      \  
                 cond = lval OP rval;                                          \  
             }                                                                 \  
         } else {                                                              \  
             if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) {                 \  
                 cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1;     \  
             } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) {          \  
                 cond = 1 OP 0;                                                \  
             } else {                                                          \  
                 if (ltmp == JSVAL_OBJECT) {                                   \  
                     DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);                 \  
                     ltmp = JSVAL_TAG(lval);                                   \  
                 } else if (rtmp == JSVAL_OBJECT) {                            \  
                     DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);                 \  
                     rtmp = JSVAL_TAG(rval);                                   \  
                 }                                                             \  
                 if (ltmp == JSVAL_STRING && rtmp == JSVAL_STRING) {           \  
                     str  = JSVAL_TO_STRING(lval);                             \  
                     str2 = JSVAL_TO_STRING(rval);                             \  
                     cond = js_EqualStrings(str, str2) OP JS_TRUE;             \  
                 } else {                                                      \  
                     VALUE_TO_NUMBER(cx, -2, lval, d);                         \  
                     VALUE_TO_NUMBER(cx, -1, rval, d2);                        \  
                     cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN);                \  
                 }                                                             \  
             }                                                                 \  
         }                                                                     \  
         TRY_BRANCH_AFTER_COND(cond, 2);                                       \  
         regs.sp--;                                                            \  
         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \  
     JS_END_MACRO  
   
           BEGIN_CASE(JSOP_EQ)  
             EQUALITY_OP(==, JS_FALSE);  
           END_CASE(JSOP_EQ)  
   
           BEGIN_CASE(JSOP_NE)  
             EQUALITY_OP(!=, JS_TRUE);  
           END_CASE(JSOP_NE)  
   
 #define STRICT_EQUALITY_OP(OP)                                                \  
     JS_BEGIN_MACRO                                                            \  
         rval = FETCH_OPND(-1);                                                \  
         lval = FETCH_OPND(-2);                                                \  
         cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE;                   \  
         regs.sp--;                                                            \  
         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \  
     JS_END_MACRO  
   
           BEGIN_CASE(JSOP_STRICTEQ)  
             STRICT_EQUALITY_OP(==);  
           END_CASE(JSOP_STRICTEQ)  
   
           BEGIN_CASE(JSOP_STRICTNE)  
             STRICT_EQUALITY_OP(!=);  
           END_CASE(JSOP_STRICTNE)  
   
           BEGIN_CASE(JSOP_CASE)  
             STRICT_EQUALITY_OP(==);  
             (void) POP();  
             if (cond) {  
                 len = GET_JUMP_OFFSET(regs.pc);  
                 BRANCH(len);  
             }  
             PUSH(lval);  
           END_CASE(JSOP_CASE)  
   
           BEGIN_CASE(JSOP_CASEX)  
             STRICT_EQUALITY_OP(==);  
             (void) POP();  
             if (cond) {  
                 len = GET_JUMPX_OFFSET(regs.pc);  
                 BRANCH(len);  
             }  
             PUSH(lval);  
           END_CASE(JSOP_CASEX)  
   
           BEGIN_CASE(JSOP_LT)  
             RELATIONAL_OP(<);  
           END_CASE(JSOP_LT)  
   
           BEGIN_CASE(JSOP_LE)  
             RELATIONAL_OP(<=);  
           END_CASE(JSOP_LE)  
   
           BEGIN_CASE(JSOP_GT)  
             RELATIONAL_OP(>);  
           END_CASE(JSOP_GT)  
   
           BEGIN_CASE(JSOP_GE)  
             RELATIONAL_OP(>=);  
           END_CASE(JSOP_GE)  
   
 #undef EQUALITY_OP  
 #undef RELATIONAL_OP  
   
 #define SIGNED_SHIFT_OP(OP)                                                   \  
     JS_BEGIN_MACRO                                                            \  
         FETCH_INT(cx, -2, i);                                                 \  
         FETCH_INT(cx, -1, j);                                                 \  
         i = i OP (j & 31);                                                    \  
         regs.sp--;                                                            \  
         STORE_INT(cx, -1, i);                                                 \  
     JS_END_MACRO  
   
           BEGIN_CASE(JSOP_LSH)  
             SIGNED_SHIFT_OP(<<);  
           END_CASE(JSOP_LSH)  
   
           BEGIN_CASE(JSOP_RSH)  
             SIGNED_SHIFT_OP(>>);  
           END_CASE(JSOP_RSH)  
   
           BEGIN_CASE(JSOP_URSH)  
           {  
             uint32 u;  
   
             FETCH_UINT(cx, -2, u);  
             FETCH_INT(cx, -1, j);  
             u >>= (j & 31);  
             regs.sp--;  
             STORE_UINT(cx, -1, u);  
           }  
           END_CASE(JSOP_URSH)  
   
 #undef BITWISE_OP  
 #undef SIGNED_SHIFT_OP  
   
           BEGIN_CASE(JSOP_ADD)  
             rval = FETCH_OPND(-1);  
             lval = FETCH_OPND(-2);  
 #if JS_HAS_XML_SUPPORT  
             if (!JSVAL_IS_PRIMITIVE(lval) &&  
                 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&  
                 VALUE_IS_XML(cx, rval)) {  
                 JSXMLObjectOps *ops;  
   
                 ops = (JSXMLObjectOps *) obj2->map->ops;  
                 if (!ops->concatenate(cx, obj2, rval, &rval))  
                     goto error;  
                 regs.sp--;  
                 STORE_OPND(-1, rval);  
             } else  
 #endif  
             {  
                 if (!JSVAL_IS_PRIMITIVE(lval))  
                     DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);  
                 if (!JSVAL_IS_PRIMITIVE(rval))  
                     DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);  
                 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {  
                     if (cond) {  
                         str = JSVAL_TO_STRING(lval);  
                         str2 = js_ValueToString(cx, rval);  
                         if (!str2)  
                             goto error;  
                         regs.sp[-1] = STRING_TO_JSVAL(str2);  
                     } else {  
                         str2 = JSVAL_TO_STRING(rval);  
                         str = js_ValueToString(cx, lval);  
                         if (!str)  
                             goto error;  
                         regs.sp[-2] = STRING_TO_JSVAL(str);  
                     }  
                     str = js_ConcatStrings(cx, str, str2);  
                     if (!str)  
                         goto error;  
                     regs.sp--;  
                     STORE_OPND(-1, STRING_TO_JSVAL(str));  
                 } else {  
                     VALUE_TO_NUMBER(cx, -2, lval, d);  
                     VALUE_TO_NUMBER(cx, -1, rval, d2);  
                     d += d2;  
                     regs.sp--;  
                     STORE_NUMBER(cx, -1, d);  
                 }  
             }  
           END_CASE(JSOP_ADD)  
   
 #define BINARY_OP(OP)                                                         \  
     JS_BEGIN_MACRO                                                            \  
         FETCH_NUMBER(cx, -2, d);                                              \  
         FETCH_NUMBER(cx, -1, d2);                                             \  
         d = d OP d2;                                                          \  
         regs.sp--;                                                            \  
         STORE_NUMBER(cx, -1, d);                                              \  
     JS_END_MACRO  
   
           BEGIN_CASE(JSOP_SUB)  
             BINARY_OP(-);  
           END_CASE(JSOP_SUB)  
   
           BEGIN_CASE(JSOP_MUL)  
             BINARY_OP(*);  
           END_CASE(JSOP_MUL)  
   
           BEGIN_CASE(JSOP_DIV)  
             FETCH_NUMBER(cx, -1, d2);  
             FETCH_NUMBER(cx, -2, d);  
             regs.sp--;  
             if (d2 == 0) {  
 #ifdef XP_WIN  
                 /* XXX MSVC miscompiles such that (NaN == 0) */  
                 if (JSDOUBLE_IS_NaN(d2))  
                     rval = DOUBLE_TO_JSVAL(rt->jsNaN);  
                 else  
 #endif  
                 if (d == 0 || JSDOUBLE_IS_NaN(d))  
                     rval = DOUBLE_TO_JSVAL(rt->jsNaN);  
                 else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31)  
                     rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity);  
                 else  
                     rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity);  
                 STORE_OPND(-1, rval);  
             } else {  
                 d /= d2;  
                 STORE_NUMBER(cx, -1, d);  
             }  
           END_CASE(JSOP_DIV)  
   
           BEGIN_CASE(JSOP_MOD)  
             FETCH_NUMBER(cx, -1, d2);  
             FETCH_NUMBER(cx, -2, d);  
             regs.sp--;  
             if (d2 == 0) {  
                 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));  
             } else {  
 #ifdef XP_WIN  
               /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */  
               if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))  
 #endif  
                 d = fmod(d, d2);  
                 STORE_NUMBER(cx, -1, d);  
             }  
           END_CASE(JSOP_MOD)  
   
           BEGIN_CASE(JSOP_NOT)  
             POP_BOOLEAN(cx, rval, cond);  
             PUSH_OPND(BOOLEAN_TO_JSVAL(!cond));  
           END_CASE(JSOP_NOT)  
   
           BEGIN_CASE(JSOP_BITNOT)  
             FETCH_INT(cx, -1, i);  
             i = ~i;  
             STORE_INT(cx, -1, i);  
           END_CASE(JSOP_BITNOT)  
   
           BEGIN_CASE(JSOP_NEG)  
             /*  
              * When the operand is int jsval, INT_FITS_IN_JSVAL(i) implies  
              * INT_FITS_IN_JSVAL(-i) unless i is 0 or JSVAL_INT_MIN when the  
              * results, -0.0 or JSVAL_INT_MAX + 1, are jsdouble values.  
              */  
             rval = FETCH_OPND(-1);  
             if (JSVAL_IS_INT(rval) &&  
                 rval != INT_TO_JSVAL(JSVAL_INT_MIN) &&  
                 (i = JSVAL_TO_INT(rval)) != 0) {  
                 JS_STATIC_ASSERT(!INT_FITS_IN_JSVAL(-JSVAL_INT_MIN));  
                 i = -i;  
                 JS_ASSERT(INT_FITS_IN_JSVAL(i));  
                 regs.sp[-1] = INT_TO_JSVAL(i);  
             } else {  
                 if (JSVAL_IS_DOUBLE(rval)) {  
                     d = *JSVAL_TO_DOUBLE(rval);  
                 } else {  
                     d = js_ValueToNumber(cx, &regs.sp[-1]);  
                     if (JSVAL_IS_NULL(regs.sp[-1]))  
                         goto error;  
                     JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) ||  
                               regs.sp[-1] == JSVAL_TRUE);  
                 }  
 #ifdef HPUX  
                 /*  
                  * Negation of a zero doesn't produce a negative  
                  * zero on HPUX. Perform the operation by bit  
                  * twiddling.  
                  */  
                 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;  
 #else  
                 d = -d;  
 #endif  
                 if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))  
                     goto error;  
             }  
           END_CASE(JSOP_NEG)  
   
           BEGIN_CASE(JSOP_POS)  
             rval = FETCH_OPND(-1);  
             if (!JSVAL_IS_NUMBER(rval)) {  
                 d = js_ValueToNumber(cx, &regs.sp[-1]);  
                 rval = regs.sp[-1];  
                 if (JSVAL_IS_NULL(rval))  
                     goto error;  
                 if (rval == JSVAL_TRUE) {  
                     if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))  
                         goto error;  
                 } else {  
                     JS_ASSERT(JSVAL_IS_NUMBER(rval));  
                 }  
             }  
           END_CASE(JSOP_POS)  
   
           BEGIN_CASE(JSOP_DELNAME)  
             LOAD_ATOM(0);  
             id = ATOM_TO_JSID(atom);  
             if (!js_FindProperty(cx, id, &obj, &obj2, &prop))  
                 goto error;  
   
             /* ECMA says to return true if name is undefined or inherited. */  
             PUSH_OPND(JSVAL_TRUE);  
             if (prop) {  
                 OBJ_DROP_PROPERTY(cx, obj2, prop);  
                 if (!OBJ_DELETE_PROPERTY(cx, obj, id, &regs.sp[-1]))  
                     goto error;  
             }  
           END_CASE(JSOP_DELNAME)  
   
           BEGIN_CASE(JSOP_DELPROP)  
             LOAD_ATOM(0);  
             id = ATOM_TO_JSID(atom);  
             PROPERTY_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval));  
             STORE_OPND(-1, rval);  
           END_CASE(JSOP_DELPROP)  
   
           BEGIN_CASE(JSOP_DELELEM)  
             ELEMENT_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval));  
             regs.sp--;  
             STORE_OPND(-1, rval);  
           END_CASE(JSOP_DELELEM)  
   
           BEGIN_CASE(JSOP_TYPEOFEXPR)  
           BEGIN_CASE(JSOP_TYPEOF)  
             rval = FETCH_OPND(-1);  
             type = JS_TypeOfValue(cx, rval);  
             atom = rt->atomState.typeAtoms[type];  
             STORE_OPND(-1, ATOM_KEY(atom));  
           END_CASE(JSOP_TYPEOF)  
   
           BEGIN_CASE(JSOP_VOID)  
             STORE_OPND(-1, JSVAL_VOID);  
           END_CASE(JSOP_VOID)  
   
           BEGIN_CASE(JSOP_INCELEM)  
           BEGIN_CASE(JSOP_DECELEM)  
           BEGIN_CASE(JSOP_ELEMINC)  
           BEGIN_CASE(JSOP_ELEMDEC)  
             /*  
              * Delay fetching of id until we have the object to ensure  
              * the proper evaluation order. See bug 372331.  
              */  
             id = 0;  
             i = -2;  
             goto fetch_incop_obj;  
   
           BEGIN_CASE(JSOP_INCPROP)  
           BEGIN_CASE(JSOP_DECPROP)  
           BEGIN_CASE(JSOP_PROPINC)  
           BEGIN_CASE(JSOP_PROPDEC)  
             LOAD_ATOM(0);  
             id = ATOM_TO_JSID(atom);  
             i = -1;  
   
           fetch_incop_obj:  
             FETCH_OBJECT(cx, i, lval, obj);  
             if (id == 0)  
                 FETCH_ELEMENT_ID(obj, -1, id);  
             goto do_incop;  
   
           BEGIN_CASE(JSOP_INCNAME)  
           BEGIN_CASE(JSOP_DECNAME)  
           BEGIN_CASE(JSOP_NAMEINC)  
           BEGIN_CASE(JSOP_NAMEDEC)  
           {  
             JSPropCacheEntry *entry;  
   
             obj = fp->scopeChain;  
             if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {  
                 PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);  
                 if (!atom) {  
                     ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);  
                     if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {  
                         slot = PCVAL_TO_SLOT(entry->vword);  
                         JS_ASSERT(slot < obj->map->freeslot);  
                         rval = LOCKED_OBJ_GET_SLOT(obj, slot);  
                         if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {  
                             rtmp = rval;  
                             rval += (js_CodeSpec[op].format & JOF_INC) ? 2 : -2;  
                             if (!(js_CodeSpec[op].format & JOF_POST))  
                                 rtmp = rval;  
                             LOCKED_OBJ_SET_SLOT(obj, slot, rval);  
                             JS_UNLOCK_OBJ(cx, obj);  
                             PUSH_OPND(rtmp);  
                             len = JSOP_INCNAME_LENGTH;  
                             DO_NEXT_OP(len);  
                         }  
                     }  
                     JS_UNLOCK_OBJ(cx, obj2);  
                     LOAD_ATOM(0);  
                 }  
             } else {  
                 entry = NULL;  
                 LOAD_ATOM(0);  
             }  
             id = ATOM_TO_JSID(atom);  
             if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)  
                 goto error;  
             if (!prop)  
                 goto atom_not_defined;  
             OBJ_DROP_PROPERTY(cx, obj2, prop);  
           }  
   
           do_incop:  
           {  
             const JSCodeSpec *cs;  
             jsval v;  
   
             /*  
              * We need a root to store the value to leave on the stack until  
              * we have done with OBJ_SET_PROPERTY.  
              */  
             PUSH_OPND(JSVAL_NULL);  
             if (!OBJ_GET_PROPERTY(cx, obj, id, &regs.sp[-1]))  
                 goto error;  
   
             cs = &js_CodeSpec[op];  
             JS_ASSERT(cs->ndefs == 1);  
             JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2);  
             v = regs.sp[-1];  
             if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) {  
                 jsval incr;  
   
                 incr = (cs->format & JOF_INC) ? 2 : -2;  
                 if (cs->format & JOF_POST) {  
                     regs.sp[-1] = v + incr;  
                 } else {  
                     v += incr;  
                     regs.sp[-1] = v;  
                 }  
                 fp->flags |= JSFRAME_ASSIGNING;  
                 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);  
                 fp->flags &= ~JSFRAME_ASSIGNING;  
                 if (!ok)  
                     goto error;  
   
                 /*  
                  * We must set regs.sp[-1] to v for both post and pre increments  
                  * as the setter overwrites regs.sp[-1].  
                  */  
                 regs.sp[-1] = v;  
             } else {  
                 /* We need an extra root for the result. */  
                 PUSH_OPND(JSVAL_NULL);  
                 if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))  
                     goto error;  
                 fp->flags |= JSFRAME_ASSIGNING;  
                 ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);  
                 fp->flags &= ~JSFRAME_ASSIGNING;  
                 if (!ok)  
                     goto error;  
                 regs.sp--;  
             }  
   
             if (cs->nuses == 0) {  
                 /* regs.sp[-1] already contains the result of name increment. */  
             } else {  
                 rtmp = regs.sp[-1];  
                 regs.sp -= cs->nuses;  
                 regs.sp[-1] = rtmp;  
             }  
             len = cs->length;  
             DO_NEXT_OP(len);  
           }  
   
           {  
             jsval incr, incr2;  
   
             /* Position cases so the most frequent i++ does not need a jump. */  
           BEGIN_CASE(JSOP_DECARG)  
             incr = -2; incr2 = -2; goto do_arg_incop;  
           BEGIN_CASE(JSOP_ARGDEC)  
             incr = -2; incr2 =  0; goto do_arg_incop;  
           BEGIN_CASE(JSOP_INCARG)  
             incr =  2; incr2 =  2; goto do_arg_incop;  
           BEGIN_CASE(JSOP_ARGINC)  
             incr =  2; incr2 =  0;  
   
           do_arg_incop:  
             slot = GET_ARGNO(regs.pc);  
             JS_ASSERT(slot < fp->fun->nargs);  
             METER_SLOT_OP(op, slot);  
             vp = fp->argv + slot;  
             goto do_int_fast_incop;  
   
           BEGIN_CASE(JSOP_DECLOCAL)  
             incr = -2; incr2 = -2; goto do_local_incop;  
           BEGIN_CASE(JSOP_LOCALDEC)  
             incr = -2; incr2 =  0; goto do_local_incop;  
           BEGIN_CASE(JSOP_INCLOCAL)  
             incr =  2; incr2 =  2; goto do_local_incop;  
           BEGIN_CASE(JSOP_LOCALINC)  
             incr =  2; incr2 =  0;  
   
           /*  
            * do_local_incop comes right before do_int_fast_incop as we want to  
            * avoid an extra jump for variable cases as local++ is more frequent  
            * than arg++.  
            */  
           do_local_incop:  
             slot = GET_SLOTNO(regs.pc);  
             JS_ASSERT(slot < fp->script->nslots);  
             vp = fp->slots + slot;  
             METER_SLOT_OP(op, slot);  
             vp = fp->slots + slot;  
   
           do_int_fast_incop:  
             rval = *vp;  
             if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {  
                 *vp = rval + incr;  
                 JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);  
                 SKIP_POP_AFTER_SET(JSOP_INCARG_LENGTH, 0);  
                 PUSH_OPND(rval + incr2);  
             } else {  
                 PUSH_OPND(rval);  
                 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))  
                     goto error;  
             }  
             len = JSOP_INCARG_LENGTH;  
             JS_ASSERT(len == js_CodeSpec[op].length);  
             DO_NEXT_OP(len);  
           }  
   
 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */  
 #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,INCR,INCR2)                           \  
     op2 = SLOWOP;                                                             \  
     incr = INCR;                                                              \  
     incr2 = INCR2;                                                            \  
     goto do_global_incop  
   
           {  
             jsval incr, incr2;  
   
           BEGIN_CASE(JSOP_DECGVAR)  
             FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, -2, -2);  
           BEGIN_CASE(JSOP_GVARDEC)  
             FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, -2,  0);  
           BEGIN_CASE(JSOP_INCGVAR)  
               FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME,  2,  2);  
           BEGIN_CASE(JSOP_GVARINC)  
             FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC,  2,  0);  
   
 #undef FAST_GLOBAL_INCREMENT_OP  
   
           do_global_incop:  
             JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) ==  
                       JOF_TMPSLOT2);  
             slot = GET_SLOTNO(regs.pc);  
             JS_ASSERT(slot < GlobalVarCount(fp));  
             METER_SLOT_OP(op, slot);  
             lval = fp->slots[slot];  
             if (JSVAL_IS_NULL(lval)) {  
                 op = op2;  
                 DO_OP();  
             }  
             slot = JSVAL_TO_INT(lval);  
             rval = OBJ_GET_SLOT(cx, fp->varobj, slot);  
             if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {  
                 PUSH_OPND(rval + incr2);  
                 rval += incr;  
             } else {  
                 PUSH_OPND(rval);  
                 PUSH_OPND(JSVAL_NULL);  /* Extra root */  
                 if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-2], &regs.sp[-1]))  
                     goto error;  
                 rval = regs.sp[-1];  
                 --regs.sp;  
             }  
             OBJ_SET_SLOT(cx, fp->varobj, slot, rval);  
             len = JSOP_INCGVAR_LENGTH;  /* all gvar incops are same length */  
             JS_ASSERT(len == js_CodeSpec[op].length);  
             DO_NEXT_OP(len);  
           }  
   
 #define COMPUTE_THIS(cx, fp, obj)                                             \  
     JS_BEGIN_MACRO                                                            \  
         if (fp->flags & JSFRAME_COMPUTED_THIS) {                              \  
             obj = fp->thisp;                                                  \  
         } else {                                                              \  
             obj = js_ComputeThis(cx, JS_TRUE, fp->argv);                      \  
             if (!obj)                                                         \  
                 goto error;                                                   \  
             fp->thisp = obj;                                                  \  
             fp->flags |= JSFRAME_COMPUTED_THIS;                               \  
         }                                                                     \  
     JS_END_MACRO  
   
           BEGIN_CASE(JSOP_THIS)  
             COMPUTE_THIS(cx, fp, obj);  
             PUSH_OPND(OBJECT_TO_JSVAL(obj));  
           END_CASE(JSOP_THIS)  
   
           BEGIN_CASE(JSOP_GETTHISPROP)  
             i = 0;  
             COMPUTE_THIS(cx, fp, obj);  
             PUSH(JSVAL_NULL);  
             goto do_getprop_with_obj;  
   
 #undef COMPUTE_THIS  
   
           BEGIN_CASE(JSOP_GETARGPROP)  
             i = ARGNO_LEN;  
             slot = GET_ARGNO(regs.pc);  
             JS_ASSERT(slot < fp->fun->nargs);  
             PUSH_OPND(fp->argv[slot]);  
             goto do_getprop_body;  
   
           BEGIN_CASE(JSOP_GETLOCALPROP)  
             i = SLOTNO_LEN;  
             slot = GET_SLOTNO(regs.pc);  
             JS_ASSERT(slot < script->nslots);  
             PUSH_OPND(fp->slots[slot]);  
             goto do_getprop_body;  
   
           BEGIN_CASE(JSOP_GETPROP)  
           BEGIN_CASE(JSOP_GETXPROP)  
             i = 0;  
   
           do_getprop_body:  
             lval = FETCH_OPND(-1);  
   
           do_getprop_with_lval:  
             VALUE_TO_OBJECT(cx, -1, lval, obj);  
   
           do_getprop_with_obj:  
             do {  
                 JSObject *aobj;  
                 JSPropCacheEntry *entry;  
   
                 aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj;  
                 if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {  
                     PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);  
                     if (!atom) {  
                         ASSERT_VALID_PROPERTY_CACHE_HIT(i, aobj, obj2, entry);  
                         if (PCVAL_IS_OBJECT(entry->vword)) {  
                             rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);  
                         } else if (PCVAL_IS_SLOT(entry->vword)) {  
                             slot = PCVAL_TO_SLOT(entry->vword);  
                             JS_ASSERT(slot < obj2->map->freeslot);  
                             rval = LOCKED_OBJ_GET_SLOT(obj2, slot);  
                         } else {  
                             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));  
                             sprop = PCVAL_TO_SPROP(entry->vword);  
                             NATIVE_GET(cx, obj, obj2, sprop, &rval);  
                         }  
                         JS_UNLOCK_OBJ(cx, obj2);  
                         break;  
                     }  
                 } else {  
                     entry = NULL;  
                     if (i < 0)  
                         atom = rt->atomState.lengthAtom;  
                     else  
                         LOAD_ATOM(i);  
                 }  
                 id = ATOM_TO_JSID(atom);  
                 if (entry  
                     ? !js_GetPropertyHelper(cx, aobj, id, &rval, &entry)  
                     : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {  
                     goto error;  
                 }  
             } while (0);  
   
             STORE_OPND(-1, rval);  
             JS_ASSERT(JSOP_GETPROP_LENGTH + i == js_CodeSpec[op].length);  
             len = JSOP_GETPROP_LENGTH + i;  
           END_VARLEN_CASE  
   
           BEGIN_CASE(JSOP_LENGTH)  
             lval = FETCH_OPND(-1);  
             if (JSVAL_IS_STRING(lval)) {  
                 str = JSVAL_TO_STRING(lval);  
                 regs.sp[-1] = INT_TO_JSVAL(JSSTRING_LENGTH(str));  
             } else if (!JSVAL_IS_PRIMITIVE(lval) &&  
                        (obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) {  
                 jsuint length;  
   
                 /*  
                  * We know that the array is created with only its 'length'  
                  * private data in a fixed slot at JSSLOT_ARRAY_LENGTH. See  
                  * also JSOP_ARRAYPUSH, far below.  
                  */  
                 length = obj->fslots[JSSLOT_ARRAY_LENGTH];  
                 if (length <= JSVAL_INT_MAX) {  
                     regs.sp[-1] = INT_TO_JSVAL(length);  
                 } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length,  
                                                       &regs.sp[-1])) {  
                     goto error;  
                 }  
             } else {  
                 i = -2;  
                 goto do_getprop_with_lval;  
             }  
           END_CASE(JSOP_LENGTH)  
   
           BEGIN_CASE(JSOP_CALLPROP)  
           {  
             JSObject *aobj;  
             JSPropCacheEntry *entry;  
   
             lval = FETCH_OPND(-1);  
             if (!JSVAL_IS_PRIMITIVE(lval)) {  
                 obj = JSVAL_TO_OBJECT(lval);  
             } else {  
                 if (JSVAL_IS_STRING(lval)) {  
                     i = JSProto_String;  
                 } else if (JSVAL_IS_NUMBER(lval)) {  
                     i = JSProto_Number;  
                 } else if (JSVAL_IS_BOOLEAN(lval)) {  
                     i = JSProto_Boolean;  
                 } else {  
                     JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));  
                     js_ReportIsNullOrUndefined(cx, -1, lval, NULL);  
                     goto error;  
                 }  
   
                 if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))  
                     goto error;  
             }  
   
             aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj;  
             if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {  
                 PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);  
                 if (!atom) {  
                     ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);  
                     if (PCVAL_IS_OBJECT(entry->vword)) {  
                         rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);  
                     } else if (PCVAL_IS_SLOT(entry->vword)) {  
                         slot = PCVAL_TO_SLOT(entry->vword);  
                         JS_ASSERT(slot < obj2->map->freeslot);  
                         rval = LOCKED_OBJ_GET_SLOT(obj2, slot);  
                     } else {  
                         JS_ASSERT(PCVAL_IS_SPROP(entry->vword));  
                         sprop = PCVAL_TO_SPROP(entry->vword);  
                         NATIVE_GET(cx, obj, obj2, sprop, &rval);  
                     }  
                     JS_UNLOCK_OBJ(cx, obj2);  
                     STORE_OPND(-1, rval);  
                     PUSH_OPND(lval);  
                     goto end_callprop;  
                 }  
             } else {  
                 entry = NULL;  
                 LOAD_ATOM(0);  
             }  
   
             /*  
              * Cache miss: use the immediate atom that was loaded for us under  
              * PROPERTY_CACHE_TEST.  
              */  
             id = ATOM_TO_JSID(atom);  
             PUSH(JSVAL_NULL);  
             if (!JSVAL_IS_PRIMITIVE(lval)) {  
 #if JS_HAS_XML_SUPPORT  
                 /* Special-case XML object method lookup, per ECMA-357. */  
                 if (OBJECT_IS_XML(cx, obj)) {  
                     JSXMLObjectOps *ops;  
   
                     ops = (JSXMLObjectOps *) obj->map->ops;  
                     obj = ops->getMethod(cx, obj, id, &rval);  
                     if (!obj)  
                         goto error;  
                 } else  
 #endif  
                 if (entry  
                     ? !js_GetPropertyHelper(cx, aobj, id, &rval, &entry)  
                     : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {  
                     goto error;  
                 }  
                 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));  
                 STORE_OPND(-2, rval);  
             } else {  
                 JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);  
                 if (!js_GetPropertyHelper(cx, obj, id, &rval, &entry))  
                     goto error;  
                 STORE_OPND(-1, lval);  
                 STORE_OPND(-2, rval);  
             }  
   
           end_callprop:  
             /* Wrap primitive lval in object clothing if necessary. */  
             if (JSVAL_IS_PRIMITIVE(lval)) {  
                 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */  
                 if (!VALUE_IS_FUNCTION(cx, rval) ||  
                     (obj = JSVAL_TO_OBJECT(rval),  
                      fun = GET_FUNCTION_PRIVATE(cx, obj),  
                      !PRIMITIVE_THIS_TEST(fun, lval))) {  
                     if (!js_PrimitiveToObject(cx, &regs.sp[-1]))  
                         goto error;  
                 }  
             }  
 #if JS_HAS_NO_SUCH_METHOD  
             if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {  
                 LOAD_ATOM(0);  
                 regs.sp[-2] = ATOM_KEY(atom);  
                 if (!js_OnUnknownMethod(cx, regs.sp - 2))  
                     goto error;  
             }  
 #endif  
           }  
           END_CASE(JSOP_CALLPROP)  
   
           BEGIN_CASE(JSOP_SETNAME)  
           BEGIN_CASE(JSOP_SETPROP)  
             rval = FETCH_OPND(-1);  
             lval = FETCH_OPND(-2);  
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval) || op == JSOP_SETPROP);  
             VALUE_TO_OBJECT(cx, -2, lval, obj);  
   
             do {  
                 JSPropCacheEntry *entry;  
   
                 entry = NULL;  
                 atom = NULL;  
                 if (JS_LIKELY(obj->map->ops->setProperty == js_SetProperty)) {  
                     JSPropertyCache *cache = &JS_PROPERTY_CACHE(cx);  
                     uint32 kshape = OBJ_SHAPE(obj);  
   
                     /*  
                      * Open-code JS_PROPERTY_CACHE_TEST, specializing for two  
                      * important set-property cases. First:  
                      *  
                      *   function f(a, b, c) {  
                      *     var o = {p:a, q:b, r:c};  
                      *     return o;  
                      *   }  
                      *  
                      * or similar real-world cases, which evolve a newborn  
                      * native object predicatably through some bounded number  
                      * of property additions. And second:  
                      *  
                      *   o.p = x;  
                      *  
                      * in a frequently executed method or loop body, where p  
                      * will (possibly after the first iteration) always exist  
                      * in native object o.  
                      */  
                     entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];  
                     PCMETER(cache->tests++);  
                     PCMETER(cache->settests++);  
                     if (entry->kpc == regs.pc && entry->kshape == kshape) {  
                         JSScope *scope;  
   
                         JS_LOCK_OBJ(cx, obj);  
                         scope = OBJ_SCOPE(obj);  
                         if (scope->shape == kshape) {  
                             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));  
                             sprop = PCVAL_TO_SPROP(entry->vword);  
                             JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));  
                             JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj)));  
   
                             if (scope->object == obj) {  
                                 /*  
                                  * Fastest path: the cached sprop is already  
                                  * in scope. Just NATIVE_SET and break to get  
                                  * out of the do-while(0).  
                                  */  
                                 if (sprop == scope->lastProp ||  
                                     SCOPE_HAS_PROPERTY(scope, sprop)) {  
                                     PCMETER(cache->pchits++);  
                                     PCMETER(cache->setpchits++);  
                                     NATIVE_SET(cx, obj, sprop, &rval);  
                                     JS_UNLOCK_SCOPE(cx, scope);  
                                     TRACE_2(SetPropHit, entry, sprop);  
                                     break;  
                                 }  
                             } else {  
                                 scope = js_GetMutableScope(cx, obj);  
                                 if (!scope) {  
                                     JS_UNLOCK_OBJ(cx, obj);  
                                     goto error;  
                                 }  
                             }  
   
                             if (sprop->parent == scope->lastProp &&  
                                 !SCOPE_HAD_MIDDLE_DELETE(scope) &&  
                                 SPROP_HAS_STUB_SETTER(sprop) &&  
                                 (slot = sprop->slot) == scope->map.freeslot) {  
                                 /*  
                                  * Fast path: adding a plain old property that  
                                  * was once at the frontier of the property  
                                  * tree, whose slot is next to claim among the  
                                  * allocated slots in obj, where scope->table  
                                  * has not been created yet.  
                                  *  
                                  * We may want to remove hazard conditions  
                                  * above and inline compensation code here,  
                                  * depending on real-world workloads.  
                                  */  
                                 JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags &  
                                             JSCLASS_SHARE_ALL_PROPERTIES));  
   
                                 PCMETER(cache->pchits++);  
                                 PCMETER(cache->addpchits++);  
   
                                 /*  
                                  * Beware classes such as Function that use  
                                  * the reserveSlots hook to allocate a number  
                                  * of reserved slots that may vary with obj.  
                                  */  
                                 if (slot < STOBJ_NSLOTS(obj) &&  
                                     !OBJ_GET_CLASS(cx, obj)->reserveSlots) {  
                                     ++scope->map.freeslot;  
                                 } else {  
                                     if (!js_AllocSlot(cx, obj, &slot)) {  
                                         JS_UNLOCK_SCOPE(cx, scope);  
                                         goto error;  
                                     }  
                                 }  
   
                                 /*  
                                  * If this obj's number of reserved slots  
                                  * differed, or if something created a hash  
                                  * table for scope, we must pay the price of  
                                  * js_AddScopeProperty.  
                                  *  
                                  * If slot does not match the cached sprop's  
                                  * slot, update the cache entry in the hope  
                                  * that obj and other instances with the same  
                                  * number of reserved slots are now "hot".  
                                  */  
                                 if (slot != sprop->slot || scope->table) {  
                                     JSScopeProperty *sprop2 =  
                                         js_AddScopeProperty(cx, scope,  
                                                             sprop->id,  
                                                             sprop->getter,  
                                                             sprop->setter,  
                                                             slot,  
                                                             sprop->attrs,  
                                                             sprop->flags,  
                                                             sprop->shortid);  
                                     if (!sprop2) {  
                                         js_FreeSlot(cx, obj, slot);  
                                         JS_UNLOCK_SCOPE(cx, scope);  
                                         goto error;  
                                     }  
                                     if (sprop2 != sprop) {  
                                         PCMETER(cache->slotchanges++);  
                                         JS_ASSERT(slot != sprop->slot &&  
                                                   slot == sprop2->slot &&  
                                                   sprop2->id == sprop->id);  
                                         entry->vword = SPROP_TO_PCVAL(sprop2);  
                                     }  
                                     sprop = sprop2;  
                                 } else {  
                                     SCOPE_EXTEND_SHAPE(cx, scope, sprop);  
                                     ++scope->entryCount;  
                                     scope->lastProp = sprop;  
                                 }  
   
                                 GC_WRITE_BARRIER(cx, scope,  
                                                  LOCKED_OBJ_GET_SLOT(obj, slot),  
                                                  rval);  
                                 LOCKED_OBJ_SET_SLOT(obj, slot, rval);  
                                 JS_UNLOCK_SCOPE(cx, scope);  
                                 TRACE_2(SetPropHit, entry, sprop);  
                                 break;  
                             }  
   
                             PCMETER(cache->setpcmisses++);  
                             atom = NULL;  
                         }  
   
                         JS_UNLOCK_OBJ(cx, obj);  
                     }  
   
                     atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,  
                                                     &entry);  
                     if (atom) {  
                         PCMETER(cache->misses++);  
                         PCMETER(cache->setmisses++);  
                     } else {  
                         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);  
                         sprop = NULL;  
                         if (obj == obj2) {  
                             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));  
                             sprop = PCVAL_TO_SPROP(entry->vword);  
                             JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));  
                             JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2)));  
                             NATIVE_SET(cx, obj, sprop, &rval);  
                         }  
                         JS_UNLOCK_OBJ(cx, obj2);  
                         if (sprop) {  
                             TRACE_2(SetPropHit, entry, sprop);  
                             break;  
                         }  
                     }  
                 }  
   
                 if (!atom)  
                     LOAD_ATOM(0);  
                 id = ATOM_TO_JSID(atom);  
                 if (entry) {  
                     if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry))  
                         goto error;  
 #ifdef JS_TRACER  
                     if (entry)  
                         TRACE_1(SetPropMiss, entry);  
 #endif  
                 } else {  
                     if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))  
                         goto error;  
                 }  
 #ifdef JS_TRACER  
                 if (!entry && TRACE_RECORDER(cx)) {  
                     js_AbortRecording(cx, NULL, "SetPropUncached");  
                     ENABLE_TRACER(0);  
                 }  
 #endif  
             } while (0);  
           END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);  
   
           BEGIN_CASE(JSOP_GETELEM)  
             /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */  
             lval = FETCH_OPND(-2);  
             rval = FETCH_OPND(-1);  
             if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) {  
                 str = JSVAL_TO_STRING(lval);  
                 i = JSVAL_TO_INT(rval);  
                 if ((size_t)i < JSSTRING_LENGTH(str)) {  
                     str = js_GetUnitString(cx, str, (size_t)i);  
                     if (!str)  
                         goto error;  
                     rval = STRING_TO_JSVAL(str);  
                     goto end_getelem;  
                 }  
             }  
   
             VALUE_TO_OBJECT(cx, -2, lval, obj);  
             if (JSVAL_IS_INT(rval)) {  
                 if (OBJ_IS_DENSE_ARRAY(cx, obj)) {  
                     jsuint length;  
   
                     length = ARRAY_DENSE_LENGTH(obj);  
                     i = JSVAL_TO_INT(rval);  
                     if ((jsuint)i < length &&  
                         i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {  
                         rval = obj->dslots[i];  
                         if (rval != JSVAL_HOLE)  
                             goto end_getelem;  
   
                         /* Reload rval from the stack in the rare hole case. */  
                         rval = FETCH_OPND(-1);  
                     }  
                 }  
                 id = INT_JSVAL_TO_JSID(rval);  
             } else {  
                 if (!js_InternNonIntElementId(cx, obj, rval, &id))  
                     goto error;  
             }  
   
             if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))  
                 goto error;  
           end_getelem:  
             regs.sp--;  
             STORE_OPND(-1, rval);  
           END_CASE(JSOP_GETELEM)  
   
           BEGIN_CASE(JSOP_CALLELEM)  
             /*  
              * FIXME: JSOP_CALLELEM should call getMethod on XML objects as  
              * CALLPROP does. See bug 362910.  
              */  
             ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval));  
 #if JS_HAS_NO_SUCH_METHOD  
             if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {  
                 regs.sp[-2] = regs.sp[-1];  
                 regs.sp[-1] = OBJECT_TO_JSVAL(obj);  
                 if (!js_OnUnknownMethod(cx, regs.sp - 2))  
                     goto error;  
             } else  
 #endif  
             {  
                 STORE_OPND(-2, rval);  
                 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));  
             }  
           END_CASE(JSOP_CALLELEM)  
   
           BEGIN_CASE(JSOP_SETELEM)  
             rval = FETCH_OPND(-1);  
             FETCH_OBJECT(cx, -3, lval, obj);  
             FETCH_ELEMENT_ID(obj, -2, id);  
             if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {  
                 jsuint length;  
   
                 length = ARRAY_DENSE_LENGTH(obj);  
                 i = JSID_TO_INT(id);  
                 if ((jsuint)i < length) {  
                     if (obj->dslots[i] == JSVAL_HOLE) {  
                         if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])  
                             obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;  
                         obj->fslots[JSSLOT_ARRAY_COUNT]++;  
                     }  
                     obj->dslots[i] = rval;  
                     goto end_setelem;  
                 }  
             }  
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))  
                 goto error;  
         end_setelem:  
           END_SET_CASE_STORE_RVAL(JSOP_SETELEM, 3)  
   
           BEGIN_CASE(JSOP_ENUMELEM)  
             /* Funky: the value to set is under the [obj, id] pair. */  
             rval = FETCH_OPND(-3);  
             FETCH_OBJECT(cx, -2, lval, obj);  
             FETCH_ELEMENT_ID(obj, -1, id);  
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))  
                 goto error;  
             regs.sp -= 3;  
           END_CASE(JSOP_ENUMELEM)  
   
           BEGIN_CASE(JSOP_NEW)  
             /* Get immediate argc and find the constructor function. */  
             argc = GET_ARGC(regs.pc);  
             vp = regs.sp - (2 + argc);  
             JS_ASSERT(vp >= StackBase(fp));  
   
             /*  
              * Assign lval, obj, and fun exactly as the code at inline_call:  
              * expects to find them, to avoid nesting a js_Interpret call via  
              * js_InvokeConstructor.  
              */  
             lval = *vp;  
             if (VALUE_IS_FUNCTION(cx, lval)) {  
                 obj = JSVAL_TO_OBJECT(lval);  
                 fun = GET_FUNCTION_PRIVATE(cx, obj);  
                 if (FUN_INTERPRETED(fun)) {  
                     /* Root as we go using vp[1]. */  
                     if (!OBJ_GET_PROPERTY(cx, obj,  
                                           ATOM_TO_JSID(cx->runtime->atomState  
                                                        .classPrototypeAtom),  
                                           &vp[1])) {  
                         goto error;  
                     }  
                     rval = vp[1];  
                     obj2 = js_NewObject(cx, &js_ObjectClass,  
                                         JSVAL_IS_OBJECT(rval)  
                                         ? JSVAL_TO_OBJECT(rval)  
                                         : NULL,  
                                         OBJ_GET_PARENT(cx, obj),  
                                         0);  
                     if (!obj2)  
                         goto error;  
                     vp[1] = OBJECT_TO_JSVAL(obj2);  
                     flags = JSFRAME_CONSTRUCTING;  
                     goto inline_call;  
                 }  
             }  
   
             if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))  
                 goto error;  
             regs.sp = vp + 1;  
             LOAD_INTERRUPT_HANDLER(cx);  
             JS_ASSERT(regs.pc[JSOP_NEW_LENGTH] == JSOP_RESUME);  
             len = JSOP_NEW_LENGTH + JSOP_RESUME_LENGTH;  
           END_VARLEN_CASE  
   
           BEGIN_CASE(JSOP_CALL)  
           BEGIN_CASE(JSOP_EVAL)  
             argc = GET_ARGC(regs.pc);  
             vp = regs.sp - (argc + 2);  
             lval = *vp;  
             if (VALUE_IS_FUNCTION(cx, lval)) {  
                 obj = JSVAL_TO_OBJECT(lval);  
                 fun = GET_FUNCTION_PRIVATE(cx, obj);  
   
                 /* Clear frame flags since this is not a constructor call. */  
                 flags = 0;  
                 if (FUN_INTERPRETED(fun))  
               inline_call:  
                 {  
                     uintN nframeslots, nvars, missing;  
                     JSArena *a;  
                     jsuword nbytes;  
                     void *newmark;  
                     jsval *newsp;  
                     JSInlineFrame *newifp;  
                     JSInterpreterHook hook;  
   
                     /* Restrict recur