/[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 399 by siliconforks, Tue Dec 9 03:37:47 2008 UTC revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 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 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 85  Line 86 
86  #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___  #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
87    
88  uint32  uint32
89  js_GenerateShape(JSContext *cx, JSBool gcLocked, JSScopeProperty *sprop)  js_GenerateShape(JSContext *cx, JSBool gcLocked)
90  {  {
91      JSRuntime *rt;      JSRuntime *rt;
92      uint32 shape;      uint32 shape;
     JSTempValueRooter tvr;  
93    
94      rt = cx->runtime;      rt = cx->runtime;
95      shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);      shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
96      JS_ASSERT(shape != 0);      JS_ASSERT(shape != 0);
97      if (shape & SHAPE_OVERFLOW_BIT) {      if (shape >= SHAPE_OVERFLOW_BIT) {
98          rt->gcPoke = JS_TRUE;          /*
99          if (sprop)           * FIXME bug 440834: The shape id space has overflowed. Currently we
100              JS_PUSH_TEMP_ROOT_SPROP(cx, sprop, &tvr);           * cope badly with this and schedule the GC on the every call. But
101          js_GC(cx, gcLocked ? GC_LOCK_HELD : GC_NORMAL);           * first we make sure that increments from other threads would not
102          if (sprop)           * have a chance to wrap around shapeGen to zero.
103              JS_POP_TEMP_ROOT(cx, &tvr);           */
104          shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);          rt->shapeGen = SHAPE_OVERFLOW_BIT;
105          JS_ASSERT(shape != 0);          js_TriggerGC(cx, gcLocked);
         JS_ASSERT_IF(shape & SHAPE_OVERFLOW_BIT,  
                      JS_PROPERTY_CACHE(cx).disabled);  
106      }      }
107      return shape;      return shape;
108  }  }
109    
110  void  JS_REQUIRES_STACK JSPropCacheEntry *
111  js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,  js_FillPropertyCache(JSContext *cx, JSObject *obj,
112                       uintN scopeIndex, uintN protoIndex,                       uintN scopeIndex, uintN protoIndex, JSObject *pobj,
113                       JSObject *pobj, JSScopeProperty *sprop,                       JSScopeProperty *sprop, JSBool adding)
                      JSPropCacheEntry **entryp)  
114  {  {
115      JSPropertyCache *cache;      JSPropertyCache *cache;
116      jsbytecode *pc;      jsbytecode *pc;
117      JSScope *scope;      JSScope *scope;
118        jsuword kshape, vshape, khash;
119      JSOp op;      JSOp op;
120      const JSCodeSpec *cs;      const JSCodeSpec *cs;
121      jsuword vword;      jsuword vword;
122      ptrdiff_t pcoff;      ptrdiff_t pcoff;
     jsuword khash;  
123      JSAtom *atom;      JSAtom *atom;
124      JSPropCacheEntry *entry;      JSPropCacheEntry *entry;
125    
126      JS_ASSERT(!cx->runtime->gcRunning);      JS_ASSERT(!cx->runtime->gcRunning);
127      cache = &JS_PROPERTY_CACHE(cx);      cache = &JS_PROPERTY_CACHE(cx);
128      pc = cx->fp->regs->pc;  
129      if (cache->disabled || (cx->fp->flags & JSFRAME_EVAL)) {      /* FIXME bug 489098: consider enabling the property cache for eval. */
130        if (js_IsPropertyCacheDisabled(cx) || (cx->fp->flags & JSFRAME_EVAL)) {
131          PCMETER(cache->disfills++);          PCMETER(cache->disfills++);
132          *entryp = NULL;          return JS_NO_PROP_CACHE_FILL;
         return;  
133      }      }
134    
135      /*      /*
# Line 143  Line 140 
140      JS_ASSERT(scope->object == pobj);      JS_ASSERT(scope->object == pobj);
141      if (!SCOPE_HAS_PROPERTY(scope, sprop)) {      if (!SCOPE_HAS_PROPERTY(scope, sprop)) {
142          PCMETER(cache->oddfills++);          PCMETER(cache->oddfills++);
143          *entryp = NULL;          return JS_NO_PROP_CACHE_FILL;
         return;  
144      }      }
145    
146      /*      /*
# Line 161  Line 157 
157       * but vcap vs. scope shape tests ensure nothing malfunctions.       * but vcap vs. scope shape tests ensure nothing malfunctions.
158       */       */
159      JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);      JS_ASSERT_IF(scopeIndex == 0 && protoIndex == 0, obj == pobj);
160    
161      if (protoIndex != 0) {      if (protoIndex != 0) {
162          JSObject *tmp;          JSObject *tmp = obj;
163    
164            for (uintN i = 0; i != scopeIndex; i++)
165                tmp = OBJ_GET_PARENT(cx, tmp);
166            JS_ASSERT(tmp != pobj);
167    
         JS_ASSERT(pobj != obj);  
168          protoIndex = 1;          protoIndex = 1;
         tmp = obj;  
169          for (;;) {          for (;;) {
170              tmp = OBJ_GET_PROTO(cx, tmp);              tmp = OBJ_GET_PROTO(cx, tmp);
171              if (!tmp) {  
172                /*
173                 * We cannot cache properties coming from native objects behind
174                 * non-native ones on the prototype chain. The non-natives can
175                 * mutate in arbitrary way without changing any shapes.
176                 */
177                if (!tmp || !OBJ_IS_NATIVE(tmp)) {
178                  PCMETER(cache->noprotos++);                  PCMETER(cache->noprotos++);
179                  *entryp = NULL;                  return JS_NO_PROP_CACHE_FILL;
                 return;  
180              }              }
181              if (tmp == pobj)              if (tmp == pobj)
182                  break;                  break;
183              ++protoIndex;              ++protoIndex;
184          }          }
185      }      }
186    
187      if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {      if (scopeIndex > PCVCAP_SCOPEMASK || protoIndex > PCVCAP_PROTOMASK) {
188          PCMETER(cache->longchains++);          PCMETER(cache->longchains++);
189          *entryp = NULL;          return JS_NO_PROP_CACHE_FILL;
         return;  
190      }      }
191    
192      /*      /*
193       * 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
194       * opcode format flags.       * opcode format flags.
195       */       */
196      op = (JSOp) *pc;      pc = cx->fp->regs->pc;
197        op = js_GetOpcode(cx, cx->fp->script, pc);
198      cs = &js_CodeSpec[op];      cs = &js_CodeSpec[op];
199        kshape = 0;
200    
201      do {      do {
202          /*          /*
# Line 214  Line 220 
220                       *                       *
221                       * So here, on first cache fill for this method, we brand                       * So here, on first cache fill for this method, we brand
222                       * the scope with a new shape and set the SCOPE_BRANDED                       * the scope with a new shape and set the SCOPE_BRANDED
223                       * flag.  Once this scope flag is set, any write that adds                       * flag. Once this scope flag is set, any write that adds
224                       * or deletes a function-valued plain old property in                       * or deletes a function-valued plain old property in
225                       * scope->object will result in shape being regenerated.                       * scope->object will result in shape being regenerated.
226                       */                       */
# Line 222  Line 228 
228                          PCMETER(cache->brandfills++);                          PCMETER(cache->brandfills++);
229  #ifdef DEBUG_notme  #ifdef DEBUG_notme
230                          fprintf(stderr,                          fprintf(stderr,
231                              "branding %p (%s) for funobj %p (%s), kshape %lu\n",                              "branding %p (%s) for funobj %p (%s), shape %lu\n",
232                              pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,                              pobj, LOCKED_OBJ_GET_CLASS(pobj)->name,
233                              JSVAL_TO_OBJECT(v),                              JSVAL_TO_OBJECT(v),
234                              JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,                              JS_GetFunctionName(GET_FUNCTION_PRIVATE(cx,
235                                                   JSVAL_TO_OBJECT(v))),                                                   JSVAL_TO_OBJECT(v))),
236                              kshape);                              OBJ_SHAPE(obj));
237  #endif  #endif
238                          SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);                          js_MakeScopeShapeUnique(cx, scope);
239                            if (js_IsPropertyCacheDisabled(cx)) {
240                                /*
241                                 * js_GenerateShape could not recover from
242                                 * rt->shapeGen's overflow.
243                                 */
244                                return JS_NO_PROP_CACHE_FILL;
245                            }
246                          SCOPE_SET_BRANDED(scope);                          SCOPE_SET_BRANDED(scope);
                         kshape = scope->shape;  
247                      }                      }
248                      vword = JSVAL_OBJECT_TO_PCVAL(v);                      vword = JSVAL_OBJECT_TO_PCVAL(v);
249                      break;                      break;
# Line 240  Line 252 
252          }          }
253    
254          /* 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. */
255          if (!(cs->format & JOF_SET) &&          if (!(cs->format & (JOF_SET | JOF_INCDEC | JOF_FOR)) &&
256              SPROP_HAS_STUB_GETTER(sprop) &&              SPROP_HAS_STUB_GETTER(sprop) &&
257              SPROP_HAS_VALID_SLOT(sprop, scope)) {              SPROP_HAS_VALID_SLOT(sprop, scope)) {
258              /* 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 248  Line 260 
260          } else {          } else {
261              /* Best we can do is to cache sprop (still a nice speedup). */              /* Best we can do is to cache sprop (still a nice speedup). */
262              vword = SPROP_TO_PCVAL(sprop);              vword = SPROP_TO_PCVAL(sprop);
263                if (adding &&
264                    sprop == scope->lastProp &&
265                    scope->shape == sprop->shape) {
266                    /*
267                     * Our caller added a new property. We also know that a setter
268                     * that js_NativeSet could have run has not mutated the scope
269                     * so the added property is still the last one added and the
270                     * scope is not branded.
271                     *
272                     * We want to cache under scope's shape before the property
273                     * addition to bias for the case when the mutator opcode
274                     * always adds the same property. It allows to optimize
275                     * periodic execution of object initializers or explicit
276                     * initialization sequences like
277                     *
278                     *   obj = {}; obj.x = 1; obj.y = 2;
279                     *
280                     * We assume that on average the win from this optimization is
281                     * bigger that the cost of an extra mismatch per loop due to
282                     * the bias for the following case:
283                     *
284                     *   obj = {}; ... for (...) { ... obj.x = ... }
285                     *
286                     * On the first iteration JSOP_SETPROP fills the cache with
287                     * the shape of newly created object, not the shape after
288                     * obj.x is assigned. That mismatches obj's shape on the
289                     * second iteration. Note that on third and the following
290                     * iterations the cache will be hit since the shape no longer
291                     * mutates.
292                     */
293                    JS_ASSERT(scope->object == obj);
294                    if (sprop->parent) {
295                        kshape = sprop->parent->shape;
296                    } else {
297                        JSObject *proto = STOBJ_GET_PROTO(obj);
298                        if (proto && OBJ_IS_NATIVE(proto))
299                            kshape = OBJ_SHAPE(proto);
300                    }
301    
302                    /*
303                     * When adding we predict no prototype object will later gain a
304                     * readonly property or setter.
305                     */
306                    vshape = cx->runtime->protoHazardShape;
307                }
308          }          }
309      } while (0);      } while (0);
310    
311      /*      if (kshape == 0) {
312       * Our caller preserved the scope shape prior to the js_GetPropertyHelper          kshape = OBJ_SHAPE(obj);
313       * or similar call out of the interpreter. We want to cache under that          vshape = scope->shape;
314       * 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;  
315    
316      khash = PROPERTY_CACHE_HASH_PC(pc, kshape);      khash = PROPERTY_CACHE_HASH_PC(pc, kshape);
317      if (obj == pobj) {      if (obj == pobj) {
         JS_ASSERT(kshape != 0 || scope->shape != 0);  
318          JS_ASSERT(scopeIndex == 0 && protoIndex == 0);          JS_ASSERT(scopeIndex == 0 && protoIndex == 0);
319          JS_ASSERT(OBJ_SCOPE(obj)->object == obj);          JS_ASSERT(OBJ_SCOPE(obj)->object == obj);
320            JS_ASSERT(kshape != 0);
321      } else {      } else {
322          if (op == JSOP_LENGTH) {          if (op == JSOP_LENGTH) {
323              atom = cx->runtime->atomState.lengthAtom;              atom = cx->runtime->atomState.lengthAtom;
324          } else {          } else {
325              pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;              pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
326              GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);              GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
327          }          }
328          JS_ASSERT_IF(scopeIndex == 0,  
329                       protoIndex != 1 || OBJ_GET_PROTO(cx, obj) == pobj);  #ifdef DEBUG
330            if (scopeIndex == 0) {
331                JS_ASSERT(protoIndex != 0);
332                JS_ASSERT((protoIndex == 1) == (OBJ_GET_PROTO(cx, obj) == pobj));
333            }
334    #endif
335    
336          if (scopeIndex != 0 || protoIndex != 1) {          if (scopeIndex != 0 || protoIndex != 1) {
337              khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);              khash = PROPERTY_CACHE_HASH_ATOM(atom, obj, pobj);
338              PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)              PCMETER(if (PCVCAP_TAG(cache->table[khash].vcap) <= 1)
339                          cache->pcrecycles++);                          cache->pcrecycles++);
340              pc = (jsbytecode *) atom;              pc = (jsbytecode *) atom;
341              kshape = (jsuword) obj;              kshape = (jsuword) obj;
342    
343                /*
344                 * Make sure that a later shadowing assignment will enter
345                 * PurgeProtoChain and invalidate this entry, bug 479198.
346                 *
347                 * This is thread-safe even though obj is not locked. Only the
348                 * DELEGATE bit of obj->classword can change at runtime, given that
349                 * obj is native; and the bit is only set, never cleared. And on
350                 * platforms where another CPU can fail to see this write, it's OK
351                 * because the property cache and JIT cache are thread-local.
352                 */
353                OBJ_SET_DELEGATE(cx, obj);
354          }          }
355      }      }
356    
357      entry = &cache->table[khash];      entry = &cache->table[khash];
358      PCMETER(if (entry != *entryp) cache->modfills++);      PCMETER(PCVAL_IS_NULL(entry->vword) || cache->recycles++);
     PCMETER(if (!PCVAL_IS_NULL(entry->vword)) cache->recycles++);  
359      entry->kpc = pc;      entry->kpc = pc;
360      entry->kshape = kshape;      entry->kshape = kshape;
361      entry->vcap = PCVCAP_MAKE(scope->shape, scopeIndex, protoIndex);      entry->vcap = PCVCAP_MAKE(vshape, scopeIndex, protoIndex);
362      entry->vword = vword;      entry->vword = vword;
     *entryp = entry;  
363    
364      cache->empty = JS_FALSE;      cache->empty = JS_FALSE;
365      PCMETER(cache->fills++);      PCMETER(cache->fills++);
366    
367        /*
368         * The modfills counter is not exact. It increases if a getter or setter
369         * recurse into the interpreter.
370         */
371        PCMETER(entry == cache->pctestentry || cache->modfills++);
372        PCMETER(cache->pctestentry = NULL);
373        return entry;
374  }  }
375    
376  JSAtom *  JS_REQUIRES_STACK JSAtom *
377  js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,  js_FullTestPropertyCache(JSContext *cx, jsbytecode *pc,
378                           JSObject **objp, JSObject **pobjp,                           JSObject **objp, JSObject **pobjp,
379                           JSPropCacheEntry **entryp)                           JSPropCacheEntry **entryp)
# Line 317  Line 389 
389      JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)      JS_ASSERT(uintN((cx->fp->imacpc ? cx->fp->imacpc : pc) - cx->fp->script->code)
390                < cx->fp->script->length);                < cx->fp->script->length);
391    
392      op = (JSOp) *pc;      op = js_GetOpcode(cx, cx->fp->script, pc);
393      cs = &js_CodeSpec[op];      cs = &js_CodeSpec[op];
394      if (op == JSOP_LENGTH) {      if (op == JSOP_LENGTH) {
395          atom = cx->runtime->atomState.lengthAtom;          atom = cx->runtime->atomState.lengthAtom;
396      } else {      } else {
397          pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? 2 : 0;          pcoff = (JOF_TYPE(cs->format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
398          GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);          GET_ATOM_FROM_BYTECODE(cx->fp->script, pc, pcoff, atom);
399      }      }
400    
# Line 424  Line 496 
496  JS_STATIC_ASSERT(PCVAL_NULL == 0);  JS_STATIC_ASSERT(PCVAL_NULL == 0);
497    
498  void  void
499  js_FlushPropertyCache(JSContext *cx)  js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache)
500  {  {
     JSPropertyCache *cache;  
   
     cache = &JS_PROPERTY_CACHE(cx);  
501      if (cache->empty) {      if (cache->empty) {
502          ASSERT_CACHE_IS_EMPTY(cache);          ASSERT_CACHE_IS_EMPTY(cache);
503          return;          return;
# Line 496  Line 565 
565  }  }
566    
567  void  void
568  js_FlushPropertyCacheForScript(JSContext *cx, JSScript *script)  js_PurgePropertyCacheForScript(JSContext *cx, JSScript *script)
569  {  {
570      JSPropertyCache *cache;      JSPropertyCache *cache;
571      JSPropCacheEntry *entry;      JSPropCacheEntry *entry;
# Line 514  Line 583 
583      }      }
584  }  }
585    
 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);  
 }  
   
586  /*  /*
587   * 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
588   * so, reserve the necessary space.   * so, reserve the necessary space.
589   */   */
590  static JSBool  static JS_REQUIRES_STACK JSBool
591  AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)  AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
592  {  {
593      uintN surplus;      uintN surplus;
# Line 557  Line 612 
612      return JS_TRUE;      return JS_TRUE;
613  }  }
614    
615  JS_STATIC_INTERPRET jsval *  JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval *
616  js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)  js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
617  {  {
618      jsval *sp;      jsval *sp;
619    
620        JS_ASSERT(nslots != 0);
621        js_LeaveTrace(cx);
622    
623      if (!cx->stackPool.first.next) {      if (!cx->stackPool.first.next) {
624          int64 *timestamp;          int64 *timestamp;
625    
# Line 582  Line 640 
640      return sp;      return sp;
641  }  }
642    
643  JS_STATIC_INTERPRET void  JS_STATIC_INTERPRET JS_REQUIRES_STACK void
644  js_FreeRawStack(JSContext *cx, void *mark)  js_FreeRawStack(JSContext *cx, void *mark)
645  {  {
646      JS_ARENA_RELEASE(&cx->stackPool, mark);      JS_ARENA_RELEASE(&cx->stackPool, mark);
647  }  }
648    
649  JS_FRIEND_API(jsval *)  JS_REQUIRES_STACK JS_FRIEND_API(jsval *)
650  js_AllocStack(JSContext *cx, uintN nslots, void **markp)  js_AllocStack(JSContext *cx, uintN nslots, void **markp)
651  {  {
652      jsval *sp;      jsval *sp;
# Line 634  Line 692 
692      return sp;      return sp;
693  }  }
694    
695  JS_FRIEND_API(void)  JS_REQUIRES_STACK JS_FRIEND_API(void)
696  js_FreeStack(JSContext *cx, void *mark)  js_FreeStack(JSContext *cx, void *mark)
697  {  {
698      JSStackHeader *sh;      JSStackHeader *sh;
# Line 662  Line 720 
720  JSObject *  JSObject *
721  js_GetScopeChain(JSContext *cx, JSStackFrame *fp)  js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
722  {  {
723      JSObject *obj, *cursor, *clonedChild, *parent;      JSObject *sharedBlock = fp->blockChain;
     JSTempValueRooter tvr;  
724    
725      obj = fp->blockChain;      if (!sharedBlock) {
     if (!obj) {  
726          /*          /*
727           * 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
728           * 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 678  Line 734 
734          return fp->scopeChain;          return fp->scopeChain;
735      }      }
736    
737        /* We don't handle cloning blocks on trace.  */
738        js_LeaveTrace(cx);
739    
740      /*      /*
741       * 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
742       * 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,
743       * if this frame is a call frame.       * if this frame is a call frame.
744         *
745         * Also, identify the innermost compiler-allocated block we needn't clone.
746       */       */
747        JSObject *limitBlock, *limitClone;
748      if (fp->fun && !fp->callobj) {      if (fp->fun && !fp->callobj) {
749          JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||          JS_ASSERT(OBJ_GET_CLASS(cx, fp->scopeChain) != &js_BlockClass ||
750                    OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);                    OBJ_GET_PRIVATE(cx, fp->scopeChain) != fp);
751          if (!js_GetCallObject(cx, fp, fp->scopeChain))          if (!js_GetCallObject(cx, fp))
752              return NULL;              return NULL;
753    
754            /* We know we must clone everything on blockChain. */
755            limitBlock = limitClone = NULL;
756        } else {
757            /*
758             * scopeChain includes all blocks whose static scope we're within that
759             * have already been cloned.  Find the innermost such block.  Its
760             * prototype should appear on blockChain; we'll clone blockChain up
761             * to, but not including, that prototype.
762             */
763            limitClone = fp->scopeChain;
764            while (OBJ_GET_CLASS(cx, limitClone) == &js_WithClass)
765                limitClone = OBJ_GET_PARENT(cx, limitClone);
766            JS_ASSERT(limitClone);
767    
768            /*
769             * It may seem like we don't know enough about limitClone to be able
770             * to just grab its prototype as we do here, but it's actually okay.
771             *
772             * If limitClone is a block object belonging to this frame, then its
773             * prototype is the innermost entry in blockChain that we have already
774             * cloned, and is thus the place to stop when we clone below.
775             *
776             * Otherwise, there are no blocks for this frame on scopeChain, and we
777             * need to clone the whole blockChain.  In this case, limitBlock can
778             * point to any object known not to be on blockChain, since we simply
779             * loop until we hit limitBlock or NULL.  If limitClone is a block, it
780             * isn't a block from this function, since blocks can't be nested
781             * within themselves on scopeChain (recursion is dynamic nesting, not
782             * static nesting).  If limitClone isn't a block, its prototype won't
783             * be a block either.  So we can just grab limitClone's prototype here
784             * regardless of its type or which frame it belongs to.
785             */
786            limitBlock = OBJ_GET_PROTO(cx, limitClone);
787    
788            /* If the innermost block has already been cloned, we are done. */
789            if (limitBlock == sharedBlock)
790                return fp->scopeChain;
791      }      }
792    
793      /*      /*
794       * 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
795       * the cloned child after we clone the parent. In the following loop when       * common with subsequent steps to include in the loop.
796       * clonedChild is null it indicates the first iteration when no special GC       *
797       * rooting is necessary. On the second and the following iterations we       * We pass fp->scopeChain and not null even if we override the parent slot
798       * have to protect cloned so far chain against the GC during cloning of       * later as null triggers useless calculations of slot's value in
799       * the cursor object.       * js_NewObject that js_CloneBlockObject calls.
800       */       */
801      cursor = obj;      JSObject *innermostNewChild
802      clonedChild = NULL;          = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp);
803        if (!innermostNewChild)
804            return NULL;
805        JSAutoTempValueRooter tvr(cx, innermostNewChild);
806    
807        /*
808         * Clone our way towards outer scopes until we reach the innermost
809         * enclosing function, or the innermost block we've already cloned.
810         */
811        JSObject *newChild = innermostNewChild;
812      for (;;) {      for (;;) {
813          parent = OBJ_GET_PARENT(cx, cursor);          JS_ASSERT(OBJ_GET_PROTO(cx, newChild) == sharedBlock);
814            sharedBlock = OBJ_GET_PARENT(cx, sharedBlock);
815    
816            /* Sometimes limitBlock will be NULL, so check that first.  */
817            if (sharedBlock == limitBlock || !sharedBlock)
818                break;
819    
820            /* As in the call above, we don't know the real parent yet.  */
821            JSObject *clone
822                = js_CloneBlockObject(cx, sharedBlock, fp->scopeChain, fp);
823            if (!clone)
824                return NULL;
825    
826          /*          /*
827           * We pass fp->scopeChain and not null even if we override the parent           * Avoid OBJ_SET_PARENT overhead as newChild cannot escape to
828           * slot later as null triggers useless calculations of slot's value in           * other threads.
          * js_NewObject that js_CloneBlockObject calls.  
829           */           */
830          cursor = js_CloneBlockObject(cx, cursor, fp->scopeChain, fp);          STOBJ_SET_PARENT(newChild, clone);
831          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;  
832      }      }
833      fp->flags |= JSFRAME_POP_BLOCKS;  
834      fp->scopeChain = obj;      /*
835      fp->blockChain = NULL;       * If we found a limit block belonging to this frame, then we should have
836      return obj;       * found it in blockChain.
837         */
838        JS_ASSERT_IF(limitBlock &&
839                     OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
840                     OBJ_GET_PRIVATE(cx, limitClone) == fp,
841                     sharedBlock);
842    
843        /* Place our newly cloned blocks at the head of the scope chain.  */
844        fp->scopeChain = innermostNewChild;
845        return fp->scopeChain;
846  }  }
847    
848  JSBool  JSBool
# Line 802  Line 903 
903           * imposes a performance penalty on all js_ComputeGlobalThis calls,           * imposes a performance penalty on all js_ComputeGlobalThis calls,
904           * and it represents a maintenance hazard.           * and it represents a maintenance hazard.
905           */           */
906          fp = cx->fp;    /* quell GCC overwarning */          fp = js_GetTopStackFrame(cx);    /* quell GCC overwarning */
907          if (lazy) {          if (lazy) {
908              JS_ASSERT(fp->argv == argv);              JS_ASSERT(fp->argv == argv);
909              fp->dormantNext = cx->dormantFrameChain;              fp->dormantNext = cx->dormantFrameChain;
# Line 830  Line 931 
931              thisp = parent;              thisp = parent;
932      }      }
933    
934      OBJ_TO_OUTER_OBJECT(cx, thisp);      /* Some objects (e.g., With) delegate 'this' to another object. */
935        thisp = OBJ_THIS_OBJECT(cx, thisp);
936      if (!thisp)      if (!thisp)
937          return NULL;          return NULL;
938      argv[-1] = OBJECT_TO_JSVAL(thisp);      argv[-1] = OBJECT_TO_JSVAL(thisp);
# Line 849  Line 951 
951          thisp = JSVAL_TO_OBJECT(argv[-1]);          thisp = JSVAL_TO_OBJECT(argv[-1]);
952      } else {      } else {
953          thisp = JSVAL_TO_OBJECT(argv[-1]);          thisp = JSVAL_TO_OBJECT(argv[-1]);
954          if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass)          if (OBJ_GET_CLASS(cx, thisp) == &js_CallClass ||
955                OBJ_GET_CLASS(cx, thisp) == &js_BlockClass) {
956              return js_ComputeGlobalThis(cx, lazy, argv);              return js_ComputeGlobalThis(cx, lazy, argv);
   
         if (thisp->map->ops->thisObject) {  
             /* Some objects (e.g., With) delegate 'this' to another object. */  
             thisp = thisp->map->ops->thisObject(cx, thisp);  
             if (!thisp)  
                 return NULL;  
957          }          }
958          OBJ_TO_OUTER_OBJECT(cx, thisp);  
959            /* Some objects (e.g., With) delegate 'this' to another object. */
960            thisp = OBJ_THIS_OBJECT(cx, thisp);
961          if (!thisp)          if (!thisp)
962              return NULL;              return NULL;
963          argv[-1] = OBJECT_TO_JSVAL(thisp);          argv[-1] = OBJECT_TO_JSVAL(thisp);
# Line 881  Line 980 
980    
981  JSClass js_NoSuchMethodClass = {  JSClass js_NoSuchMethodClass = {
982      "NoSuchMethod",      "NoSuchMethod",
983      JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS |      JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
     JSCLASS_HAS_CACHED_PROTO(JSProto_NoSuchMethod),  
984      JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,      JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,   JS_PropertyStub,
985      JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,      JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,    JS_FinalizeStub,
986      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL      NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
987  };  };
988    
 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;  
 }  
   
989  /*  /*
990   * 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
991   * 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 937  Line 1014 
1014    
1015      MUST_FLOW_THROUGH("out");      MUST_FLOW_THROUGH("out");
1016      id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);      id = ATOM_TO_JSID(cx->runtime->atomState.noSuchMethodAtom);
1017  #if JS_HAS_XML_SUPPORT      ok = js_GetMethod(cx, obj, id, false, &tvr.u.value);
1018      if (OBJECT_IS_XML(cx, obj)) {      if (!ok)
1019          JSXMLObjectOps *ops;          goto out;
   
         ops = (JSXMLObjectOps *) obj->map->ops;  
         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;  
     }  
1020      if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {      if (JSVAL_IS_PRIMITIVE(tvr.u.value)) {
1021          vp[0] = tvr.u.value;          vp[0] = tvr.u.value;
1022      } else {      } else {
# Line 969  Line 1031 
1031                  vp[0] = ID_TO_VALUE(id);                  vp[0] = ID_TO_VALUE(id);
1032          }          }
1033  #endif  #endif
1034          obj = js_NewObject(cx, &js_NoSuchMethodClass, NULL, NULL, 0);          obj = js_NewObjectWithGivenProto(cx, &js_NoSuchMethodClass,
1035                                             NULL, NULL, 0);
1036          if (!obj) {          if (!obj) {
1037              ok = JS_FALSE;              ok = JS_FALSE;
1038              goto out;              goto out;
# Line 985  Line 1048 
1048      return ok;      return ok;
1049  }  }
1050    
1051  static JSBool  static JS_REQUIRES_STACK JSBool
1052  NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)  NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
1053  {  {
1054      jsval *invokevp;      jsval *invokevp;
# Line 1046  Line 1109 
1109   * required arguments, allocate declared local variables, and pop everything   * required arguments, allocate declared local variables, and pop everything
1110   * when done.  Then push the return value.   * when done.  Then push the return value.
1111   */   */
1112  JS_FRIEND_API(JSBool)  JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
1113  js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)  js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
1114  {  {
1115      void *mark;      void *mark;
# Line 1069  Line 1132 
1132      JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);      JS_ASSERT((jsval *) cx->stackPool.current->base <= vp);
1133      JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);      JS_ASSERT(vp + 2 + argc <= (jsval *) cx->stackPool.current->avail);
1134    
1135      /*      /* 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.  
      */  
1136      mark = JS_ARENA_MARK(&cx->stackPool);      mark = JS_ARENA_MARK(&cx->stackPool);
1137        MUST_FLOW_THROUGH("out2");
1138      v = *vp;      v = *vp;
1139    
1140      if (JSVAL_IS_PRIMITIVE(v))      if (JSVAL_IS_PRIMITIVE(v))
# Line 1098  Line 1159 
1159           * XXX better to call that hook without converting           * XXX better to call that hook without converting
1160           * XXX the only thing that needs fixing is liveconnect           * XXX the only thing that needs fixing is liveconnect
1161           *           *
1162           * Try converting to function, for closure and API compatibility.           * FIXME bug 408416: try converting to function, for API compatibility
1163           * 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.  
1164           */           */
1165          if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {          if ((ops == &js_ObjectOps) ? clasp->call : ops->call) {
1166              ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);              ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
# Line 1141  Line 1201 
1201          if (FUN_INTERPRETED(fun)) {          if (FUN_INTERPRETED(fun)) {
1202              native = NULL;              native = NULL;
1203              script = fun->u.i.script;              script = fun->u.i.script;
1204                JS_ASSERT(script);
1205          } else {          } else {
1206              native = fun->u.n.native;              native = fun->u.n.native;
1207              script = NULL;              script = NULL;
# Line 1265  Line 1326 
1326      frame.down = cx->fp;      frame.down = cx->fp;
1327      frame.annotation = NULL;      frame.annotation = NULL;
1328      frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */      frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
1329        frame.blockChain = NULL;
1330      frame.regs = NULL;      frame.regs = NULL;
1331      frame.imacpc = NULL;      frame.imacpc = NULL;
1332      frame.slots = NULL;      frame.slots = NULL;
# Line 1273  Line 1335 
1335      frame.flags = flags | rootedArgsFlag;      frame.flags = flags | rootedArgsFlag;
1336      frame.dormantNext = NULL;      frame.dormantNext = NULL;
1337      frame.xmlNamespace = NULL;      frame.xmlNamespace = NULL;
1338      frame.blockChain = NULL;      frame.displaySave = NULL;
1339    
1340      MUST_FLOW_THROUGH("out");      MUST_FLOW_THROUGH("out");
1341      cx->fp = &frame;      cx->fp = &frame;
# Line 1282  Line 1344 
1344      hook = cx->debugHooks->callHook;      hook = cx->debugHooks->callHook;
1345      hookData = NULL;      hookData = NULL;
1346    
     /* 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. */  
1347      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  
   
1348          /* If native, use caller varobj and scopeChain for eval. */          /* If native, use caller varobj and scopeChain for eval. */
1349          JS_ASSERT(!frame.varobj);          JS_ASSERT(!frame.varobj);
1350          JS_ASSERT(!frame.scopeChain);          JS_ASSERT(!frame.scopeChain);
# Line 1308  Line 1356 
1356          /* But ensure that we have a scope chain. */          /* But ensure that we have a scope chain. */
1357          if (!frame.scopeChain)          if (!frame.scopeChain)
1358              frame.scopeChain = parent;              frame.scopeChain = parent;
1359        } 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) {  
1360          /* Use parent scope so js_GetCallObject can find the right "Call". */          /* Use parent scope so js_GetCallObject can find the right "Call". */
1361          frame.scopeChain = parent;          frame.scopeChain = parent;
1362          if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {          if (JSFUN_HEAVYWEIGHT_TEST(fun->flags)) {
1363              /* Scope with a call object parented by the callee's parent. */              /* Scope with a call object parented by the callee's parent. */
1364              if (!js_GetCallObject(cx, &frame, parent)) {              if (!js_GetCallObject(cx, &frame)) {
1365                  ok = JS_FALSE;                  ok = JS_FALSE;
1366                  goto out;                  goto out;
1367              }              }
1368          }          }
1369          frame.slots = sp - fun->u.i.nvars;          frame.slots = sp - fun->u.i.nvars;
1370        }
1371    
1372          ok = js_Interpret(cx);      /* Call the hook if present after we fully initialized the frame. */
1373        if (hook)
1374            hookData = hook(cx, &frame, JS_TRUE, 0, cx->debugHooks->callHookData);
1375    
1376        /* Call the function, either a native method or an interpreted script. */
1377        if (native) {
1378    #ifdef DEBUG_NOT_THROWING
1379            JSBool alreadyThrowing = cx->throwing;
1380    #endif
1381    
1382    #if JS_HAS_LVALUE_RETURN
1383            /* Set by JS_SetCallReturnValue2, used to return reference types. */
1384            cx->rval2set = JS_FALSE;
1385    #endif
1386            ok = native(cx, frame.thisp, argc, frame.argv, &frame.rval);
1387            JS_RUNTIME_METER(cx->runtime, nativeCalls);
1388    #ifdef DEBUG_NOT_THROWING
1389            if (ok && !alreadyThrowing)
1390                ASSERT_NOT_THROWING(cx);
1391    #endif
1392      } else {      } else {
1393          /* fun might be onerror trying to report a syntax error in itself. */          JS_ASSERT(script);
1394          frame.scopeChain = NULL;          ok = js_Interpret(cx);
         frame.displaySave = NULL;  
         ok = JS_TRUE;  
1395      }      }
1396    
1397  out:  out:
# Line 1377  Line 1435 
1435      void *mark;      void *mark;
1436      JSBool ok;      JSBool ok;
1437    
1438        js_LeaveTrace(cx);
1439      invokevp = js_AllocStack(cx, 2 + argc, &mark);      invokevp = js_AllocStack(cx, 2 + argc, &mark);
1440      if (!invokevp)      if (!invokevp)
1441          return JS_FALSE;          return JS_FALSE;
# Line 1415  Line 1474 
1474  {  {
1475      JSSecurityCallbacks *callbacks;      JSSecurityCallbacks *callbacks;
1476    
1477        js_LeaveTrace(cx);
1478    
1479      /*      /*
1480       * 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
1481       * again, see bug 355497.       * again, see bug 355497.
# Line 1459  Line 1520 
1520      JSObject *obj, *tmp;      JSObject *obj, *tmp;
1521      JSBool ok;      JSBool ok;
1522    
1523        js_LeaveTrace(cx);
1524    
1525  #ifdef INCLUDE_MOZILLA_DTRACE  #ifdef INCLUDE_MOZILLA_DTRACE
1526      if (JAVASCRIPT_EXECUTE_START_ENABLED())      if (JAVASCRIPT_EXECUTE_START_ENABLED())
1527          jsdtrace_execute_start(script);          jsdtrace_execute_start(script);
# Line 1466  Line 1529 
1529    
1530      hook = cx->debugHooks->executeHook;      hook = cx->debugHooks->executeHook;
1531      hookData = mark = NULL;      hookData = mark = NULL;
1532      oldfp = cx->fp;      oldfp = js_GetTopStackFrame(cx);
1533      frame.script = script;      frame.script = script;
1534      if (down) {      if (down) {
1535          /* Propagate arg state for eval and the debugger API. */          /* Propagate arg state for eval and the debugger API. */
# Line 1543  Line 1606 
1606    
1607      cx->fp = &frame;      cx->fp = &frame;
1608      if (!down) {      if (!down) {
1609          OBJ_TO_OUTER_OBJECT(cx, frame.thisp);          frame.thisp = OBJ_THIS_OBJECT(cx, frame.thisp);
1610          if (!frame.thisp) {          if (!frame.thisp) {
1611              ok = JS_FALSE;              ok = JS_FALSE;
1612              goto out2;              goto out2;
# Line 1596  Line 1659 
1659      jsval value;      jsval value;
1660      const char *type, *name;      const char *type, *name;
1661    
1662        /*
1663         * Both objp and propp must be either null or given. When given, *propp
1664         * must be null. This way we avoid an extra "if (propp) *propp = NULL" for
1665         * the common case of a non-existing property.
1666         */
1667        JS_ASSERT(!objp == !propp);
1668        JS_ASSERT_IF(propp, !*propp);
1669    
1670        /* The JSPROP_INITIALIZER case below may generate a warning. Since we must
1671         * drop the property before reporting it, we insists on !propp to avoid
1672         * looking up the property again after the reporting is done.
1673         */
1674        JS_ASSERT_IF(attrs & JSPROP_INITIALIZER, attrs == JSPROP_INITIALIZER);
1675        JS_ASSERT_IF(attrs == JSPROP_INITIALIZER, !propp);
1676    
1677      if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))      if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1678          return JS_FALSE;          return JS_FALSE;
     if (propp) {  
         *objp = obj2;  
         *propp = prop;  
     }  
1679      if (!prop)      if (!prop)
1680          return JS_TRUE;          return JS_TRUE;
1681    
1682      /*      /* Use prop as a speedup hint to OBJ_GET_ATTRIBUTES. */
      * Use prop as a speedup hint to OBJ_GET_ATTRIBUTES, but drop it on error.  
      * An assertion at label bad: will insist that it is null.  
      */  
1683      if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {      if (!OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &oldAttrs)) {
1684          OBJ_DROP_PROPERTY(cx, obj2, prop);          OBJ_DROP_PROPERTY(cx, obj2, prop);
1685  #ifdef DEBUG          return JS_FALSE;
         prop = NULL;  
 #endif  
         goto bad;  
1686      }      }
1687    
1688      /*      /*
      * From here, return true, or else goto bad on failure to null out params.  
1689       * 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).
1690       */       */
1691      if (!propp) {      if (!propp) {
1692          OBJ_DROP_PROPERTY(cx, obj2, prop);          OBJ_DROP_PROPERTY(cx, obj2, prop);
1693          prop = NULL;          prop = NULL;
1694        } else {
1695            *objp = obj2;
1696            *propp = prop;
1697      }      }
1698    
1699      if (attrs == JSPROP_INITIALIZER) {      if (attrs == JSPROP_INITIALIZER) {
1700          /* Allow the new object to override properties. */          /* Allow the new object to override properties. */
1701          if (obj2 != obj)          if (obj2 != obj)
1702              return JS_TRUE;              return JS_TRUE;
1703    
1704            /* The property must be dropped already. */
1705            JS_ASSERT(!prop);
1706          report = JSREPORT_WARNING | JSREPORT_STRICT;          report = JSREPORT_WARNING | JSREPORT_STRICT;
1707      } else {      } else {
1708          /* We allow redeclaring some non-readonly properties. */          /* We allow redeclaring some non-readonly properties. */
1709          if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {          if (((oldAttrs | attrs) & JSPROP_READONLY) == 0) {
1710              /*              /* 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).  
              */  
1711              if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))              if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
1712                  return JS_TRUE;                  return JS_TRUE;
1713    
1714                /*
1715                 * Allow adding a getter only if a property already has a setter
1716                 * but no getter and similarly for adding a setter. That is, we
1717                 * allow only the following transitions:
1718                 *
1719                 *   no-property --> getter --> getter + setter
1720                 *   no-property --> setter --> getter + setter
1721                 */
1722              if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)              if ((~(oldAttrs ^ attrs) & (JSPROP_GETTER | JSPROP_SETTER)) == 0)
1723                  return JS_TRUE;                  return JS_TRUE;
1724    
1725                /*
1726                 * Allow redeclaration of an impermanent property (in which case
1727                 * anyone could delete it and redefine it, willy-nilly).
1728                 */
1729              if (!(oldAttrs & JSPROP_PERMANENT))              if (!(oldAttrs & JSPROP_PERMANENT))
1730                  return JS_TRUE;                  return JS_TRUE;
1731          }          }
1732            if (prop)
1733                OBJ_DROP_PROPERTY(cx, obj2, prop);
1734    
1735          report = JSREPORT_ERROR;          report = JSREPORT_ERROR;
1736          isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;          isFunction = (oldAttrs & (JSPROP_GETTER | JSPROP_SETTER)) != 0;
1737          if (!isFunction) {          if (!isFunction) {
1738              if (!OBJ_GET_PROPERTY(cx, obj, id, &value))              if (!OBJ_GET_PROPERTY(cx, obj, id, &value))
1739                  goto bad;                  return JS_FALSE;
1740              isFunction = VALUE_IS_FUNCTION(cx, value);              isFunction = VALUE_IS_FUNCTION(cx, value);
1741          }          }
1742      }      }
# Line 1670  Line 1754 
1754             : js_var_str;             : js_var_str;
1755      name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));      name = js_ValueToPrintableString(cx, ID_TO_VALUE(id));
1756      if (!name)      if (!name)
1757          goto bad;          return JS_FALSE;
1758      return JS_ReportErrorFlagsAndNumber(cx, report,      return JS_ReportErrorFlagsAndNumber(cx, report,
1759                                          js_GetErrorMessage, NULL,                                          js_GetErrorMessage, NULL,
1760                                          JSMSG_REDECLARED_VAR,                                          JSMSG_REDECLARED_VAR,
1761                                          type, name);                                          type, name);
   
 bad:  
     if (propp) {  
         *objp = NULL;  
         *propp = NULL;  
     }  
     JS_ASSERT(!prop);  
     return JS_FALSE;  
1762  }  }
1763    
1764  JSBool  JSBool
# Line 1728  Line 1804 
1804      return lval == rval;      return lval == rval;
1805  }  }
1806    
1807  JSBool  JS_REQUIRES_STACK JSBool
1808  js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)  js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
1809  {  {
1810      JSFunction *fun, *fun2;      JSFunction *fun, *fun2;
# Line 1773  Line 1849 
1849    
1850          if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {          if (OBJ_GET_CLASS(cx, obj2) == &js_FunctionClass) {
1851              fun2 = GET_FUNCTION_PRIVATE(cx, obj2);              fun2 = GET_FUNCTION_PRIVATE(cx, obj2);
1852              if (!FUN_INTERPRETED(fun2) &&              if (!FUN_INTERPRETED(fun2) && fun2->u.n.clasp)
1853                  !(fun2->flags & JSFUN_TRACEABLE) &&                  clasp = fun2->u.n.clasp;
                 fun2->u.n.u.clasp) {  
                 clasp = fun2->u.n.u.clasp;  
             }  
1854          }          }
1855      }      }
1856      obj = js_NewObject(cx, clasp, proto, parent, 0);      obj = js_NewObject(cx, clasp, proto, parent, 0);
# Line 1833  Line 1906 
1906   * 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
1907   * of the with block with sp + stackIndex.   * of the with block with sp + stackIndex.
1908   */   */
1909  JS_STATIC_INTERPRET JSBool  JS_STATIC_INTERPRET JS_REQUIRES_STACK JSBool
1910  js_EnterWith(JSContext *cx, jsint stackIndex)  js_EnterWith(JSContext *cx, jsint stackIndex)
1911  {  {
1912      JSStackFrame *fp;      JSStackFrame *fp;
# Line 1868  Line 1941 
1941          return JS_FALSE;          return JS_FALSE;
1942    
1943      fp->scopeChain = withobj;      fp->scopeChain = withobj;
     js_DisablePropertyCache(cx);  
1944      return JS_TRUE;      return JS_TRUE;
1945  }  }
1946    
1947  JS_STATIC_INTERPRET void  JS_STATIC_INTERPRET JS_REQUIRES_STACK void
1948  js_LeaveWith(JSContext *cx)  js_LeaveWith(JSContext *cx)
1949  {  {
1950      JSObject *withobj;      JSObject *withobj;
# Line 1883  Line 1955 
1955      JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);      JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
1956      cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);      cx->fp->scopeChain = OBJ_GET_PARENT(cx, withobj);
1957      JS_SetPrivate(cx, withobj, NULL);      JS_SetPrivate(cx, withobj, NULL);
     js_EnablePropertyCache(cx);  
1958  }  }
1959    
1960  JSClass *  JS_REQUIRES_STACK JSClass *
1961  js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)  js_IsActiveWithOrBlock(JSContext *cx, JSObject *obj, int stackDepth)
1962  {  {
1963      JSClass *clasp;      JSClass *clasp;
# Line 1900  Line 1971 
1971      return NULL;      return NULL;
1972  }  }
1973    
 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;  
 }  
   
1974  /*  /*
1975   * 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
1976   * fp->sp on return to stackDepth.   * fp->sp on return to stackDepth.
1977   */   */
1978  JSBool  JS_REQUIRES_STACK JSBool
1979  js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,  js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
1980                 JSBool normalUnwind)                 JSBool normalUnwind)
1981  {  {
# Line 1989  Line 2043 
2043      return JS_TRUE;      return JS_TRUE;
2044  }  }
2045    
2046    jsval&
2047    js_GetUpvar(JSContext *cx, uintN level, uintN cookie)
2048    {
2049        level -= UPVAR_FRAME_SKIP(cookie);
2050        JS_ASSERT(level < JS_DISPLAY_SIZE);
2051    
2052        JSStackFrame *fp = cx->display[level];
2053        JS_ASSERT(fp->script);
2054    
2055        uintN slot = UPVAR_FRAME_SLOT(cookie);
2056        jsval *vp;
2057    
2058        if (!fp->fun) {
2059            vp = fp->slots + fp->script->nfixed;
2060        } else if (slot < fp->fun->nargs) {
2061            vp = fp->argv;
2062        } else if (slot == CALLEE_UPVAR_SLOT) {
2063            vp = &fp->argv[-2];
2064            slot = 0;
2065        } else {
2066            slot -= fp->fun->nargs;
2067            JS_ASSERT(slot < fp->script->nslots);
2068            vp = fp->slots;
2069        }
2070    
2071        return vp[slot];
2072    }
2073    
2074  #ifdef DEBUG  #ifdef DEBUG
2075    
2076  JS_STATIC_INTERPRET void  JS_STATIC_INTERPRET JS_REQUIRES_STACK void
2077  js_TraceOpcode(JSContext *cx, jsint len)  js_TraceOpcode(JSContext *cx)
2078  {  {
2079      FILE *tracefp;      FILE *tracefp;
2080      JSStackFrame *fp;      JSStackFrame *fp;
2081      JSFrameRegs *regs;      JSFrameRegs *regs;
     JSOp prevop;  
2082      intN ndefs, n, nuses;      intN ndefs, n, nuses;
2083      jsval *siter;      jsval *siter;
2084      JSString *str;      JSString *str;
# Line 2007  Line 2088 
2088      JS_ASSERT(tracefp);      JS_ASSERT(tracefp);
2089      fp = cx->fp;      fp = cx->fp;
2090      regs = fp->regs;      regs = fp->regs;
2091      if (len != 0) {  
2092          prevop = (JSOp) regs->pc[-len];      /*
2093          ndefs = js_CodeSpec[prevop].ndefs;       * Operations in prologues don't produce interesting values, and
2094          if (ndefs != 0) {       * js_DecompileValueGenerator isn't set up to handle them anyway.
2095         */
2096        if (cx->tracePrevPc && regs->pc >= fp->script->main) {
2097            JSOp tracePrevOp = JSOp(*cx->tracePrevPc);
2098            ndefs = js_GetStackDefs(cx, &js_CodeSpec[tracePrevOp], tracePrevOp,
2099                                    fp->script, cx->tracePrevPc);
2100    
2101            /*
2102             * If there aren't that many elements on the stack, then
2103             * we have probably entered a new frame, and printing output
2104             * would just be misleading.
2105             */
2106            if (ndefs != 0 &&
2107                ndefs < regs->sp - fp->slots) {
2108              for (n = -ndefs; n < 0; n++) {              for (n = -ndefs; n < 0; n++) {
2109                  char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],                  char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
2110                                                           NULL);                                                           NULL);
# Line 2041  Line 2135 
2135                      PTRDIFF(regs->pc, fp->script->code, jsbytecode),                      PTRDIFF(regs->pc, fp->script->code, jsbytecode),
2136                      JS_FALSE, tracefp);                      JS_FALSE, tracefp);
2137      op = (JSOp) *regs->pc;      op = (JSOp) *regs->pc;
2138      nuses = js_CodeSpec[op].nuses;      nuses = js_GetStackUses(&js_CodeSpec[op], op, regs->pc);
2139      if (nuses != 0) {      if (nuses != 0) {
2140          for (n = -nuses; n < 0; n++) {          for (n = -nuses; n < 0; n++) {
2141              char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],              char *bytes = js_DecompileValueGenerator(cx, n, regs->sp[n],
# Line 2055  Line 2149 
2149          }          }
2150          fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));          fprintf(tracefp, " @ %u\n", (uintN) (regs->sp - StackBase(fp)));
2151      }      }
2152        cx->tracePrevPc = regs->pc;
2153    
2154        /* It's nice to have complete traces when debugging a crash.  */
2155        fflush(tracefp);
2156  }  }
2157    
2158  #endif /* DEBUG */  #endif /* DEBUG */
# Line 2411  Line 2509 
2509  #endif  #endif
2510    
2511  /*  /*
2512   * Interpreter assumes the following to implement condition-free interrupt   * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
2513   * implementation when !JS_THREADED_INTERP.   * single-thread DEBUG js shell testing to verify property cache hits.
2514   */   */
2515  JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);  #if defined DEBUG && !defined JS_THREADSAFE
2516    
2517    # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \
2518        JS_BEGIN_MACRO                                                            \
2519            if (!AssertValidPropertyCacheHit(cx, script, regs, pcoff, obj, pobj,  \
2520                                             entry)) {                            \
2521                goto error;                                                       \
2522            }                                                                     \
2523        JS_END_MACRO
2524    
2525    static bool
2526    AssertValidPropertyCacheHit(JSContext *cx, JSScript *script, JSFrameRegs& regs,
2527                                ptrdiff_t pcoff, JSObject *start, JSObject *found,
2528                                JSPropCacheEntry *entry)
2529    {
2530        uint32 sample = cx->runtime->gcNumber;
2531    
2532        JSAtom *atom;
2533        if (pcoff >= 0)
2534            GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom);
2535        else
2536            atom = cx->runtime->atomState.lengthAtom;
2537    
2538        JSObject *obj, *pobj;
2539        JSProperty *prop;
2540        bool ok;
2541    
2542        if (JOF_OPMODE(*regs.pc) == JOF_NAME) {
2543            ok = js_FindProperty(cx, ATOM_TO_JSID(atom), &obj, &pobj, &prop);
2544        } else {
2545            obj = start;
2546            ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop);
2547        }
2548        if (!ok)
2549            return false;
2550        if (!prop)
2551            return true;
2552        if (cx->runtime->gcNumber != sample ||
2553            PCVCAP_SHAPE(entry->vcap) != OBJ_SHAPE(pobj)) {
2554            OBJ_DROP_PROPERTY(cx, pobj, prop);
2555            return true;
2556        }
2557        JS_ASSERT(prop);
2558        JS_ASSERT(pobj == found);
2559    
2560        JSScopeProperty *sprop = (JSScopeProperty *) prop;
2561        if (PCVAL_IS_SLOT(entry->vword)) {
2562            JS_ASSERT(PCVAL_TO_SLOT(entry->vword) == sprop->slot);
2563        } else if (PCVAL_IS_SPROP(entry->vword)) {
2564            JS_ASSERT(PCVAL_TO_SPROP(entry->vword) == sprop);
2565        } else {
2566            jsval v;
2567            JS_ASSERT(PCVAL_IS_OBJECT(entry->vword));
2568            JS_ASSERT(entry->vword != PCVAL_NULL);
2569            JS_ASSERT(SCOPE_IS_BRANDED(OBJ_SCOPE(pobj)));
2570            JS_ASSERT(SPROP_HAS_STUB_GETTER(sprop));
2571            JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
2572            v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
2573            JS_ASSERT(VALUE_IS_FUNCTION(cx, v));
2574            JS_ASSERT(PCVAL_TO_OBJECT(entry->vword) == JSVAL_TO_OBJECT(v));
2575        }
2576    
2577        OBJ_DROP_PROPERTY(cx, pobj, prop);
2578        return true;
2579    }
2580    
2581    #else
2582    # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
2583    #endif
2584    
2585  /*  /*
2586   * 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 2423  Line 2589 
2589  JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);  JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
2590  JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);  JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
2591  JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);  JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
2592    JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
2593    JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
2594    JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH);
2595  JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);  JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
2596  JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);  JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
2597  JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);  JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
2598    
2599  /*  /*
2600     * Same for debuggable flat closures defined at top level in another function
2601     * or program fragment.
2602     */
2603    JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
2604    
2605    /*
2606   * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but   * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
2607   * remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.   * remain distinct for the decompiler.
2608   */   */
2609  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);  
2610    
2611  /* See TRY_BRANCH_AFTER_COND. */  /* See TRY_BRANCH_AFTER_COND. */
2612  JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);  JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
# Line 2443  Line 2617 
2617  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEINC_LENGTH);
2618  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);  JS_STATIC_ASSERT(JSOP_INCNAME_LENGTH == JSOP_NAMEDEC_LENGTH);
2619    
2620  JSBool  #ifdef JS_TRACER
2621    # define ABORT_RECORDING(cx, reason)                                          \
2622        JS_BEGIN_MACRO                                                            \
2623            if (TRACE_RECORDER(cx))                                               \
2624                js_AbortRecording(cx, reason);                                    \
2625        JS_END_MACRO
2626    #else
2627    # define ABORT_RECORDING(cx, reason)    ((void) 0)
2628    #endif
2629    
2630    JS_REQUIRES_STACK JSBool
2631  js_Interpret(JSContext *cx)  js_Interpret(JSContext *cx)
2632  {  {
2633      JSRuntime *rt;      JSRuntime *rt;
# Line 2472  Line 2656 
2656      JSClass *clasp;      JSClass *clasp;
2657      JSFunction *fun;      JSFunction *fun;
2658      JSType type;      JSType type;
 #if JS_THREADED_INTERP  
     register void * const *jumpTable;  
 #else  
     register uint32 switchMask;  
     uintN switchOp;  
 #endif  
2659      jsint low, high, off, npairs;      jsint low, high, off, npairs;
2660      JSBool match;      JSBool match;
2661  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
# Line 2493  Line 2671 
2671  # define JS_EXTENSION_(s) s  # define JS_EXTENSION_(s) s
2672  #endif  #endif
2673    
2674    # ifdef DEBUG
2675        /*
2676         * We call this macro from BEGIN_CASE in threaded interpreters,
2677         * and before entering the switch in non-threaded interpreters.
2678         * However, reaching such points doesn't mean we've actually
2679         * fetched an OP from the instruction stream: some opcodes use
2680         * 'op=x; DO_OP()' to let another opcode's implementation finish
2681         * their work, and many opcodes share entry points with a run of
2682         * consecutive BEGIN_CASEs.
2683         *
2684         * Take care to trace OP only when it is the opcode fetched from
2685         * the instruction stream, so the trace matches what one would
2686         * expect from looking at the code.  (We do omit POPs after SETs;
2687         * unfortunate, but not worth fixing.)
2688         */
2689    #  define TRACE_OPCODE(OP)  JS_BEGIN_MACRO                                    \
2690                                    if (JS_UNLIKELY(cx->tracefp != NULL) &&       \
2691                                        (OP) == *regs.pc)                         \
2692                                        js_TraceOpcode(cx);                       \
2693                                JS_END_MACRO
2694    # else
2695    #  define TRACE_OPCODE(OP)  ((void) 0)
2696    # endif
2697    
2698  #if JS_THREADED_INTERP  #if JS_THREADED_INTERP
2699      static void *const normalJumpTable[] = {      static void *const normalJumpTable[] = {
2700  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
# Line 2501  Line 2703 
2703  # undef OPDEF  # undef OPDEF
2704      };      };
2705    
 #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 */  
   
2706      static void *const interruptJumpTable[] = {      static void *const interruptJumpTable[] = {
2707  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \  # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \
2708          JS_EXTENSION &&L_JSOP_INTERRUPT,          JS_EXTENSION &&interrupt,
2709  # include "jsopcode.tbl"  # include "jsopcode.tbl"
2710  # undef OPDEF  # undef OPDEF
2711      };      };
2712    
2713        register void * const *jumpTable = normalJumpTable;
2714    
2715      METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */      METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */
2716    
2717    # define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
2718    
2719  # ifdef JS_TRACER  # ifdef JS_TRACER
2720  #  define CHECK_RECORDER()  JS_BEGIN_MACRO                                    \  #  define CHECK_RECORDER()                                                    \
2721                                  JS_ASSERT(!TRACE_RECORDER(cx) ^               \      JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
                                           (jumpTable == recordingJumpTable)); \  
                             JS_END_MACRO  
2722  # else  # else
2723  #  define CHECK_RECORDER()  ((void)0)  #  define CHECK_RECORDER()  ((void)0)
2724  # endif  # endif
# Line 2538  Line 2733 
2733                                  DO_OP();                                      \                                  DO_OP();                                      \
2734                              JS_END_MACRO                              JS_END_MACRO
2735    
2736  # define BEGIN_CASE(OP)     L_##OP:                                           \  # define BEGIN_CASE(OP)     L_##OP: TRACE_OPCODE(OP); CHECK_RECORDER();
                                 CHECK_RECORDER();  
2737  # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);  # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
2738  # define END_VARLEN_CASE    DO_NEXT_OP(len);  # define END_VARLEN_CASE    DO_NEXT_OP(len);
2739  # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \  # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \
# Line 2551  Line 2745 
2745    
2746  #else /* !JS_THREADED_INTERP */  #else /* !JS_THREADED_INTERP */
2747    
2748        register intN switchMask = 0;
2749        intN switchOp;
2750    
2751    # define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
2752    
2753    # ifdef JS_TRACER
2754    #  define CHECK_RECORDER()                                                    \
2755        JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
2756    # else
2757    #  define CHECK_RECORDER()  ((void)0)
2758    # endif
2759    
2760  # define DO_OP()            goto do_op  # define DO_OP()            goto do_op
2761  # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \  # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
2762                                  JS_ASSERT((n) == len);                        \                                  JS_ASSERT((n) == len);                        \
2763                                  goto advance_pc;                              \                                  goto advance_pc;                              \
2764                              JS_END_MACRO                              JS_END_MACRO
2765    
2766  # define BEGIN_CASE(OP)     case OP:  # define BEGIN_CASE(OP)     case OP: CHECK_RECORDER();
2767  # define END_CASE(OP)       END_CASE_LEN(OP##_LENGTH)  # define END_CASE(OP)       END_CASE_LEN(OP##_LENGTH)
2768  # define END_CASE_LEN(n)    END_CASE_LENX(n)  # define END_CASE_LEN(n)    END_CASE_LENX(n)
2769  # define END_CASE_LENX(n)   END_CASE_LEN##n  # define END_CASE_LENX(n)   END_CASE_LEN##n
# Line 2579  Line 2785 
2785    
2786  #ifdef JS_TRACER  #ifdef JS_TRACER
2787      /* We had better not be entering the interpreter from JIT-compiled code. */      /* We had better not be entering the interpreter from JIT-compiled code. */
2788      TraceRecorder *tr = NULL;      TraceRecorder *tr = TRACE_RECORDER(cx);
2789      if (JS_ON_TRACE(cx)) {      SET_TRACE_RECORDER(cx, NULL);
2790          tr = TRACE_RECORDER(cx);  
2791          SET_TRACE_RECORDER(cx, NULL);      /* If a recorder is pending and we try to re-enter the interpreter, flag
2792          JS_TRACE_MONITOR(cx).onTrace = JS_FALSE;         the recorder to be destroyed when we return. */
2793          /*      if (tr) {
2794           * ON_TRACE means either recording or coming from traced code.          if (tr->wasDeepAborted())
2795           * If there's no recorder (the latter case), don't care.              tr->removeFragmentoReferences();
2796           */          else
2797          if (tr) {              tr->pushAbortStack();
             if (tr->wasDeepAborted())  
                 tr->removeFragmentoReferences();  
             else  
                 tr->pushAbortStack();  
         }  
2798      }      }
2799  #endif  #endif
2800    
# Line 2644  Line 2845 
2845  #define MONITOR_BRANCH()                                                      \  #define MONITOR_BRANCH()                                                      \
2846      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2847          if (TRACING_ENABLED(cx)) {                                            \          if (TRACING_ENABLED(cx)) {                                            \
2848              ENABLE_TRACER(js_MonitorLoopEdge(cx, inlineCallCount));           \              if (js_MonitorLoopEdge(cx, inlineCallCount)) {                    \
2849                    JS_ASSERT(TRACE_RECORDER(cx));                                \
2850                    ENABLE_INTERRUPTS();                                          \
2851                }                                                                 \
2852              fp = cx->fp;                                                      \              fp = cx->fp;                                                      \
2853              script = fp->script;                                              \              script = fp->script;                                              \
2854              atoms = script->atomMap.vector;                                   \              atoms = FrameAtomBase(cx, fp);                                    \
2855              currentVersion = (JSVersion) script->version;                     \              currentVersion = (JSVersion) script->version;                     \
2856              JS_ASSERT(fp->regs == &regs);                                     \              JS_ASSERT(fp->regs == &regs);                                     \
2857              if (cx->throwing)                                                 \              if (cx->throwing)                                                 \
# Line 2667  Line 2871 
2871       */       */
2872  #define CHECK_BRANCH()                                                        \  #define CHECK_BRANCH()                                                        \
2873      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2874          if ((cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) {                  \          if (!JS_CHECK_OPERATION_LIMIT(cx))                                    \
2875              if (!js_ResetOperationCount(cx))                                  \              goto error;                                                       \
                 goto error;                                                   \  
         }                                                                     \  
2876      JS_END_MACRO      JS_END_MACRO
2877    
2878    #ifndef TRACE_RECORDER
2879    #define TRACE_RECORDER(cx) (false)
2880    #endif
2881    
2882  #define BRANCH(n)                                                             \  #define BRANCH(n)                                                             \
2883      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
2884          regs.pc += n;                                                         \          regs.pc += (n);                                                       \
2885          if (n <= 0) {                                                         \          op = (JSOp) *regs.pc;                                                 \
2886            if ((n) <= 0) {                                                       \
2887              CHECK_BRANCH();                                                   \              CHECK_BRANCH();                                                   \
2888              MONITOR_BRANCH();                                                 \              if (op == JSOP_NOP) {                                             \
2889                    if (TRACE_RECORDER(cx)) {                                     \
2890                        MONITOR_BRANCH();                                         \
2891                        op = (JSOp) *regs.pc;                                     \
2892                    } else {                                                      \
2893                        op = (JSOp) *++regs.pc;                                   \
2894                    }                                                             \
2895                } else if (op == JSOP_LOOP) {                                     \
2896                    MONITOR_BRANCH();                                             \
2897                    op = (JSOp) *regs.pc;                                         \
2898                }                                                                 \
2899          }                                                                     \          }                                                                     \
         op = (JSOp) *regs.pc;                                                 \  
2900          DO_OP();                                                              \          DO_OP();                                                              \
2901      JS_END_MACRO      JS_END_MACRO
2902    
# Line 2702  Line 2918 
2918          js_SetVersion(cx, currentVersion);          js_SetVersion(cx, currentVersion);
2919    
2920      /* Update the static-link display. */      /* Update the static-link display. */
2921      if (script->staticDepth < JS_DISPLAY_SIZE) {      if (script->staticLevel < JS_DISPLAY_SIZE) {
2922          JSStackFrame **disp = &cx->display[script->staticDepth];          JSStackFrame **disp = &cx->display[script->staticLevel];
2923          fp->displaySave = *disp;          fp->displaySave = *disp;
2924          *disp = fp;          *disp = fp;
2925      }      }
2926  #ifdef DEBUG  
2927      fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;  # define CHECK_INTERRUPT_HANDLER()                                            \
2928  #endif      JS_BEGIN_MACRO                                                            \
2929            if (cx->debugHooks->interruptHandler)                                 \
2930                ENABLE_INTERRUPTS();                                              \
2931        JS_END_MACRO
2932    
2933      /*      /*
2934       * 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 2717  Line 2936 
2936       * 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
2937       * 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.
2938       */       */
2939  #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);  
2940    
2941  #if !JS_HAS_GENERATORS  #if !JS_HAS_GENERATORS
2942      JS_ASSERT(!fp->regs);      JS_ASSERT(!fp->regs);
# Line 2780  Line 2959 
2959          fp->regs = &regs;          fp->regs = &regs;
2960          JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);          JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
2961          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);  
2962    
2963          /*          /*
2964           * To support generator_throw and to catch ignored exceptions,           * To support generator_throw and to catch ignored exceptions,
# Line 2813  Line 2990 
2990       * 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
2991       * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.       * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
2992       * When interrupts are enabled, jumpTable is set to interruptJumpTable       * When interrupts are enabled, jumpTable is set to interruptJumpTable
2993       * where all jumps point to the JSOP_INTERRUPT case. The latter, after       * where all jumps point to the interrupt label. The latter, after
2994       * calling the interrupt handler, dispatches through normalJumpTable to       * calling the interrupt handler, dispatches through normalJumpTable to
2995       * continue the normal bytecode processing.       * continue the normal bytecode processing.
2996       */       */
2997  #else    interrupt:
2998    #else /* !JS_THREADED_INTERP */
2999      for (;;) {      for (;;) {
3000        advance_pc_by_one:        advance_pc_by_one:
3001          JS_ASSERT(js_CodeSpec[op].length == 1);          JS_ASSERT(js_CodeSpec[op].length == 1);
# Line 2825  Line 3003 
3003        advance_pc:        advance_pc:
3004          regs.pc += len;          regs.pc += len;
3005          op = (JSOp) *regs.pc;          op = (JSOp) *regs.pc;
 #ifdef DEBUG  
         if (cx->tracefp)  
             js_TraceOpcode(cx, len);  
 #endif  
3006    
3007        do_op:        do_op:
3008          switchOp = op & switchMask;          CHECK_RECORDER();
3009            TRACE_OPCODE(op);
3010            switchOp = intN(op) | switchMask;
3011        do_switch:        do_switch:
3012          switch (switchOp) {          switch (switchOp) {
3013              case -1:
3014                JS_ASSERT(switchMask == -1);
3015  #endif /* !JS_THREADED_INTERP */  #endif /* !JS_THREADED_INTERP */
   
           BEGIN_CASE(JSOP_INTERRUPT)  
3016            {            {
3017              JSTrapHandler handler;              bool moreInterrupts = false;
3018                JSTrapHandler handler = cx->debugHooks->interruptHandler;
             handler = cx->debugHooks->interruptHandler;  
3019              if (handler) {              if (handler) {
3020    #ifdef JS_TRACER
3021                    if (TRACE_RECORDER(cx))
3022                        js_AbortRecording(cx, "interrupt handler");
3023    #endif
3024                  switch (handler(cx, script, regs.pc, &rval,                  switch (handler(cx, script, regs.pc, &rval,
3025                                  cx->debugHooks->interruptHandlerData)) {                                  cx->debugHooks->interruptHandlerData)) {
3026                    case JSTRAP_ERROR:                    case JSTRAP_ERROR:
# Line 2858  Line 3037 
3037                      goto error;                      goto error;
3038                    default:;                    default:;
3039                  }                  }
3040  #if !JS_THREADED_INTERP                  moreInterrupts = true;
             } else {  
                 /* this was not a real interrupt, the tracer is trying to  
                    record a trace */  
                 switchOp = op + 256;  
                 goto do_switch;  
 #endif  
3041              }              }
3042              LOAD_INTERRUPT_HANDLER(cx);  
3043    #ifdef JS_TRACER
3044                TraceRecorder* tr = TRACE_RECORDER(cx);
3045                if (tr) {
3046                    JSRecordingStatus status = TraceRecorder::monitorRecording(cx, tr, op);
3047                    switch (status) {
3048                    case JSRS_CONTINUE:
3049                        moreInterrupts = true;
3050                        break;
3051                    case JSRS_IMACRO:
3052                        atoms = COMMON_ATOMS_START(&rt->atomState);
3053                        op = JSOp(*regs.pc);
3054                        DO_OP();    /* keep interrupting for op. */
3055                        break;
3056                    case JSRS_ERROR:
3057                        // The code at 'error:' aborts the recording.
3058                        goto error;
3059                    case JSRS_STOP:
3060                        break;
3061                    default:
3062                        JS_NOT_REACHED("Bad recording status");
3063                    }
3064                }
3065    #endif /* !JS_TRACER */
3066    
3067  #if JS_THREADED_INTERP  #if JS_THREADED_INTERP
3068                jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
3069              JS_EXTENSION_(goto *normalJumpTable[op]);              JS_EXTENSION_(goto *normalJumpTable[op]);
3070  #else  #else
3071              switchOp = op;              switchMask = moreInterrupts ? -1 : 0;
3072                switchOp = intN(op);
3073              goto do_switch;              goto do_switch;
3074  #endif  #endif
3075            }            }
# Line 2880  Line 3078 
3078            ADD_EMPTY_CASE(JSOP_NOP)            ADD_EMPTY_CASE(JSOP_NOP)
3079            ADD_EMPTY_CASE(JSOP_CONDSWITCH)            ADD_EMPTY_CASE(JSOP_CONDSWITCH)
3080            ADD_EMPTY_CASE(JSOP_TRY)            ADD_EMPTY_CASE(JSOP_TRY)
           ADD_EMPTY_CASE(JSOP_FINALLY)  
3081  #if JS_HAS_XML_SUPPORT  #if JS_HAS_XML_SUPPORT
3082            ADD_EMPTY_CASE(JSOP_STARTXML)            ADD_EMPTY_CASE(JSOP_STARTXML)
3083            ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)            ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
# Line 2951  Line 3148 
3148            END_CASE(JSOP_LEAVEWITH)            END_CASE(JSOP_LEAVEWITH)
3149    
3150            BEGIN_CASE(JSOP_RETURN)            BEGIN_CASE(JSOP_RETURN)
             CHECK_BRANCH();  
3151              fp->rval = POP_OPND();              fp->rval = POP_OPND();
3152              /* FALL THROUGH */              /* FALL THROUGH */
3153    
# Line 2962  Line 3158 
3158               * will be false after the inline_return label.               * will be false after the inline_return label.
3159               */               */
3160              ASSERT_NOT_THROWING(cx);              ASSERT_NOT_THROWING(cx);
3161                CHECK_BRANCH();
3162    
3163              if (fp->imacpc) {              if (fp->imacpc) {
3164                  /*                  /*
# Line 2997  Line 3194 
3194                  JSInlineFrame *ifp = (JSInlineFrame *) fp;                  JSInlineFrame *ifp = (JSInlineFrame *) fp;
3195                  void *hookData = ifp->hookData;                  void *hookData = ifp->hookData;
3196    
                 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);  
3197                  JS_ASSERT(!fp->blockChain);                  JS_ASSERT(!fp->blockChain);
3198                  JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));                  JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
3199    
3200                  if (script->staticDepth < JS_DISPLAY_SIZE)                  if (script->staticLevel < JS_DISPLAY_SIZE)
3201                      cx->display[script->staticDepth] = fp->displaySave;                      cx->display[script->staticLevel] = fp->displaySave;
3202    
3203                  if (hookData) {                  if (hookData) {
3204                      JSInterpreterHook hook;                      JSInterpreterHook hook;
# Line 3017  Line 3213 
3213                          status = ok;                          status = ok;
3214                          hook(cx, fp, JS_FALSE, &status, hookData);                          hook(cx, fp, JS_FALSE, &status, hookData);
3215                          ok = status;                          ok = status;
3216                          LOAD_INTERRUPT_HANDLER(cx);                          CHECK_INTERRUPT_HANDLER();
3217                      }                      }
3218                  }                  }
3219    
# Line 3074  Line 3270 
3270    
3271                  /* Restore the calling script's interpreter registers. */                  /* Restore the calling script's interpreter registers. */
3272                  script = fp->script;                  script = fp->script;
3273                  atoms = script->atomMap.vector;                  atoms = FrameAtomBase(cx, fp);
3274    
3275                  /* Resume execution in the calling frame. */                  /* Resume execution in the calling frame. */
3276                  inlineCallCount--;                  inlineCallCount--;
3277                  if (JS_LIKELY(ok)) {                  if (JS_LIKELY(ok)) {
3278                      TRACE_0(LeaveFrame);                      TRACE_0(LeaveFrame);
3279                      JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);                      JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
3280                                  == JSOP_CALL_LENGTH);
3281                      len = JSOP_CALL_LENGTH;                      len = JSOP_CALL_LENGTH;
3282                      DO_NEXT_OP(len);                      DO_NEXT_OP(len);
3283                  }                  }
# Line 3230  Line 3427 
3427              flags = regs.pc[1];              flags = regs.pc[1];
3428              if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))              if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
3429                  goto error;                  goto error;
3430              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
3431              JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));              JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
3432              PUSH(JSVAL_VOID);              PUSH(JSVAL_VOID);
3433            END_CASE(JSOP_ITER)            END_CASE(JSOP_ITER)
# Line 3240  Line 3437 
3437              JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));              JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
3438              if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))              if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
3439                  goto error;                  goto error;
3440              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
3441              rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);              rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
3442              PUSH(rval);              PUSH(rval);
             TRACE_0(IteratorNextComplete);  
3443            END_CASE(JSOP_NEXTITER)            END_CASE(JSOP_NEXTITER)
3444    
3445            BEGIN_CASE(JSOP_ENDITER)            BEGIN_CASE(JSOP_ENDITER)
# Line 3270  Line 3466 
3466              slot = GET_SLOTNO(regs.pc);              slot = GET_SLOTNO(regs.pc);
3467              JS_ASSERT(slot < fp->script->nslots);              JS_ASSERT(slot < fp->script->nslots);
3468              vp = &fp->slots[slot];              vp = &fp->slots[slot];
             GC_POKE(cx, *vp);  
3469              *vp = regs.sp[-1];              *vp = regs.sp[-1];
3470            END_CASE(JSOP_FORLOCAL)            END_CASE(JSOP_FORLOCAL)
3471    
# Line 3332  Line 3527 
3527              STORE_OPND(-2, rval);              STORE_OPND(-2, rval);
3528            END_CASE(JSOP_SWAP)            END_CASE(JSOP_SWAP)
3529    
3530              BEGIN_CASE(JSOP_PICK)
3531                i = regs.pc[1];
3532                JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
3533                lval = regs.sp[-(i+1)];
3534                memmove(regs.sp - (i+1), regs.sp - i, sizeof(jsval)*i);
3535                regs.sp[-1] = lval;
3536              END_CASE(JSOP_PICK)
3537    
3538  #define PROPERTY_OP(n, call)                                                  \  #define PROPERTY_OP(n, call)                                                  \
3539      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
3540          /* Fetch the left part and resolve it to a non-null object. */        \          /* Fetch the left part and resolve it to a non-null object. */        \
# Line 3370  Line 3573 
3573          }                                                                     \          }                                                                     \
3574      JS_END_MACRO      JS_END_MACRO
3575    
3576  #define NATIVE_SET(cx,obj,sprop,vp)                                           \  #define NATIVE_SET(cx,obj,sprop,entry,vp)                                     \
3577      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
3578            TRACE_2(SetPropHit, entry, sprop);                                    \
3579          if (SPROP_HAS_STUB_SETTER(sprop) &&                                   \          if (SPROP_HAS_STUB_SETTER(sprop) &&                                   \
3580              (sprop)->slot != SPROP_INVALID_SLOT) {                            \              (sprop)->slot != SPROP_INVALID_SLOT) {                            \
3581              /* Fast path for, e.g., Object instance properties. */            \              /* Fast path for, e.g., Object instance properties. */            \
# Line 3383  Line 3587 
3587      JS_END_MACRO      JS_END_MACRO
3588    
3589  /*  /*
  * 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  
   
 /*  
3590   * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is   * Skip the JSOP_POP typically found after a JSOP_SET* opcode, where oplen is
3591   * the constant length of the SET opcode sequence, and spdec is the constant   * the constant length of the SET opcode sequence, and spdec is the constant
3592   * by which to decrease the stack pointer to pop all of the SET op's operands.   * by which to decrease the stack pointer to pop all of the SET op's operands.
# Line 3497  Line 3649 
3649              do {              do {
3650                  JSPropCacheEntry *entry;                  JSPropCacheEntry *entry;
3651    
3652                    /*
3653                     * We can skip the property lookup for the global object. If
3654                     * the property does not exist anywhere on the scope chain,
3655                     * JSOP_SETNAME adds the property to the global.
3656                     *
3657                     * As a consequence of this optimization for the global object
3658                     * we run its JSRESOLVE_ASSIGNING-tolerant resolve hooks only
3659                     * in JSOP_SETNAME, after the interpreter evaluates the right-
3660                     * hand-side of the assignment, and not here.
3661                     *
3662                     * This should be transparent to the hooks because the script,
3663                     * instead of name = rhs, could have used global.name = rhs
3664                     * given a global object reference, which also calls the hooks
3665                     * only after evaluating the rhs. We desire such resolve hook
3666                     * equivalence between the two forms.
3667                     */
3668                  obj = fp->scopeChain;                  obj = fp->scopeChain;
3669                    if (!OBJ_GET_PARENT(cx, obj))
3670                        break;
3671                  if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {                  if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
3672                      PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);                      PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
3673                      if (!atom) {                      if (!atom) {
# Line 3510  Line 3680 
3680                      LOAD_ATOM(0);                      LOAD_ATOM(0);
3681                  }                  }
3682                  id = ATOM_TO_JSID(atom);                  id = ATOM_TO_JSID(atom);
3683                  obj = js_FindIdentifierBase(cx, id, entry);                  obj = js_FindIdentifierBase(cx, fp->scopeChain, id);
3684                  if (!obj)                  if (!obj)
3685                      goto error;                      goto error;
3686              } while (0);              } while (0);
# Line 3583  Line 3753 
3753          (rtmp == JSVAL_OBJECT &&                                              \          (rtmp == JSVAL_OBJECT &&                                              \
3754           (obj2 = JSVAL_TO_OBJECT(rval)) &&                                    \           (obj2 = JSVAL_TO_OBJECT(rval)) &&                                    \
3755           OBJECT_IS_XML(cx, obj2))) {                                          \           OBJECT_IS_XML(cx, obj2))) {                                          \
3756          JSXMLObjectOps *ops;                                                  \          if (JSVAL_IS_OBJECT(rval) && obj2 == JSVAL_TO_OBJECT(rval))           \
                                                                               \  
         ops = (JSXMLObjectOps *) obj2->map->ops;                              \  
         if (obj2 == JSVAL_TO_OBJECT(rval))                                    \  
3757              rval = lval;                                                      \              rval = lval;                                                      \
3758          if (!ops->equality(cx, obj2, rval, &cond))                            \          if (!js_TestXMLEquality(cx, obj2, rval, &cond))                       \
3759              goto error;                                                       \              goto error;                                                       \
3760          cond = cond OP JS_TRUE;                                               \          cond = cond OP JS_TRUE;                                               \
3761      } else      } else
# Line 3762  Line 3929 
3929              if (!JSVAL_IS_PRIMITIVE(lval) &&              if (!JSVAL_IS_PRIMITIVE(lval) &&
3930                  (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&                  (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
3931                  VALUE_IS_XML(cx, rval)) {                  VALUE_IS_XML(cx, rval)) {
3932                  JSXMLObjectOps *ops;                  if (!js_ConcatenateXML(cx, obj2, rval, &rval))
   
                 ops = (JSXMLObjectOps *) obj2->map->ops;  
                 if (!ops->concatenate(cx, obj2, rval, &rval))  
3933                      goto error;                      goto error;
3934                  regs.sp--;                  regs.sp--;
3935                  STORE_OPND(-1, rval);                  STORE_OPND(-1, rval);
# Line 4008  Line 4172 
4172                      ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);                      ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
4173                      if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {                      if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) {
4174                          slot = PCVAL_TO_SLOT(entry->vword);                          slot = PCVAL_TO_SLOT(entry->vword);
4175                          JS_ASSERT(slot < obj->map->freeslot);                          JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot);
4176                          rval = LOCKED_OBJ_GET_SLOT(obj, slot);                          rval = LOCKED_OBJ_GET_SLOT(obj, slot);
4177                          if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {                          if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
4178                              rtmp = rval;                              rtmp = rval;
# Line 4026  Line 4190 
4190                      LOAD_ATOM(0);                      LOAD_ATOM(0);
4191                  }                  }
4192              } else {              } else {
                 entry = NULL;  
4193                  LOAD_ATOM(0);                  LOAD_ATOM(0);
4194              }              }
4195              id = ATOM_TO_JSID(atom);              id = ATOM_TO_JSID(atom);
4196              if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)              if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
4197                  goto error;                  goto error;
4198              if (!prop)              if (!prop)
4199                  goto atom_not_defined;                  goto atom_not_defined;
# Line 4210  Line 4373 
4373    
4374  #define COMPUTE_THIS(cx, fp, obj)                                             \  #define COMPUTE_THIS(cx, fp, obj)                                             \
4375      JS_BEGIN_MACRO                                                            \      JS_BEGIN_MACRO                                                            \
4376          if (fp->flags & JSFRAME_COMPUTED_THIS) {                              \          if (!(obj = js_ComputeThisForFrame(cx, fp)))                          \
4377              obj = fp->thisp;                                                  \              goto error;                                                       \
         } else {                                                              \  
             obj = js_ComputeThis(cx, JS_TRUE, fp->argv);                      \  
             if (!obj)                                                         \  
                 goto error;                                                   \  
             fp->thisp = obj;                                                  \  
             fp->flags |= JSFRAME_COMPUTED_THIS;                               \  
         }                                                                     \  
4378      JS_END_MACRO      JS_END_MACRO
4379    
4380            BEGIN_CASE(JSOP_THIS)            BEGIN_CASE(JSOP_THIS)
# Line 4263  Line 4419 
4419                  JSObject *aobj;                  JSObject *aobj;
4420                  JSPropCacheEntry *entry;                  JSPropCacheEntry *entry;
4421    
4422                  aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj;                  aobj = js_GetProtoIfDenseArray(cx, obj);
4423                  if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {                  if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
4424                      PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);                      PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
4425                      if (!atom) {                      if (!atom) {
# Line 4272  Line 4428 
4428                              rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);                              rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
4429                          } else if (PCVAL_IS_SLOT(entry->vword)) {                          } else if (PCVAL_IS_SLOT(entry->vword)) {
4430                              slot = PCVAL_TO_SLOT(entry->vword);                              slot = PCVAL_TO_SLOT(entry->vword);
4431                              JS_ASSERT(slot < obj2->map->freeslot);                              JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
4432                              rval = LOCKED_OBJ_GET_SLOT(obj2, slot);                              rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
4433                          } else {                          } else {
4434                              JS_ASSERT(PCVAL_IS_SPROP(entry->vword));                              JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
# Line 4291  Line 4447 
4447                  }                  }
4448                  id = ATOM_TO_JSID(atom);                  id = ATOM_TO_JSID(atom);
4449                  if (entry                  if (entry
4450                      ? !js_GetPropertyHelper(cx, aobj, id, &rval, &entry)                      ? !js_GetPropertyHelper(cx, obj, id, true, &rval)
4451                      : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {                      : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {
4452                      goto error;                      goto error;
4453                  }                  }
# Line 4354  Line 4510 
4510                      goto error;                      goto error;
4511              }              }
4512    
4513              aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj;              aobj = js_GetProtoIfDenseArray(cx, obj);
4514              if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {              if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
4515                  PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);                  PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
4516                  if (!atom) {                  if (!atom) {
# Line 4363  Line 4519 
4519                          rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);                          rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
4520                      } else if (PCVAL_IS_SLOT(entry->vword)) {                      } else if (PCVAL_IS_SLOT(entry->vword)) {
4521                          slot = PCVAL_TO_SLOT(entry->vword);                          slot = PCVAL_TO_SLOT(entry->vword);
4522                          JS_ASSERT(slot < obj2->map->freeslot);                          JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
4523                          rval = LOCKED_OBJ_GET_SLOT(obj2, slot);                          rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
4524                      } else {                      } else {
4525                          JS_ASSERT(PCVAL_IS_SPROP(entry->vword));                          JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
# Line 4387  Line 4543 
4543              id = ATOM_TO_JSID(atom);              id = ATOM_TO_JSID(atom);
4544              PUSH(JSVAL_NULL);              PUSH(JSVAL_NULL);
4545              if (!JSVAL_IS_PRIMITIVE(lval)) {              if (!JSVAL_IS_PRIMITIVE(lval)) {
4546  #if JS_HAS_XML_SUPPORT                  if (!js_GetMethod(cx, obj, id, !!entry, &rval))
                 /* 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)) {  
4547                      goto error;                      goto error;
                 }  
4548                  STORE_OPND(-1, OBJECT_TO_JSVAL(obj));                  STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
4549                  STORE_OPND(-2, rval);                  STORE_OPND(-2, rval);
4550              } else {              } else {
4551                  JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);                  JS_ASSERT(obj->map->ops->getProperty == js_GetProperty);
4552                  if (!js_GetPropertyHelper(cx, obj, id, &rval, &entry))                  if (!js_GetPropertyHelper(cx, obj, id, true, &rval))
4553                      goto error;                      goto error;
4554                  STORE_OPND(-1, lval);                  STORE_OPND(-1, lval);
4555                  STORE_OPND(-2, rval);                  STORE_OPND(-2, rval);
# Line 4472  Line 4614 
4614                       * in native object o.                       * in native object o.
4615                       */                       */
4616                      entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];                      entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
4617                        PCMETER(cache->pctestentry = entry);
4618                      PCMETER(cache->tests++);                      PCMETER(cache->tests++);
4619                      PCMETER(cache->settests++);                      PCMETER(cache->settests++);
4620                      if (entry->kpc == regs.pc && entry->kshape == kshape) {                      if (entry->kpc == regs.pc &&
4621                          JSScope *scope;                          entry->kshape == kshape &&
4622                            PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) {
4623                            JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
4624    
4625                          JS_LOCK_OBJ(cx, obj);                          JS_LOCK_OBJ(cx, obj);
4626                          scope = OBJ_SCOPE(obj);                          JSScope *scope = OBJ_SCOPE(obj);
4627                          if (scope->shape == kshape) {                          if (scope->shape == kshape) {
4628                              JS_ASSERT(PCVAL_IS_SPROP(entry->vword));                              JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
4629                              sprop = PCVAL_TO_SPROP(entry->vword);                              sprop = PCVAL_TO_SPROP(entry->vword);
4630                              JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));                              JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
4631                              JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj)));                              JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj)));
4632    
4633                              if (scope->object == obj) {                              /*
4634                                  /*                               * Fastest path: check whether the cached sprop is
4635                                   * Fastest path: the cached sprop is already                               * already in scope and call NATIVE_GET and break
4636                                   * in scope. Just NATIVE_SET and break to get                               * to get out of the do-while(0). But we can call
4637                                   * out of the do-while(0).                               * NATIVE_GET only if obj owns scope or sprop is
4638                                   */                               * shared.
4639                                 */
4640                                bool checkForAdd;
4641                                if (scope->object == obj ||
4642                                    (sprop->attrs & JSPROP_SHARED)) {
4643                                  if (sprop == scope->lastProp ||                                  if (sprop == scope->lastProp ||
4644                                      SCOPE_HAS_PROPERTY(scope, sprop)) {                                      SCOPE_HAS_PROPERTY(scope, sprop)) {
4645                                      PCMETER(cache->pchits++);                                      PCMETER(cache->pchits++);
4646                                      PCMETER(cache->setpchits++);                                      PCMETER(cache->setpchits++);
4647                                      NATIVE_SET(cx, obj, sprop, &rval);                                      NATIVE_SET(cx, obj, sprop, entry, &rval);
4648                                      JS_UNLOCK_SCOPE(cx, scope);                                      JS_UNLOCK_SCOPE(cx, scope);
                                     TRACE_2(SetPropHit, entry, sprop);  
4649                                      break;                                      break;
4650                                  }                                  }
4651                                    checkForAdd =
4652                                        !(sprop->attrs & JSPROP_SHARED) &&
4653                                        sprop->parent == scope->lastProp &&
4654                                        !SCOPE_HAD_MIDDLE_DELETE(scope);
4655    
4656                              } else {                              } else {
4657                                  scope = js_GetMutableScope(cx, obj);                                  scope = js_GetMutableScope(cx, obj);
4658                                  if (!scope) {                                  if (!scope) {
4659                                      JS_UNLOCK_OBJ(cx, obj);                                      JS_UNLOCK_OBJ(cx, obj);
4660                                      goto error;                                      goto error;
4661                                  }                                  }
4662                                    checkForAdd = !sprop->parent;
4663                              }                              }
4664    
4665                              if (sprop->parent == scope->lastProp &&                              if (checkForAdd &&
                                 !SCOPE_HAD_MIDDLE_DELETE(scope) &&  
4666                                  SPROP_HAS_STUB_SETTER(sprop) &&                                  SPROP_HAS_STUB_SETTER(sprop) &&
4667                                  (slot = sprop->slot) == scope->map.freeslot) {                                  (slot = sprop->slot) == scope->freeslot) {
4668                                  /*                                  /*
4669                                   * Fast path: adding a plain old property that                                   * Fast path: adding a plain old property that
4670                                   * was once at the frontier of the property                                   * was once at the frontier of the property
# Line 4536  Line 4689 
4689                                   */                                   */
4690                                  if (slot < STOBJ_NSLOTS(obj) &&                                  if (slot < STOBJ_NSLOTS(obj) &&
4691                                      !OBJ_GET_CLASS(cx, obj)->reserveSlots) {                                      !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
4692                                      ++scope->map.freeslot;                                      ++scope->freeslot;
4693                                  } else {                                  } else {
4694                                      if (!js_AllocSlot(cx, obj, &slot)) {                                      if (!js_AllocSlot(cx, obj, &slot)) {
4695                                          JS_UNLOCK_SCOPE(cx, scope);                                          JS_UNLOCK_SCOPE(cx, scope);
# Line 4579  Line 4732 
4732                                      }                                      }
4733                                      sprop = sprop2;                                      sprop = sprop2;
4734                                  } else {                                  } else {
4735                                      SCOPE_EXTEND_SHAPE(cx, scope, sprop);                                      js_ExtendScopeShape(cx, scope, sprop);
4736                                      ++scope->entryCount;                                      ++scope->entryCount;
4737                                      scope->lastProp = sprop;                                      scope->lastProp = sprop;
4738                                  }                                  }
# Line 4587  Line 4740 
4740                                  GC_WRITE_BARRIER(cx, scope,                                  GC_WRITE_BARRIER(cx, scope,
4741                                                   LOCKED_OBJ_GET_SLOT(obj, slot),                                                   LOCKED_OBJ_GET_SLOT(obj, slot),
4742                                                   rval);                                                   rval);
4743                                    TRACE_2(SetPropHit, entry, sprop);
4744                                  LOCKED_OBJ_SET_SLOT(obj, slot, rval);                                  LOCKED_OBJ_SET_SLOT(obj, slot, rval);
4745                                  JS_UNLOCK_SCOPE(cx, scope);                                  JS_UNLOCK_SCOPE(cx, scope);
4746                                  TRACE_2(SetPropHit, entry, sprop);  
4747                                    /*
4748                                     * Purge the property cache of the id we may
4749                                     * have just shadowed in obj's scope and proto
4750                                     * chains. We do this after unlocking obj's
4751                                     * scope to avoid lock nesting.
4752                                     */
4753                                    js_PurgeScopeChain(cx, obj, sprop->id);
4754                                  break;                                  break;
4755                              }                              }
4756    
# Line 4613  Line 4774 
4774                              sprop = PCVAL_TO_SPROP(entry->vword);                              sprop = PCVAL_TO_SPROP(entry->vword);
4775                              JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));                              JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
4776                              JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2)));                              JS_ASSERT(!SCOPE_IS_SEALED(OBJ_SCOPE(obj2)));
4777                              NATIVE_SET(cx, obj, sprop, &rval);                              NATIVE_SET(cx, obj, sprop, entry, &rval);
4778                          }                          }
4779                          JS_UNLOCK_OBJ(cx, obj2);                          JS_UNLOCK_OBJ(cx, obj2);
4780                          if (sprop) {                          if (sprop)
                             TRACE_2(SetPropHit, entry, sprop);  
4781                              break;                              break;
                         }  
4782                      }                      }
4783                  }                  }
4784    
# Line 4627  Line 4786 
4786                      LOAD_ATOM(0);                      LOAD_ATOM(0);
4787                  id = ATOM_TO_JSID(atom);                  id = ATOM_TO_JSID(atom);
4788                  if (entry) {                  if (entry) {
4789                      if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry))                      if (!js_SetPropertyHelper(cx, obj, id, true, &rval))
4790                          goto error;                          goto error;
 #ifdef JS_TRACER  
                     if (entry)  
                         TRACE_1(SetPropMiss, entry);  
 #endif  
4791                  } else {                  } else {
4792                      if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))                      if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
4793                          goto error;                          goto error;
4794                  }                      ABORT_RECORDING(cx, "Non-native set");
4795  #ifdef JS_TRACER                   }
                 if (!entry && TRACE_RECORDER(cx)) {  
                     js_AbortRecording(cx, "SetPropUncached");  
                     ENABLE_TRACER(0);  
                 }  
 #endif  
4796              } while (0);              } while (0);
4797            END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);            END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
4798    
# Line 4667  Line 4817 
4817                  if (OBJ_IS_DENSE_ARRAY(cx, obj)) {                  if (OBJ_IS_DENSE_ARRAY(cx, obj)) {
4818                      jsuint length;                      jsuint length;
4819    
4820                      length = ARRAY_DENSE_LENGTH(obj);                      length = js_DenseArrayCapacity(obj);
4821                      i = JSVAL_TO_INT(rval);                      i = JSVAL_TO_INT(rval);
4822                      if ((jsuint)i < length &&                      if ((jsuint)i < length &&
4823                          i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {                          i < obj->fslots[JSSLOT_ARRAY_LENGTH]) {
# Line 4693  Line 4843 
4843            END_CASE(JSOP_GETELEM)            END_CASE(JSOP_GETELEM)
4844    
4845            BEGIN_CASE(JSOP_CALLELEM)            BEGIN_CASE(JSOP_CALLELEM)
4846              /*              ELEMENT_OP(-1, js_GetMethod(cx, obj, id, false, &rval));
              * 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));  
4847  #if JS_HAS_NO_SUCH_METHOD  #if JS_HAS_NO_SUCH_METHOD
4848              if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {              if (JS_UNLIKELY(JSVAL_IS_VOID(rval))) {
4849                  regs.sp[-2] = regs.sp[-1];                  regs.sp[-2] = regs.sp[-1];
# Line 4720  Line 4866 
4866                  if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {                  if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {
4867                      jsuint length;                      jsuint length;
4868    
4869                      length = ARRAY_DENSE_LENGTH(obj);                      length = js_DenseArrayCapacity(obj);
4870                      i = JSID_TO_INT(id);                      i = JSID_TO_INT(id);
4871                      if ((jsuint)i < length) {                      if ((jsuint)i < length) {
4872                          if (obj->dslots[i] == JSVAL_HOLE) {                          if (obj->dslots[i] == JSVAL_HOLE) {
4873                              if (rt->anyArrayProtoHasElement)                              if (js_PrototypeHasIndexedProperties(cx, obj))
4874                                  break;                                  break;
4875                              if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])                              if (i >= obj->fslots[JSSLOT_ARRAY_LENGTH])
4876                                  obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;                                  obj->fslots[JSSLOT_ARRAY_LENGTH] = i + 1;
# Line 4791  Line 4937 
4937              if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))              if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
4938                  goto error;                  goto error;
4939              regs.sp = vp + 1;              regs.sp = vp + 1;
4940              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
4941                TRACE_0(NativeCallComplete);
4942            END_CASE(JSOP_NEW)            END_CASE(JSOP_NEW)
4943    
           BEGIN_CASE(JSOP_APPLY)  
           {  
             argc = GET_ARGC(regs.pc);  
             vp = regs.sp - (argc + 2);  
             lval = *vp;  
             if (!VALUE_IS_FUNCTION(cx, lval))  
                 goto do_call;  
             obj = JSVAL_TO_OBJECT(lval);  
             fun = GET_FUNCTION_PRIVATE(cx, obj);  
             if (FUN_INTERPRETED(fun))  
                 goto do_call;  
   
             bool apply = (JSFastNative)fun->u.n.native == js_fun_apply;  
             if (!apply && (JSFastNative)fun->u.n.native != js_fun_call)  
                 goto do_call;  
   
             /*  
              * If the second arg to apply is null or void, treat it as an empty  
              * array.  
              */  
             jsuint applylen = 0;  
             if (apply && argc >= 2 &&  
                 !JSVAL_IS_VOID(vp[3]) && !JSVAL_IS_NULL(vp[3])) {  
                 /*  
                  * Fall back on js_Invoke when the array argument has a wrong  
                  * type or when it has too many elements to fit into the  
                  * current stack chunk.  
                  */  
                 if (!JSVAL_IS_OBJECT(vp[3]))  
                     goto do_call;  
   
                 JSBool arraylike;  
                 JSObject* aobj = JSVAL_TO_OBJECT(vp[3]);  
                 if (!js_IsArrayLike(cx, aobj, &arraylike, &applylen))  
                     goto error;  
                 if (!arraylike || applylen > ARGC_LIMIT)  
                     goto do_call;  
   
                 JSArena *a = cx->stackPool.current;  
                 JS_ASSERT(jsuword(vp + 2) <= a->limit);  
   
                 /*  
                  * We need space for applylen elements plus an extra slot to  
                  * temporary root the array object when we unpack its elements  
                  * using OBJ_GET_PROPERTY below.  
                  */  
                 if (a->limit - jsuword(vp + 2) < (applylen + 1) * sizeof(jsval))  
                     goto do_call;  
             }  
   
             if (!VALUE_IS_FUNCTION(cx, vp[1]))  
                 goto do_call;  
             vp[0] = vp[1];  
   
             if (argc == 0) {  
                 /*  
                  * Call fun with its global object as the 'this' param if  
                  * no args.  
                  */  
                 obj = NULL;  
             } else {  
                 /* Convert the first arg to 'this'. */  
                 if (!JSVAL_IS_PRIMITIVE(vp[2]))  
                     obj = JSVAL_TO_OBJECT(vp[2]);  
                 else if (!js_ValueToObject(cx, vp[2], &obj))  
                     goto error;  
             }  
             vp[1] = OBJECT_TO_JSVAL(obj);  
   
             if (!apply) {  
                 if (argc != 0) {  
                     --argc;  
                     memmove(vp + 2, vp + 3, argc * sizeof *vp);  
                 }  
             } else if (applylen == 0) {  
                 argc = 0;  
             } else {  
                 /*  
                  * Make room for missing arguments to the right including the  
                  * temporary root nulling any extra stack slots for GC safety.  
                  */  
                 jsval* newsp = vp + 2 + applylen + 1;  
                 if (newsp > regs.sp) {  
                     JSArena *a = cx->stackPool.current;  
                     JS_ASSERT(jsuword(newsp) <= a->limit); /* see above */  
                     if ((jsuword) newsp > a->avail)  
                         a->avail = (jsuword) newsp;  
                     memset(vp + 2 + argc, 0, (applylen - argc) * sizeof(jsval));  
                 }  
   
                 JSObject *aobj = JSVAL_TO_OBJECT(vp[3]);  
                 newsp[-1] = vp[3];  
                 regs.sp = newsp;  
   
                 /* Expand array content onto the stack. */  
                 for (i = 0; i < jsint(applylen); i++) {  
                     id = INT_TO_JSID(i);  
                     if (!OBJ_GET_PROPERTY(cx, aobj, id, &vp[2 + i])) {  
                         /*  
                          * There is no good way to restore the original stack  
                          * state here, but it is in a reasonable  state with  
                          * either original elements or nulls for all arguments  
                          * we didn't unpack yet, so we leave it at that.  
                          */  
                         goto error;  
                     }  
                 }  
                 argc = applylen;  
             }  
             regs.sp = vp + 2 + argc;  
             goto do_call_with_specified_vp_and_argc;  
           }  
             
4944            BEGIN_CASE(JSOP_CALL)            BEGIN_CASE(JSOP_CALL)
4945            BEGIN_CASE(JSOP_EVAL)            BEGIN_CASE(JSOP_EVAL)
4946            do_call:            BEGIN_CASE(JSOP_APPLY)
4947              argc = GET_ARGC(regs.pc);              argc = GET_ARGC(regs.pc);
4948              vp = regs.sp - (argc + 2);              vp = regs.sp - (argc + 2);
4949                
           do_call_with_specified_vp_and_argc:  
4950              lval = *vp;              lval = *vp;
4951              if (VALUE_IS_FUNCTION(cx, lval)) {              if (VALUE_IS_FUNCTION(cx, lval)) {
4952                  obj = JSVAL_TO_OBJECT(lval);                  obj = JSVAL_TO_OBJECT(lval);
# Line 5015  Line 5048 
5048                      newifp->frame.dormantNext = NULL;                      newifp->frame.dormantNext = NULL;
5049                      newifp->frame.xmlNamespace = NULL;                      newifp->frame.xmlNamespace = NULL;
5050                      newifp->frame.blockChain = NULL;                      newifp->frame.blockChain = NULL;
5051                      if (script->staticDepth < JS_DISPLAY_SIZE) {                      if (script->staticLevel < JS_DISPLAY_SIZE) {
5052                          JSStackFrame **disp = &cx->display[script->staticDepth];                          JSStackFrame **disp = &cx->display[script->staticLevel];
5053                          newifp->frame.displaySave = *disp;                          newifp->frame.displaySave = *disp;
5054                          *disp = &newifp->frame;                          *disp = &newifp->frame;
5055                      }                      }
 #ifdef DEBUG  
                     newifp->frame.pcDisabledSave =  
                         JS_PROPERTY_CACHE(cx).disabled;  
 #endif  
5056                      newifp->mark = newmark;                      newifp->mark = newmark;
5057    
5058                      /* Compute the 'this' parameter now that argv is set. */                      /* Compute the 'this' parameter now that argv is set. */
# Line 5040  Line 5069 
5069                      while (nvars--)                      while (nvars--)
5070                          *newsp++ = JSVAL_VOID;                          *newsp++ = JSVAL_VOID;
5071    
                     /* Call the debugger hook if present. */  
                     hook = cx->debugHooks->callHook;  
                     if (hook) {  
                         newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,  
                                                 cx->debugHooks->callHookData);  
                         LOAD_INTERRUPT_HANDLER(cx);  
                     } else {  
                         newifp->hookData = NULL;  
                     }  
   
5072                      /* Scope with a call object parented by callee's parent. */                      /* Scope with a call object parented by callee's parent. */
5073                      if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&                      if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
5074                          !js_GetCallObject(cx, &newifp->frame, parent)) {                          !js_GetCallObject(cx, &newifp->frame)) {
5075                          goto bad_inline_call;                          goto bad_inline_call;
5076                      }                      }
5077    
# Line 5072  Line 5091 
5091                      newifp->frame.regs = &regs;                      newifp->frame.regs = &regs;
5092                      cx->fp = fp = &newifp->frame;                      cx->fp = fp = &newifp->frame;
5093    
5094                        /* Call the debugger hook if present. */
5095                        hook = cx->debugHooks->callHook;
5096                        if (hook) {
5097                            newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
5098                                                    cx->debugHooks->callHookData);
5099                            CHECK_INTERRUPT_HANDLER();
5100                        } else {
5101                            newifp->hookData = NULL;
5102                        }
5103    
5104                      TRACE_0(EnterFrame);                      TRACE_0(EnterFrame);
5105    
5106                      inlineCallCount++;                      inlineCallCount++;
# Line 5125  Line 5154 
5154                      }                      }
5155  #endif  #endif
5156                      regs.sp = vp + 1;                      regs.sp = vp + 1;
5157                      if (!ok)                      if (!ok) {
5158                          goto error;                          /*
5159                      TRACE_0(FastNativeCallComplete);                           * If we are executing the JSOP_NEXTITER imacro and a Stopiteration
5160                             * exception is raised, transform it into a JSVAL_HOLE return value.
5161                             * The tracer generates equivalent code by calling CatchStopIteration_tn.
5162                             */
5163                            if (fp->imacpc && *fp->imacpc == JSOP_NEXTITER &&
5164                                cx->throwing && js_ValueIsStopIteration(cx->exception)) {
5165                                // pc may point to JSOP_DUP here due to bug 474854.
5166                                JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
5167                                cx->throwing = JS_FALSE;
5168                                cx->exception = JSVAL_VOID;
5169                                regs.sp[-1] = JSVAL_HOLE;
5170                            } else {
5171                                goto error;
5172                            }
5173                        }
5174                        TRACE_0(NativeCallComplete);
5175                      goto end_call;                      goto end_call;
5176                  }                  }
5177              }              }
# Line 5143  Line 5187 
5187              }              }
5188  #endif  #endif
5189              regs.sp = vp + 1;              regs.sp = vp + 1;
5190              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
5191              if (!ok)              if (!ok)
5192                  goto error;                  goto error;
5193              JS_RUNTIME_METER(rt, nonInlineCalls);              JS_RUNTIME_METER(rt, nonInlineCalls);
5194                TRACE_0(NativeCallComplete);
5195    
5196            end_call:            end_call:
5197  #if JS_HAS_LVALUE_RETURN  #if JS_HAS_LVALUE_RETURN
# Line 5181  Line 5226 
5226              vp = regs.sp - argc - 2;              vp = regs.sp - argc - 2;
5227              ok = js_Invoke(cx, argc, vp, 0);              ok = js_Invoke(cx, argc, vp, 0);
5228              regs.sp = vp + 1;              regs.sp = vp + 1;
5229              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
5230              if (!ok)              if (!ok)
5231                  goto error;                  goto error;
5232              if (!cx->rval2set) {              if (!cx->rval2set) {
5233                  op2 = (JSOp) regs.pc[JSOP_SETCALL_LENGTH];                  op2 = js_GetOpcode(cx, script, regs.pc + JSOP_SETCALL_LENGTH);
5234                  if (op2 != JSOP_DELELEM) {                  if (op2 != JSOP_DELELEM) {
                     JS_ASSERT(!(js_CodeSpec[op2].format & JOF_DEL));  
5235                      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,                      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5236                                           JSMSG_BAD_LEFTSIDE_OF_ASS);                                           JSMSG_BAD_LEFTSIDE_OF_ASS);
5237                      goto error;                      goto error;
# Line 5226  Line 5270 
5270    
5271                      if (PCVAL_IS_SLOT(entry->vword)) {                      if (PCVAL_IS_SLOT(entry->vword)) {
5272                          slot = PCVAL_TO_SLOT(entry->vword);                          slot = PCVAL_TO_SLOT(entry->vword);
5273                          JS_ASSERT(slot < obj2->map->freeslot);                          JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot);
5274                          rval = LOCKED_OBJ_GET_SLOT(obj2, slot);                          rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
5275                          JS_UNLOCK_OBJ(cx, obj2);                          JS_UNLOCK_OBJ(cx, obj2);
5276                          goto do_push_rval;                          goto do_push_rval;
# Line 5237  Line 5281 
5281                      goto do_native_get;                      goto do_native_get;
5282                  }                  }
5283              } else {              } else {
                 entry = NULL;  
5284                  LOAD_ATOM(0);                  LOAD_ATOM(0);
5285              }              }
5286    
5287              id = ATOM_TO_JSID(atom);              id = ATOM_TO_JSID(atom);
5288              if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)              if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
5289                  goto error;                  goto error;
5290              if (!prop) {              if (!prop) {
5291                  /* Kludge to allow (typeof foo == "undefined") tests. */                  /* Kludge to allow (typeof foo == "undefined") tests. */
5292                  endpc = script->code + script->length;                  endpc = script->code + script->length;
5293                  op2 = (JSOp) regs.pc[JSOP_NAME_LENGTH];                  op2 = js_GetOpcode(cx, script, regs.pc + JSOP_NAME_LENGTH);
5294                  if (op2 == JSOP_TYPEOF) {                  if (op2 == JSOP_TYPEOF) {
5295                      PUSH_OPND(JSVAL_VOID);                      PUSH_OPND(JSVAL_VOID);
5296                      len = JSOP_NAME_LENGTH;                      len = JSOP_NAME_LENGTH;
# Line 5261  Line 5304 
5304                  OBJ_DROP_PROPERTY(cx, obj2, prop);                  OBJ_DROP_PROPERTY(cx, obj2, prop);
5305                  if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))                  if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
5306                      goto error;                      goto error;
                 entry = NULL;  
5307              } else {              } else {
5308                  sprop = (JSScopeProperty *)prop;                  sprop = (JSScopeProperty *)prop;
5309            do_native_get:            do_native_get:
# Line 5453  Line 5495 
5495            END_CASE(JSOP_ONE)            END_CASE(JSOP_ONE)
5496    
5497            BEGIN_CASE(JSOP_NULL)            BEGIN_CASE(JSOP_NULL)
           BEGIN_CASE(JSOP_NULLTHIS)  
5498              PUSH_OPND(JSVAL_NULL);              PUSH_OPND(JSVAL_NULL);
5499            END_CASE(JSOP_NULL)            END_CASE(JSOP_NULL)
5500    
# Line 5619  Line 5660 
5660                  break;                  break;
5661              }              }
5662              JS_ASSERT(status == JSTRAP_CONTINUE);              JS_ASSERT(status == JSTRAP_CONTINUE);
5663              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
5664              JS_ASSERT(JSVAL_IS_INT(rval));              JS_ASSERT(JSVAL_IS_INT(rval));
5665              op = (JSOp) JSVAL_TO_INT(rval);              op = (JSOp) JSVAL_TO_INT(rval);
5666              JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);              JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
# Line 5661  Line 5702 
5702              JS_ASSERT(slot < fp->fun->nargs);              JS_ASSERT(slot < fp->fun->nargs);
5703              METER_SLOT_OP(op, slot);              METER_SLOT_OP(op, slot);
5704              vp = &fp->argv[slot];              vp = &fp->argv[slot];
             GC_POKE(cx, *vp);  
5705              *vp = FETCH_OPND(-1);              *vp = FETCH_OPND(-1);
5706            END_SET_CASE(JSOP_SETARG)            END_SET_CASE(JSOP_SETARG)
5707    
# Line 5682  Line 5722 
5722              slot = GET_SLOTNO(regs.pc);              slot = GET_SLOTNO(regs.pc);
5723              JS_ASSERT(slot < script->nslots);              JS_ASSERT(slot < script->nslots);
5724              vp = &fp->slots[slot];              vp = &fp->slots[slot];
             GC_POKE(cx, *vp);  
5725              *vp = FETCH_OPND(-1);              *vp = FETCH_OPND(-1);
5726            END_SET_CASE(JSOP_SETLOCAL)            END_SET_CASE(JSOP_SETLOCAL)
5727    
5728            BEGIN_CASE(JSOP_GETUPVAR)            BEGIN_CASE(JSOP_GETUPVAR)
5729            BEGIN_CASE(JSOP_CALLUPVAR)            BEGIN_CASE(JSOP_CALLUPVAR)
5730            {            {
5731              JSUpvarArray *uva;              JSUpvarArray *uva = JS_SCRIPT_UPVARS(script);
             uint32 skip;  
             JSStackFrame *fp2;  
5732    
5733              index = GET_UINT16(regs.pc);              index = GET_UINT16(regs.pc);
             uva = JS_SCRIPT_UPVARS(script);  
5734              JS_ASSERT(index < uva->length);              JS_ASSERT(index < uva->length);
             skip = UPVAR_FRAME_SKIP(uva->vector[index]);  
             fp2 = cx->display[script->staticDepth - skip];  
             JS_ASSERT(fp2->fun && fp2->script);  
   
             slot = UPVAR_FRAME_SLOT(uva->vector[index]);  
             if (slot < fp2->fun->nargs) {  
                 vp = fp2->argv;  
             } else {  
                 slot -= fp2->fun->nargs;  
                 JS_ASSERT(slot < fp2->script->nslots);  
                 vp = fp2->slots;  
             }  
5735    
5736              PUSH_OPND(vp[slot]);              rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
5737                PUSH_OPND(rval);
5738    
5739              if (op == JSOP_CALLUPVAR)              if (op == JSOP_CALLUPVAR)
5740                  PUSH_OPND(JSVAL_NULL);                  PUSH_OPND(JSVAL_NULL);
5741            }            }
5742            END_CASE(JSOP_GETUPVAR)            END_CASE(JSOP_GETUPVAR)
5743    
5744              BEGIN_CASE(JSOP_GETUPVAR_DBG)
5745              BEGIN_CASE(JSOP_CALLUPVAR_DBG)
5746                fun = fp->fun;
5747                JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
5748                JS_ASSERT(fun->u.i.wrapper);
5749    
5750                /* Scope for tempPool mark and local names allocation in it. */
5751                {
5752                    void *mark = JS_ARENA_MARK(&cx->tempPool);
5753                    jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
5754                    if (!names)
5755                        goto error;
5756    
5757                    index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
5758                    atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
5759                    id = ATOM_TO_JSID(atom);
5760    
5761                    ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
5762                    JS_ARENA_RELEASE(&cx->tempPool, mark);
5763                    if (!ok)
5764                        goto error;
5765                }
5766    
5767                if (!prop)
5768                    goto atom_not_defined;
5769    
5770                /* Minimize footprint with generic code instead of NATIVE_GET. */
5771                OBJ_DROP_PROPERTY(cx, obj2, prop);
5772                vp = regs.sp;
5773                PUSH_OPND(JSVAL_NULL);
5774                if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
5775                    goto error;
5776    
5777                if (op == JSOP_CALLUPVAR_DBG)
5778                    PUSH_OPND(JSVAL_NULL);
5779              END_CASE(JSOP_GETUPVAR_DBG)
5780    
5781              BEGIN_CASE(JSOP_GETDSLOT)
5782              BEGIN_CASE(JSOP_CALLDSLOT)
5783                obj = fp->callee;
5784                JS_ASSERT(obj);
5785                JS_ASSERT(obj->dslots);
5786    
5787                index = GET_UINT16(regs.pc);
5788                JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
5789                JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj,
5790                             JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot);
5791    
5792                PUSH_OPND(obj->dslots[index]);
5793                if (op == JSOP_CALLDSLOT)
5794                    PUSH_OPND(JSVAL_NULL);
5795              END_CASE(JSOP_GETDSLOT)
5796    
5797            BEGIN_CASE(JSOP_GETGVAR)            BEGIN_CASE(JSOP_GETGVAR)
5798            BEGIN_CASE(JSOP_CALLGVAR)            BEGIN_CASE(JSOP_CALLGVAR)
5799              slot = GET_SLOTNO(regs.pc);              slot = GET_SLOTNO(regs.pc);
# Line 5746  Line 5825 
5825                   * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]                   * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
5826                   * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.                   * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
5827                   */                   */
5828    #ifdef JS_TRACER
5829                    if (TRACE_RECORDER(cx))
5830                        js_AbortRecording(cx, "SETGVAR with NULL slot");
5831    #endif
5832                  LOAD_ATOM(0);                  LOAD_ATOM(0);
5833                  id = ATOM_TO_JSID(atom);                  id = ATOM_TO_JSID(atom);
5834                  if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))                  if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
# Line 5777  Line 5860 
5860    
5861              /* Lookup id in order to check for redeclaration problems. */              /* Lookup id in order to check for redeclaration problems. */
5862              id = ATOM_TO_JSID(atom);              id = ATOM_TO_JSID(atom);
5863                prop = NULL;
5864              if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))              if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))
5865                  goto error;                  goto error;
5866    
# Line 5799  Line 5883 
5883               */               */
5884              if (!fp->fun &&              if (!fp->fun &&
5885                  index < GlobalVarCount(fp) &&                  index < GlobalVarCount(fp) &&
                 (attrs & JSPROP_PERMANENT) &&  
5886                  obj2 == obj &&                  obj2 == obj &&
5887                  OBJ_IS_NATIVE(obj)) {                  OBJ_IS_NATIVE(obj)) {
5888                  sprop = (JSScopeProperty *) prop;                  sprop = (JSScopeProperty *) prop;
5889                  if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&                  if ((sprop->attrs & JSPROP_PERMANENT) &&
5890                        SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj)) &&
5891                      SPROP_HAS_STUB_GETTER(sprop) &&                      SPROP_HAS_STUB_GETTER(sprop) &&
5892                      SPROP_HAS_STUB_SETTER(sprop)) {                      SPROP_HAS_STUB_SETTER(sprop)) {
5893                      /*                      /*
# Line 5821  Line 5905 
5905            END_CASE(JSOP_DEFVAR)            END_CASE(JSOP_DEFVAR)
5906    
5907            BEGIN_CASE(JSOP_DEFFUN)            BEGIN_CASE(JSOP_DEFFUN)
5908              {
5909                JSPropertyOp getter, setter;
5910                bool doSet;
5911                JSObject *pobj;
5912                JSProperty *prop;
5913                uint32 old;
5914    
5915              /*              /*
5916               * A top-level function defined in Global or Eval code (see               * A top-level function defined in Global or Eval code (see
5917               * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named               * ECMA-262 Ed. 3), or else a SpiderMonkey extension: a named
# Line 5829  Line 5920 
5920               * function body).               * function body).
5921               */               */
5922              LOAD_FUNCTION(0);              LOAD_FUNCTION(0);
5923                obj = FUN_OBJECT(fun);
5924    
5925              if (!fp->blockChain) {              if (FUN_NULL_CLOSURE(fun)) {
5926                    /*
5927                     * Even a null closure needs a parent for principals finding.
5928                     * FIXME: bug 476950, although debugger users may also demand
5929                     * some kind of scope link for debugger-assisted eval-in-frame.
5930                     */
5931                  obj2 = fp->scopeChain;                  obj2 = fp->scopeChain;
5932              } else {              } else {
5933                  obj2 = js_GetScopeChain(cx, fp);                  JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
5934                  if (!obj2)  
5935                      goto error;                  /*
5936                     * Inline js_GetScopeChain a bit to optimize for the case of a
5937                     * top-level function.
5938                     */
5939                    if (!fp->blockChain) {
5940                        obj2 = fp->scopeChain;
5941                    } else {
5942                        obj2 = js_GetScopeChain(cx, fp);
5943                        if (!obj2)
5944                            goto error;
5945                    }
5946              }              }
5947    
5948              /*              /*
5949               * If static link is not current scope, clone fun's object to link               * If static link is not current scope, clone fun's object to link
5950               * to the current scope via parent. This clause exists to enable               * to the current scope via parent. We do this to enable sharing of
5951               * sharing of compiled functions among multiple equivalent scopes,               * compiled functions among multiple equivalent scopes, amortizing
5952               * splitting the cost of compilation evenly among the scopes and               * the cost of compilation over a number of executions.  Examples
5953               * amortizing it over a number of executions. Examples include XUL               * include XUL scripts and event handlers shared among Firefox or
5954               * scripts and event handlers shared among Mozilla chrome windows,               * other Mozilla app chrome windows, and user-defined JS functions
5955               * and server-side JS user-defined functions shared among requests.               * precompiled and then shared among requests in server-side JS.
5956               */               */
             obj = FUN_OBJECT(fun);  
5957              if (OBJ_GET_PARENT(cx, obj) != obj2) {              if (OBJ_GET_PARENT(cx, obj) != obj2) {
5958                  obj = js_CloneFunctionObject(cx, fun, obj2);                  obj = js_CloneFunctionObject(cx, fun, obj2);
5959                  if (!obj)                  if (!obj)
# Line 5859  Line 5965 
5965               * paths from here must flow through the "Restore fp->scopeChain"               * paths from here must flow through the "Restore fp->scopeChain"
5966               * code below the OBJ_DEFINE_PROPERTY call.               * code below the OBJ_DEFINE_PROPERTY call.
5967               */               */
5968              MUST_FLOW_THROUGH("restore");              MUST_FLOW_THROUGH("restore_scope");
5969              fp->scopeChain = obj;              fp->scopeChain = obj;
5970    
5971              rval = OBJECT_TO_JSVAL(obj);              rval = OBJECT_TO_JSVAL(obj);
5972    
5973              /*              /*
# Line 5876  Line 5983 
5983               * and setters do not need a slot, their value is stored elsewhere               * and setters do not need a slot, their value is stored elsewhere
5984               * in the property itself, not in obj slots.               * in the property itself, not in obj slots.
5985               */               */
5986                setter = getter = JS_PropertyStub;
5987              flags = JSFUN_GSFLAG2ATTR(fun->flags);              flags = JSFUN_GSFLAG2ATTR(fun->flags);
5988              if (flags) {              if (flags) {
5989                    /* Function cannot be both getter a setter. */
5990                    JS_ASSERT(flags == JSPROP_GETTER || flags == JSPROP_SETTER);
5991                  attrs |= flags | JSPROP_SHARED;                  attrs |= flags | JSPROP_SHARED;
5992                  rval = JSVAL_VOID;                  rval = JSVAL_VOID;
5993                    if (flags == JSPROP_GETTER)
5994                        getter = js_CastAsPropertyOp(obj);
5995                    else
5996                        setter = js_CastAsPropertyOp(obj);
5997              }              }
5998    
5999              /*              /*
# Line 5898  Line 6012 
6012               * as well as multiple HTML script tags.               * as well as multiple HTML script tags.
6013               */               */
6014              id = ATOM_TO_JSID(fun->atom);              id = ATOM_TO_JSID(fun->atom);
6015                prop = NULL;
6016                ok = js_CheckRedeclaration(cx, parent, id, attrs, &pobj, &prop);
6017                if (!ok)
6018                    goto restore_scope;
6019    
6020                /*
6021                 * We deviate from 10.1.2 in ECMA 262 v3 and under eval use for
6022                 * function declarations OBJ_SET_PROPERTY, not OBJ_DEFINE_PROPERTY,
6023                 * to preserve the JSOP_PERMANENT attribute of existing properties
6024                 * and make sure that such properties cannot be deleted.
6025                 *
6026                 * We also use OBJ_SET_PROPERTY for the existing properties of
6027                 * Call objects with matching attributes to preserve the native
6028                 * getters and setters that store the value of the property in the
6029                 * interpreter frame, see bug 467495.
6030                 */
6031                doSet = (attrs == JSPROP_ENUMERATE);
6032                JS_ASSERT_IF(doSet, fp->flags & JSFRAME_EVAL);
6033                if (prop) {
6034                    if (parent == pobj &&
6035                        OBJ_GET_CLASS(cx, parent) == &js_CallClass &&
6036                        (old = ((JSScopeProperty *) prop)->attrs,
6037                         !(old & (JSPROP_GETTER|JSPROP_SETTER)) &&
6038                         (old & (JSPROP_ENUMERATE|JSPROP_PERMANENT)) == attrs)) {
6039                        /*
6040                         * js_CheckRedeclaration must reject attempts to add a
6041                         * getter or setter to an existing property without a
6042                         * getter or setter.
6043                         */
6044                        JS_ASSERT(!(attrs & ~(JSPROP_ENUMERATE|JSPROP_PERMANENT)));
6045                        JS_ASSERT(!(old & JSPROP_READONLY));
6046                        doSet = JS_TRUE;
6047                    }
6048                    OBJ_DROP_PROPERTY(cx, pobj, prop);
6049                }
6050                ok = doSet
6051                     ? OBJ_SET_PROPERTY(cx, parent, id, &rval)
6052                     : OBJ_DEFINE_PROPERTY(cx, parent, id, rval, getter, setter,
6053                                           attrs, NULL);
6054    
6055              restore_scope:
6056                /* Restore fp->scopeChain now that obj is defined in fp->varobj. */
6057                fp->scopeChain = obj2;
6058                if (!ok) {
6059                    cx->weakRoots.newborn[GCX_OBJECT] = NULL;
6060                    goto error;
6061                }
6062              }
6063              END_CASE(JSOP_DEFFUN)
6064    
6065              BEGIN_CASE(JSOP_DEFFUN_FC)
6066              BEGIN_CASE(JSOP_DEFFUN_DBGFC)
6067                LOAD_FUNCTION(0);
6068    
6069                obj = (op == JSOP_DEFFUN_FC)
6070                      ? js_NewFlatClosure(cx, fun)
6071                      : js_NewDebuggableFlatClosure(cx, fun);
6072                if (!obj)
6073                    goto error;
6074                rval = OBJECT_TO_JSVAL(obj);
6075    
6076                attrs = (fp->flags & JSFRAME_EVAL)
6077                        ? JSPROP_ENUMERATE
6078                        : JSPROP_ENUMERATE | JSPROP_PERMANENT;
6079    
6080                flags = JSFUN_GSFLAG2ATTR(fun->flags);
6081                if (flags) {
6082                    attrs |= flags | JSPROP_SHARED;
6083                    rval = JSVAL_VOID;
6084                }
6085    
6086                parent = fp->varobj;
6087                JS_ASSERT(parent);
6088    
6089                id = ATOM_TO_JSID(fun->atom);
6090              ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);              ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
6091              if (ok) {              if (ok) {
6092                  if (attrs == JSPROP_ENUMERATE) {                  if (attrs == JSPROP_ENUMERATE) {
# Line 5918  Line 6107 
6107                  }                  }
6108              }              }
6109    
             /* Restore fp->scopeChain now that obj is defined in fp->varobj. */  
             MUST_FLOW_LABEL(restore)  
             fp->scopeChain = obj2;  
6110              if (!ok) {              if (!ok) {
6111                  cx->weakRoots.newborn[GCX_OBJECT] = NULL;                  cx->weakRoots.newborn[GCX_OBJECT] = NULL;
6112                  goto error;                  goto error;
6113              }              }
6114            END_CASE(JSOP_DEFFUN)            END_CASE(JSOP_DEFFUN_FC)
6115    
6116            BEGIN_CASE(JSOP_DEFLOCALFUN)            BEGIN_CASE(JSOP_DEFLOCALFUN)
             LOAD_FUNCTION(SLOTNO_LEN);  
   
6117              /*              /*
6118               * Define a local function (i.e., one nested at the top level of               * Define a local function (i.e., one nested at the top level of
6119               * another function), parented by the current scope chain, and               * another function), parented by the current scope chain, stored
6120               * stored in a local variable slot that the compiler allocated.               * in a local variable slot that the compiler allocated.  This is
6121               * This is an optimization over JSOP_DEFFUN that avoids requiring               * an optimization over JSOP_DEFFUN that avoids requiring a call
6122               * a call object for the outer function's activation.               * object for the outer function's activation.
6123               */               */
6124              slot = GET_SLOTNO(regs.pc);              LOAD_FUNCTION(SLOTNO_LEN);
6125                JS_ASSERT(FUN_INTERPRETED(fun));
6126              parent = js_GetScopeChain(cx, fp);              JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
             if (!parent)  
                 goto error;  
   
6127              obj = FUN_OBJECT(fun);              obj = FUN_OBJECT(fun);
6128              if (OBJ_GET_PARENT(cx, obj) != parent) {  
6129                  obj = js_CloneFunctionObject(cx, fun, parent);              if (FUN_NULL_CLOSURE(fun)) {
6130                    obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
6131                  if (!obj)                  if (!obj)
6132                      goto error;                      goto error;
6133                } else {
6134                    parent = js_GetScopeChain(cx, fp);
6135                    if (!parent)
6136                        goto error;
6137    
6138                    if (OBJ_GET_PARENT(cx, obj) != parent) {
6139    #ifdef JS_TRACER
6140                        if (TRACE_RECORDER(cx))
6141                            js_AbortRecording(cx, "DEFLOCALFUN for closure");
6142    #endif
6143                        obj = js_CloneFunctionObject(cx, fun, parent);
6144                        if (!obj)
6145                            goto error;
6146                    }
6147              }              }
6148    
6149                slot = GET_SLOTNO(regs.pc);
6150              TRACE_2(DefLocalFunSetSlot, slot, obj);              TRACE_2(DefLocalFunSetSlot, slot, obj);
6151                
6152              fp->slots[slot] = OBJECT_TO_JSVAL(obj);              fp->slots[slot] = OBJECT_TO_JSVAL(obj);
6153            END_CASE(JSOP_DEFLOCALFUN)            END_CASE(JSOP_DEFLOCALFUN)
6154    
6155            BEGIN_CASE(JSOP_ANONFUNOBJ)            BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
6156              /* Load the specified function object literal. */              LOAD_FUNCTION(SLOTNO_LEN);
             LOAD_FUNCTION(0);  
6157    
6158              /* If re-parenting, push a clone of the function object. */              obj = js_NewFlatClosure(cx, fun);
6159              parent = js_GetScopeChain(cx, fp);              if (!obj)
             if (!parent)  
6160                  goto error;                  goto error;
6161    
6162                slot = GET_SLOTNO(regs.pc);
6163                TRACE_2(DefLocalFunSetSlot, slot, obj);
6164    
6165                fp->slots[slot] = OBJECT_TO_JSVAL(obj);
6166              END_CASE(JSOP_DEFLOCALFUN_FC)
6167    
6168              BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
6169                LOAD_FUNCTION(SLOTNO_LEN);
6170    
6171                obj = js_NewDebuggableFlatClosure(cx, fun);
6172                if (!obj)
6173                    goto error;
6174    
6175                slot = GET_SLOTNO(regs.pc);
6176                fp->slots[slot] = OBJECT_TO_JSVAL(obj);
6177              END_CASE(JSOP_DEFLOCALFUN_DBGFC)
6178    
6179              BEGIN_CASE(JSOP_LAMBDA)
6180                /* Load the specified function object literal. */
6181                LOAD_FUNCTION(0);
6182              obj = FUN_OBJECT(fun);              obj = FUN_OBJECT(fun);
6183              if (OBJ_GET_PARENT(cx, obj) != parent) {  
6184                  obj = js_CloneFunctionObject(cx, fun, parent);              if (FUN_NULL_CLOSURE(fun)) {
6185                    obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
6186                  if (!obj)                  if (!obj)
6187                      goto error;                      goto error;
6188                } else {
6189                    parent = js_GetScopeChain(cx, fp);
6190                    if (!parent)
6191                        goto error;
6192    
6193                    /* If re-parenting, push a clone of the function object. */
6194                    if (OBJ_GET_PARENT(cx, obj) != parent) {
6195                        obj = js_CloneFunctionObject(cx, fun, parent);
6196                        if (!obj)
6197                            goto error;
6198                    }
6199              }              }
6200    
6201              PUSH_OPND(OBJECT_TO_JSVAL(obj));              PUSH_OPND(OBJECT_TO_JSVAL(obj));
6202            END_CASE(JSOP_ANONFUNOBJ)            END_CASE(JSOP_LAMBDA)
6203    
6204            BEGIN_CASE(JSOP_NAMEDFUNOBJ)            BEGIN_CASE(JSOP_LAMBDA_FC)
6205              LOAD_FUNCTION(0);              LOAD_FUNCTION(0);
6206    
6207              /*              obj = js_NewFlatClosure(cx, fun);
              * ECMA ed. 3 FunctionExpression: function Identifier [etc.].  
              *  
              * 1. Create a new object as if by the expression new Object().  
              * 2. Add Result(1) to the front of the scope chain.  
              *  
              * Step 2 is achieved by making the new object's parent be the  
              * current scope chain, and then making the new object the parent  
              * of the Function object clone.  
              */  
             obj2 = js_GetScopeChain(cx, fp);  
             if (!obj2)  
                 goto error;  
             parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2, 0);  
             if (!parent)  
                 goto error;  
   
             /*  
              * 3. Create a new Function object as specified in section 13.2  
              * with [parameters and body specified by the function expression  
              * that was parsed by the compiler into a Function object, and  
              * saved in the script's atom map].  
              *  
              * Protect parent from the GC.  
              */  
             fp->scopeChain = parent;  
             obj = js_CloneFunctionObject(cx, fun, parent);  
6208              if (!obj)              if (!obj)
6209                  goto error;                  goto error;
6210    
6211              /*              PUSH_OPND(OBJECT_TO_JSVAL(obj));
6212               * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY.  All            END_CASE(JSOP_LAMBDA_FC)
              * paths from here must flow through the "Restore fp->scopeChain"  
              * code below the OBJ_DEFINE_PROPERTY call.  
              */  
             MUST_FLOW_THROUGH("restore2");  
             fp->scopeChain = obj;  
             rval = OBJECT_TO_JSVAL(obj);  
6213    
6214              /*            BEGIN_CASE(JSOP_LAMBDA_DBGFC)
6215               * 4. Create a property in the object Result(1).  The property's              LOAD_FUNCTION(0);
              * name is [fun->atom, the identifier parsed by the compiler],  
              * value is Result(3), and attributes are { DontDelete, ReadOnly }.  
              */  
             attrs = JSFUN_GSFLAG2ATTR(fun->flags);  
             if (attrs) {  
                 attrs |= JSPROP_SHARED;  
                 rval = JSVAL_VOID;  
             }  
             ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval,  
                                      (attrs & JSPROP_GETTER)  
                                      ? JS_EXTENSION (JSPropertyOp) obj  
                                      : JS_PropertyStub,  
                                      (attrs & JSPROP_SETTER)  
                                      ? JS_EXTENSION (JSPropertyOp) obj  
                                      : JS_PropertyStub,  
                                      attrs |  
                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |  
                                      JSPROP_READONLY,  
                                      NULL);  
6216    
6217              /* Restore fp->scopeChain now that obj is defined in parent. */              obj = js_NewDebuggableFlatClosure(cx, fun);
6218              MUST_FLOW_LABEL(restore2)              if (!obj)
             fp->scopeChain = obj2;  
             if (!ok) {  
                 cx->weakRoots.newborn[GCX_OBJECT] = NULL;  
6219                  goto error;                  goto error;
             }  
6220    
             /*  
              * 5. Remove Result(1) from the front of the scope chain [no-op].  
              * 6. Return Result(3).  
              */  
6221              PUSH_OPND(OBJECT_TO_JSVAL(obj));              PUSH_OPND(OBJECT_TO_JSVAL(obj));
6222            END_CASE(JSOP_NAMEDFUNOBJ)            END_CASE(JSOP_LAMBDA_DBGFC)
6223    
6224              BEGIN_CASE(JSOP_CALLEE)
6225                PUSH_OPND(OBJECT_TO_JSVAL(fp->callee));
6226              END_CASE(JSOP_CALLEE)
6227    
6228  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
6229            BEGIN_CASE(JSOP_GETTER)            BEGIN_CASE(JSOP_GETTER)
# Line 6157  Line 6331 
6331            END_CASE(JSOP_HOLE)            END_CASE(JSOP_HOLE)
6332    
6333            BEGIN_CASE(JSOP_NEWARRAY)            BEGIN_CASE(JSOP_NEWARRAY)
6334              len = GET_UINT24(regs.pc);              len = GET_UINT16(regs.pc);
6335              JS_ASSERT(len <= regs.sp - StackBase(fp));              cx->fp->assertValidStackDepth(len);
6336              obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);              obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
6337              if (!obj)              if (!obj)
6338                  goto error;                  goto error;
# Line 6176  Line 6350 
6350                  goto error;                  goto error;
6351              PUSH_OPND(OBJECT_TO_JSVAL(obj));              PUSH_OPND(OBJECT_TO_JSVAL(obj));
6352              fp->sharpDepth++;              fp->sharpDepth++;
6353              LOAD_INTERRUPT_HANDLER(cx);              CHECK_INTERRUPT_HANDLER();
6354            END_CASE(JSOP_NEWINIT)            END_CASE(JSOP_NEWINIT)
6355    
6356            BEGIN_CASE(JSOP_ENDINIT)            BEGIN_CASE(JSOP_ENDINIT)
# Line 6215  Line 6389 
6389                  kshape = scope->shape;                  kshape = scope->shape;
6390                  cache = &JS_PROPERTY_CACHE(cx);                  cache = &JS_PROPERTY_CACHE(cx);
6391                  entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];                  entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
6392                    PCMETER(cache->pctestentry = entry);
6393                  PCMETER(cache->tests++);                  PCMETER(cache->tests++);
6394                  PCMETER(cache->initests++);                  PCMETER(cache->initests++);
6395    
6396                  if (entry->kpc == regs.pc && entry->kshape == kshape) {                  if (entry->kpc == regs.pc &&
6397                        entry->kshape == kshape &&
6398                        PCVCAP_SHAPE(entry->vcap) == rt->protoHazardShape) {
6399                        JS_ASSERT(PCVCAP_TAG(entry->vcap) == 0);
6400    
6401                      PCMETER(cache->pchits++);                      PCMETER(cache->pchits++);
6402                      PCMETER(cache->inipchits++);                      PCMETER(cache->inipchits++);
6403    
# Line 6251  Line 6430 
6430                      if (sprop->parent != scope->lastProp)                      if (sprop->parent != scope->lastProp)
6431                          goto do_initprop_miss;                          goto do_initprop_miss;
6432    
                     TRACE_2(SetPropHit, entry, sprop);  
   
6433                      /*                      /*
6434                       * Otherwise this entry must be for a direct property of                       * Otherwise this entry must be for a direct property of
6435                       * obj, not a proto-property, and there cannot have been                       * obj, not a proto-property, and there cannot have been
6436                       * any deletions of prior properties.                       * any deletions of prior properties.
6437                       */                       */
                     JS_ASSERT(PCVCAP_MAKE(sprop->shape, 0, 0) == entry->vcap);  
6438                      JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope));                      JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope));
6439                      JS_ASSERT(!scope->table ||                      JS_ASSERT(!scope->table ||
6440                                !SCOPE_HAS_PROPERTY(scope, sprop));                                !SCOPE_HAS_PROPERTY(scope, sprop));
6441    
6442                      slot = sprop->slot;                      slot = sprop->slot;
6443                      JS_ASSERT(slot == scope->map.freeslot);                      JS_ASSERT(slot == scope->freeslot);
6444                      if (slot < STOBJ_NSLOTS(obj)) {                      if (slot < STOBJ_NSLOTS(obj)) {
6445                          ++scope->map.freeslot;                          ++scope->freeslot;
6446                      } else {                      } else {
6447                          if (!js_AllocSlot(cx, obj, &slot)) {                          if (!js_AllocSlot(cx, obj, &slot)) {
6448                              JS_UNLOCK_SCOPE(cx, scope);                              JS_UNLOCK_SCOPE(cx, scope);
# Line 6290  Line 6466 
6466                          }                          }
6467                          JS_ASSERT(sprop2 == sprop);                          JS_ASSERT(sprop2 == sprop);
6468                      } else {                      } else {
6469                            js_LeaveTraceIfGlobalObject(cx, scope->object);
6470                          scope->shape = sprop->shape;                          scope->shape = sprop->shape;
6471                          ++scope->entryCount;                          ++scope->entryCount;
6472                          scope->lastProp = sprop;                          scope->lastProp = sprop;
# Line 6298  Line 6475 
6475                      GC_WRITE_BARRIER(cx, scope,                      GC_WRITE_BARRIER(cx, scope,
6476                                       LOCKED_OBJ_GET_SLOT(obj, slot),                                       LOCKED_OBJ_GET_SLOT(obj, slot),
6477                                       rval);                                       rval);
6478                        TRACE_2(SetPropHit, entry, sprop);
6479                      LOCKED_OBJ_SET_SLOT(obj, slot, rval);                      LOCKED_OBJ_SET_SLOT(obj, slot, rval);
6480                      JS_UNLOCK_SCOPE(cx, scope);                      JS_UNLOCK_SCOPE(cx, scope);
6481                      break;                      break;
# Line 6316  Line 6494 
6494                                             NULL, NULL)) {                                             NULL, NULL)) {
6495                      goto error;                      goto error;
6496                  }                  }
6497                  if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry))  
6498                    if (!(JS_UNLIKELY(atom == cx->runtime->atomState.protoAtom)
6499                          ? js_SetPropertyHelper(cx, obj, id, true, &rval)
6500                          : js_DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
6501                                                    JSPROP_ENUMERATE, 0, 0, NULL,
6502                                                    JSDNP_CACHE_RESULT)))
6503                      goto error;                      goto error;
 #ifdef JS_TRACER  
                 if (entry)  
                     TRACE_1(SetPropMiss, entry);  
 #endif  
6504              } while (0);              } while (0);
6505    
6506              /* Common tail for property cache hit and miss cases. */              /* Common tail for property cache hit and miss cases. */
# Line 6345  Line 6524 
6524               * Check for property redeclaration strict warning (we may be in               * Check for property redeclaration strict warning (we may be in
6525               * an object initialiser, not an array initialiser).               * an object initialiser, not an array initialiser).
6526               */               */
6527              if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL,              if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL, NULL))
                                        NULL)) {  
6528                  goto error;                  goto error;
             }  
6529    
6530              /*              /*
6531               * If rval is a hole, do not call OBJ_SET_PROPERTY. In this case,               * If rval is a hole, do not call OBJ_DEFINE_PROPERTY. In this case,
6532               * obj must be an array, so if the current op is the last element               * obj must be an array, so if the current op is the last element
6533               * initialiser, set the array length to one greater than id.               * initialiser, set the array length to one greater than id.
6534               */               */
# Line 6359  Line 6536 
6536                  JS_ASSERT(OBJ_IS_ARRAY(cx, obj));                  JS_ASSERT(OBJ_IS_ARRAY(cx, obj));
6537                  JS_ASSERT(JSID_IS_INT(id));                  JS_ASSERT(JSID_IS_INT(id));
6538                  JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT);                  JS_ASSERT((jsuint) JSID_TO_INT(id) < ARRAY_INIT_LIMIT);
6539                  if ((JSOp) regs.pc[JSOP_INITELEM_LENGTH] == JSOP_ENDINIT &&                  if (js_GetOpcode(cx, script, regs.pc + JSOP_INITELEM_LENGTH) == JSOP_ENDINIT &&
6540                      !js_SetLengthProperty(cx, obj,                      !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
                                           (jsuint) (JSID_TO_INT(id) + 1))) {  
6541                      goto error;                      goto error;
6542                  }                  }
6543              } else {              } else {
6544                  if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))                  if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_ENUMERATE, NULL))
6545                      goto error;                      goto error;
6546              }              }
6547              regs.sp -= 2;              regs.sp -= 2;
# Line 6455  Line 6631 
6631              JS_ASSERT(cx->throwing);              JS_ASSERT(cx->throwing);
6632              PUSH(cx->exception);              PUSH(cx->exception);
6633              cx->throwing = JS_FALSE;              cx->throwing = JS_FALSE;
6634                CHECK_BRANCH();
6635            END_CASE(JSOP_EXCEPTION)            END_CASE(JSOP_EXCEPTION)
6636    
6637              BEGIN_CASE(JSOP_FINALLY)
6638                CHECK_BRANCH();
6639              END_CASE(JSOP_FINALLY)
6640    
6641            BEGIN_CASE(JSOP_THROWING)            BEGIN_CASE(JSOP_THROWING)
6642              JS_ASSERT(!cx->throwing);              JS_ASSERT(!cx->throwing);
6643              cx->throwing = JS_TRUE;              cx->throwing = JS_TRUE;
# Line 6465  Line 6646 
6646    
6647            BEGIN_CASE(JSOP_THROW)            BEGIN_CASE(JSOP_THROW)
6648              JS_ASSERT(!cx->throwing);              JS_ASSERT(!cx->throwing);
6649                CHECK_BRANCH();
6650              cx->throwing = JS_TRUE;              cx->throwing = JS_TRUE;
6651              cx->exception = POP_OPND();              cx->exception = POP_OPND();
6652              /* let the code at error try to catch the exception. */              /* let the code at error try to catch the exception. */
# Line 6481  Line 6663 
6663              fp->slots[slot] = POP_OPND();              fp->slots[slot] = POP_OPND();
6664            END_CASE(JSOP_SETLOCALPOP)            END_CASE(JSOP_SETLOCALPOP)
6665    
6666              BEGIN_CASE(JSOP_IFPRIMTOP)
6667                /*
6668                 * If the top of stack is of primitive type, jump to our target.
6669                 * Otherwise advance to the next opcode.
6670                 */
6671                JS_ASSERT(regs.sp > StackBase(fp));
6672                rval = FETCH_OPND(-1);
6673                if (JSVAL_IS_PRIMITIVE(rval)) {
6674                    len = GET_JUMP_OFFSET(regs.pc);
6675                    BRANCH(len);
6676                }
6677              END_CASE(JSOP_IFPRIMTOP)
6678    
6679              BEGIN_CASE(JSOP_PRIMTOP)
6680                JS_ASSERT(regs.sp > StackBase(fp));
6681                lval = FETCH_OPND(-1);
6682                i = GET_INT8(regs.pc);
6683                if (!JSVAL_IS_PRIMITIVE(lval)) {
6684                    lval = FETCH_OPND(-2);
6685                    js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
6686                                         -2, lval, NULL,
6687                                         (i == JSTYPE_VOID)
6688                                         ? "primitive type"
6689                                         : JS_TYPE_STR(i));
6690                    goto error;
6691                }
6692              END_CASE(JSOP_PRIMTOP)
6693    
6694              BEGIN_CASE(JSOP_OBJTOP)
6695                lval = FETCH_OPND(-1);
6696                if (JSVAL_IS_PRIMITIVE(lval)) {
6697                    js_ReportValueError(cx, GET_UINT16(regs.pc), -1, lval, NULL);
6698                    goto error;
6699                }
6700              END_CASE(JSOP_OBJTOP)
6701    
6702            BEGIN_CASE(JSOP_INSTANCEOF)            BEGIN_CASE(JSOP_INSTANCEOF)
6703              rval = FETCH_OPND(-1);              rval = FETCH_OPND(-1);
6704              if (JSVAL_IS_PRIMITIVE(rval) ||              if (JSVAL_IS_PRIMITIVE(rval) ||
# Line 6518  Line 6736 
6736                      goto error;                      goto error;
6737                    default:;                    default:;
6738                  }                  }
6739                  LOAD_INTERRUPT_HANDLER(cx);                  CHECK_INTERRUPT_HANDLER();
6740              }              }
6741            }            }
6742            END_CASE(JSOP_DEBUGGER)            END_CASE(JSOP_DEBUGGER)
# Line 6771  Line 6989 
6989                  regs.sp++;                  regs.sp++;
6990              }              }
6991    
6992    #ifdef DEBUG
6993                JS_ASSERT(fp->blockChain == OBJ_GET_PARENT(cx, obj));
6994    
6995              /*              /*
6996               * If this frame had to reflect the compile-time block chain into               * The young end of fp->scopeChain may omit blocks if we
6997               * the runtime scope chain, we can't optimize block scopes out of               * haven't closed over them, but if there are any closure
6998               * runtime any longer, because an outer block that parents obj has               * blocks on fp->scopeChain, they'd better be (clones of)
6999               * been cloned onto the scope chain.  To avoid re-cloning such a               * ancestors of the block we're entering now; anything
7000               * parent and accumulating redundant clones via js_GetScopeChain,               * else we should have popped off fp->scopeChain when we
7001               * we must clone each block eagerly on entry, and push it on the               * left its static scope.
7002               * scope chain, until this frame pops.               */
7003               */              obj2 = fp->scopeChain;
7004              if (fp->flags & JSFRAME_POP_BLOCKS) {              while ((clasp = OBJ_GET_CLASS(cx, obj2)) == &js_WithClass)
7005                  JS_ASSERT(!fp->blockChain);                  obj2 = OBJ_GET_PARENT(cx, obj2);
7006                  obj = js_CloneBlockObject(cx, obj, fp->scopeChain, fp);              if (clasp == &js_BlockClass &&
7007                  if (!obj)                  OBJ_GET_PRIVATE(cx, obj2) == fp) {
7008                      goto error;                  JSObject *youngestProto = OBJ_GET_PROTO(cx, obj2);
7009                  fp->scopeChain = obj;                  JS_ASSERT(!OBJ_IS_CLONED_BLOCK(youngestProto));
7010              } else {                  parent = obj;
7011                  JS_ASSERT(!fp->blockChain ||                  while ((parent = OBJ_GET_PARENT(cx, parent)) != youngestProto)
7012                            OBJ_GET_PARENT(cx, obj) == fp->blockChain);                      JS_ASSERT(parent);
                 fp->blockChain = obj;  
7013              }              }
7014    #endif
7015    
7016                fp->blockChain = obj;
7017            END_CASE(JSOP_ENTERBLOCK)            END_CASE(JSOP_ENTERBLOCK)
7018    
7019            BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)            BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
7020            BEGIN_CASE(JSOP_LEAVEBLOCK)            BEGIN_CASE(JSOP_LEAVEBLOCK)
7021            {            {
7022  #ifdef DEBUG  #ifdef DEBUG
7023               uintN blockDepth = OBJ_BLOCK_DEPTH(cx,              JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
7024                                                  fp->blockChain              uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
7025                                                  ? fp->blockChain  
7026                                                  : fp->scopeChain);              JS_ASSERT(blockDepth <= StackDepth(script));
7027    #endif
7028               JS_ASSERT(blockDepth <= StackDepth(script));              /*
7029  #endif               * If we're about to leave the dynamic scope of a block that has
7030              if (fp->blockChain) {               * been cloned onto fp->scopeChain, clear its private data, move
7031                  JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);               * its locals from the stack into the clone, and pop it off the
7032                  fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);               * chain.
7033              } else {               */
7034                  /*              obj = fp->scopeChain;
7035                   * This block was cloned into fp->scopeChain, so clear its              if (OBJ_GET_PROTO(cx, obj) == fp->blockChain) {
7036                   * private data and sync its locals to their property slots.                  JS_ASSERT (OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
                  */  
7037                  if (!js_PutBlockObject(cx, JS_TRUE))                  if (!js_PutBlockObject(cx, JS_TRUE))
7038                      goto error;                      goto error;
7039              }              }
7040    
7041                /* Pop the block chain, too.  */
7042                fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);
7043    
7044              /*              /*
7045               * We will move the result of the expression to the new topmost               * We will move the result of the expression to the new topmost
7046               * stack slot.               * stack slot.
# Line 6832  Line 7057 
7057            }            }
7058            END_CASE(JSOP_LEAVEBLOCK)            END_CASE(JSOP_LEAVEBLOCK)
7059    
7060              BEGIN_CASE(JSOP_CALLBUILTIN)
7061    #ifdef JS_TRACER
7062                  obj = js_GetBuiltinFunction(cx, GET_INDEX(regs.pc));
7063                  if (!obj)
7064                      goto error;
7065                  rval = FETCH_OPND(-1);
7066                  PUSH_OPND(rval);
7067                  STORE_OPND(-2, OBJECT_TO_JSVAL(obj));
7068    #else
7069                  goto bad_opcode;  /* This is an imacro-only opcode. */
7070    #endif
7071              END_CASE(JSOP_CALLBUILTIN)
7072    
7073  #if JS_HAS_GENERATORS  #if JS_HAS_GENERATORS
7074            BEGIN_CASE(JSOP_GENERATOR)            BEGIN_CASE(JSOP_GENERATOR)
7075              ASSERT_NOT_THROWING(cx);              ASSERT_NOT_THROWING(cx);
# Line 6865  Line 7103 
7103              JS_ASSERT(slot < script->nslots);              JS_ASSERT(slot < script->nslots);
7104              lval = fp->slots[slot];              lval = fp->slots[slot];
7105              obj  = JSVAL_TO_OBJECT(lval);              obj  = JSVAL_TO_OBJECT(lval);
             JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);  
7106              rval = FETCH_OPND(-1);              rval = FETCH_OPND(-1);
7107                if (!js_ArrayCompPush(cx, obj, rval))
             /*  
              * We know that the array is created with only a 'length' private  
              * data slot at JSSLOT_ARRAY_LENGTH, and that previous iterations  
              * of the comprehension have added the only properties directly in  
              * the array object.  
              */  
             i = obj->fslots[JSSLOT_ARRAY_LENGTH];  
             if (i == ARRAY_INIT_LIMIT) {  
                 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,  
                                        JSMSG_ARRAY_INIT_TOO_BIG);  
                 goto error;  
             }  
             id = INT_TO_JSID(i);  
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))  
7108                  goto error;                  goto error;
7109              regs.sp--;              regs.sp--;
7110            END_CASE(JSOP_ARRAYPUSH)            END_CASE(JSOP_ARRAYPUSH)
7111  #endif /* JS_HAS_GENERATORS */  #endif /* JS_HAS_GENERATORS */
7112    
7113              BEGIN_CASE(JSOP_LOOP)
7114              END_CASE(JSOP_LOOP)
7115    
7116  #if JS_THREADED_INTERP  #if JS_THREADED_INTERP
7117            L_JSOP_BACKPATCH:            L_JSOP_BACKPATCH:
7118            L_JSOP_BACKPATCH_POP:            L_JSOP_BACKPATCH_POP:
# Line 6897  Line 7123 
7123            L_JSOP_ARRAYPUSH:            L_JSOP_ARRAYPUSH:
7124  # endif  # endif
7125    
7126    # if !JS_HAS_SHARP_VARS
7127              L_JSOP_DEFSHARP:
7128              L_JSOP_USESHARP:
7129    # endif
7130    
7131  # if !JS_HAS_DESTRUCTURING  # if !JS_HAS_DESTRUCTURING
7132            L_JSOP_ENUMCONSTELEM:            L_JSOP_ENUMCONSTELEM:
7133  # endif  # endif
# Line 6932  Line 7163 
7163            L_JSOP_DEFXMLNS:            L_JSOP_DEFXMLNS:
7164  # endif  # endif
7165    
           L_JSOP_UNUSED131:  
           L_JSOP_UNUSED201:  
           L_JSOP_UNUSED202:  
           L_JSOP_UNUSED203:  
           L_JSOP_UNUSED204:  
           L_JSOP_UNUSED205:  
           L_JSOP_UNUSED206:  
           L_JSOP_UNUSED207:  
           L_JSOP_UNUSED208:  
           L_JSOP_UNUSED209:  
           L_JSOP_UNUSED219:  
           L_JSOP_UNUSED226:  
   
7166  #else /* !JS_THREADED_INTERP */  #else /* !JS_THREADED_INTERP */
7167            default:            default:
7168  #endif  #endif
7169    #ifndef JS_TRACER
7170            bad_opcode:
7171    #endif
7172            {            {
7173              char numBuf[12];              char numBuf[12];
7174              JS_snprintf(numBuf, sizeof numBuf, "%d", op);              JS_snprintf(numBuf, sizeof numBuf, "%d", op);
# Line 6956  Line 7177 
7177              goto error;              goto error;
7178            }            }
7179    
 #ifdef JS_TRACER  
   
 #if JS_THREADED_INTERP  
 # define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \  
     R_##x: RECORD(x); goto L_##x;  
 #else  
 # define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \  
     case 256 + x: RECORD(x); op = x; switchOp = x; goto do_switch;  
 #endif  
 #include "jsopcode.tbl"  
 #undef OPDEF  
   
 #endif /* JS_TRACER */  
   
7180  #if !JS_THREADED_INTERP  #if !JS_THREADED_INTERP
   
7181          } /* switch (op) */          } /* switch (op) */
7182      }      } /* for (;;) */
7183  #endif /* !JS_THREADED_INTERP */  #endif /* !JS_THREADED_INTERP */
7184