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

Diff of /trunk/js/jsscope.cpp

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

revision 460 by siliconforks, Sat Sep 26 23:15:22 2009 UTC revision 507 by siliconforks, Sun Jan 10 07:23:34 2010 UTC
# Line 1  Line 1 
1  /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-  /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2   * vim: set ts=8 sw=4 et tw=78:   * vim: set ts=8 sw=4 et tw=78:
3   *   *
4   * ***** BEGIN LICENSE BLOCK *****   * ***** BEGIN LICENSE BLOCK *****
# Line 41  Line 41 
41  /*  /*
42   * JS symbol tables.   * JS symbol tables.
43   */   */
44  #include "jsstddef.h"  #include <new>
45  #include <stdlib.h>  #include <stdlib.h>
46  #include <string.h>  #include <string.h>
47  #include "jstypes.h"  #include "jstypes.h"
48    #include "jsstdint.h"
49  #include "jsarena.h"  #include "jsarena.h"
50  #include "jsbit.h"  #include "jsbit.h"
51  #include "jsclist.h"  #include "jsclist.h"
# Line 58  Line 59 
59  #include "jsnum.h"  #include "jsnum.h"
60  #include "jsscope.h"  #include "jsscope.h"
61  #include "jsstr.h"  #include "jsstr.h"
62  #include "jsarray.h"  #include "jstracer.h"
63    
64    uint32
65    js_GenerateShape(JSContext *cx, bool gcLocked)
66    {
67        JSRuntime *rt;
68        uint32 shape;
69    
70        rt = cx->runtime;
71        shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
72        JS_ASSERT(shape != 0);
73        if (shape >= SHAPE_OVERFLOW_BIT) {
74            /*
75             * FIXME bug 440834: The shape id space has overflowed. Currently we
76             * cope badly with this and schedule the GC on the every call. But
77             * first we make sure that increments from other threads would not
78             * have a chance to wrap around shapeGen to zero.
79             */
80            rt->shapeGen = SHAPE_OVERFLOW_BIT;
81            shape = SHAPE_OVERFLOW_BIT;
82            js_TriggerGC(cx, gcLocked);
83        }
84        return shape;
85    }
86    
87  JSScope *  JSScope *
88  js_GetMutableScope(JSContext *cx, JSObject *obj)  js_GetMutableScope(JSContext *cx, JSObject *obj)
# Line 69  Line 93 
93    
94      scope = OBJ_SCOPE(obj);      scope = OBJ_SCOPE(obj);
95      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
96      if (scope->object == obj)      if (scope->owned())
97          return scope;          return scope;
98    
99      /*      /*
# Line 77  Line 101 
101       * birth, and runtime clone of a block objects are never mutated.       * birth, and runtime clone of a block objects are never mutated.
102       */       */
103      JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass);      JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass);
104      newscope = js_NewScope(cx, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), obj);      newscope = JSScope::create(cx, scope->ops, obj->getClass(), obj, scope->shape);
105      if (!newscope)      if (!newscope)
106          return NULL;          return NULL;
107      JS_LOCK_SCOPE(cx, newscope);      JS_LOCK_SCOPE(cx, newscope);
108      obj->map = &newscope->map;      obj->map = newscope;
109    
110      JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));      JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj)));
111      clasp = STOBJ_GET_CLASS(obj);      clasp = STOBJ_GET_CLASS(obj);
# Line 93  Line 117 
117              newscope->freeslot = freeslot;              newscope->freeslot = freeslot;
118      }      }
119      JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);      JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
120      js_DropScope(cx, scope, obj);      JS_ATOMIC_DECREMENT(&scope->nrefs);
121        if (scope->nrefs == 0)
122            JSScope::destroy(cx, scope);
123      return newscope;      return newscope;
124  }  }
125    
# Line 107  Line 133 
133  #define MIN_SCOPE_SIZE          JS_BIT(MIN_SCOPE_SIZE_LOG2)  #define MIN_SCOPE_SIZE          JS_BIT(MIN_SCOPE_SIZE_LOG2)
134  #define SCOPE_TABLE_NBYTES(n)   ((n) * sizeof(JSScopeProperty *))  #define SCOPE_TABLE_NBYTES(n)   ((n) * sizeof(JSScopeProperty *))
135    
136  static void  void
137  InitMinimalScope(JSContext *cx, JSScope *scope)  JSScope::initMinimal(JSContext *cx, uint32 newShape)
138  {  {
139      js_LeaveTraceIfGlobalObject(cx, scope->object);      shape = newShape;
140      scope->shape = 0;      emptyScope = NULL;
141      scope->hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;      hashShift = JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2;
142      scope->entryCount = scope->removedCount = 0;      entryCount = removedCount = 0;
143      scope->table = NULL;      table = NULL;
144      scope->lastProp = NULL;      lastProp = NULL;
145  }  }
146    
147  static JSBool  bool
148  CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report)  JSScope::createTable(JSContext *cx, bool report)
149  {  {
150      int sizeLog2;      int sizeLog2;
151      JSScopeProperty *sprop, **spp;      JSScopeProperty *sprop, **spp;
152    
153      JS_ASSERT(!scope->table);      JS_ASSERT(!table);
154      JS_ASSERT(scope->lastProp);      JS_ASSERT(lastProp);
155    
156      if (scope->entryCount > SCOPE_HASH_THRESHOLD) {      if (entryCount > SCOPE_HASH_THRESHOLD) {
157          /*          /*
158           * Either we're creating a table for a large scope that was populated           * Either we're creating a table for a large scope that was populated
159           * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or           * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
# Line 135  Line 161 
161           * event, let's try to grow, overallocating to hold at least twice the           * event, let's try to grow, overallocating to hold at least twice the
162           * current population.           * current population.
163           */           */
164          sizeLog2 = JS_CeilingLog2(2 * scope->entryCount);          sizeLog2 = JS_CeilingLog2(2 * entryCount);
165          scope->hashShift = JS_DHASH_BITS - sizeLog2;          hashShift = JS_DHASH_BITS - sizeLog2;
166      } else {      } else {
167          JS_ASSERT(scope->hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2);          JS_ASSERT(hashShift == JS_DHASH_BITS - MIN_SCOPE_SIZE_LOG2);
168          sizeLog2 = MIN_SCOPE_SIZE_LOG2;          sizeLog2 = MIN_SCOPE_SIZE_LOG2;
169      }      }
170    
171      scope->table = (JSScopeProperty **)      table = (JSScopeProperty **) js_calloc(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
172          calloc(JS_BIT(sizeLog2), sizeof(JSScopeProperty *));      if (!table) {
     if (!scope->table) {  
173          if (report)          if (report)
174              JS_ReportOutOfMemory(cx);              JS_ReportOutOfMemory(cx);
175          return JS_FALSE;          return false;
176      }      }
177      js_UpdateMallocCounter(cx, JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));      cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
178    
179      scope->hashShift = JS_DHASH_BITS - sizeLog2;      hashShift = JS_DHASH_BITS - sizeLog2;
180      for (sprop = scope->lastProp; sprop; sprop = sprop->parent) {      for (sprop = lastProp; sprop; sprop = sprop->parent) {
181          spp = js_SearchScope(scope, sprop->id, JS_TRUE);          spp = search(sprop->id, true);
182          SPROP_STORE_PRESERVING_COLLISION(spp, sprop);          SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
183      }      }
184      return JS_TRUE;      return true;
185  }  }
186    
187  JSScope *  JSScope *
188  js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj)  JSScope::create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp,
189                    JSObject *obj, uint32 shape)
190  {  {
191      JS_ASSERT(OPS_IS_NATIVE(ops));      JS_ASSERT(OPS_IS_NATIVE(ops));
192      JS_ASSERT(obj);      JS_ASSERT(obj);
193    
194      JSScope *scope = (JSScope *) JS_malloc(cx, sizeof(JSScope));      JSScope *scope = cx->create<JSScope>(ops, obj);
195      if (!scope)      if (!scope)
196          return NULL;          return NULL;
197    
     scope->map.ops = ops;  
     scope->object = obj;  
198      scope->nrefs = 1;      scope->nrefs = 1;
199      scope->freeslot = JSSLOT_FREE(clasp);      scope->freeslot = JSSLOT_FREE(clasp);
200      scope->flags = 0;      scope->flags = cx->runtime->gcRegenShapesScopeFlag;
201      InitMinimalScope(cx, scope);      js_LeaveTraceIfGlobalObject(cx, obj);
202        scope->initMinimal(cx, shape);
203    
204  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
205      js_InitTitle(cx, &scope->title);      js_InitTitle(cx, &scope->title);
# Line 184  Line 209 
209      return scope;      return scope;
210  }  }
211    
212    JSEmptyScope *
213    JSScope::createEmptyScope(JSContext *cx, JSClass *clasp)
214    {
215        JS_ASSERT(!emptyScope);
216    
217        JSEmptyScope *scope = cx->create<JSEmptyScope>(ops, clasp);
218        if (!scope)
219            return NULL;
220    
221        /*
222         * This scope holds a reference to the new empty scope. Our only caller,
223         * getEmptyScope, also promises to incref on behalf of its caller.
224         */
225        scope->nrefs = 2;
226        scope->freeslot = JSSLOT_FREE(clasp);
227        scope->flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag;
228        scope->initMinimal(cx, js_GenerateShape(cx, false));
229    
230    #ifdef JS_THREADSAFE
231        js_InitTitle(cx, &scope->title);
232    #endif
233        JS_RUNTIME_METER(cx->runtime, liveScopes);
234        JS_RUNTIME_METER(cx->runtime, totalScopes);
235        emptyScope = scope;
236        return scope;
237    }
238    
239  #if defined DEBUG || defined JS_DUMP_PROPTREE_STATS  #if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
240  # include "jsprf.h"  # include "jsprf.h"
241  # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)  # define LIVE_SCOPE_METER(cx,expr) JS_LOCK_RUNTIME_VOID(cx->runtime,expr)
# Line 192  Line 244 
244  #endif  #endif
245    
246  void  void
247  js_DestroyScope(JSContext *cx, JSScope *scope)  JSScope::destroy(JSContext *cx, JSScope *scope)
248  {  {
249  #ifdef JS_THREADSAFE  #ifdef JS_THREADSAFE
250      js_FinishTitle(cx, &scope->title);      js_FinishTitle(cx, &scope->title);
251  #endif  #endif
252      if (scope->table)      if (scope->table)
253          JS_free(cx, scope->table);          cx->free(scope->table);
254        if (scope->emptyScope)
255            scope->emptyScope->drop(cx, NULL);
256    
257      LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);      LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);
258      JS_RUNTIME_UNMETER(cx->runtime, liveScopes);      JS_RUNTIME_UNMETER(cx->runtime, liveScopes);
259      JS_free(cx, scope);      cx->free(scope);
 }  
   
 void  
 js_HoldScope(JSScope *scope)  
 {  
     JS_ASSERT(scope->nrefs >= 0);  
     JS_ATOMIC_INCREMENT(&scope->nrefs);  
 }  
   
 JSBool  
 js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj)  
 {  
     JS_ASSERT(scope->nrefs > 0);  
     JS_ATOMIC_DECREMENT(&scope->nrefs);  
   
     if (scope->nrefs == 0) {  
         js_DestroyScope(cx, scope);  
         return false;  
     }  
     if (scope->object == obj)  
         scope->object = NULL;  
     return true;  
260  }  }
261    
262  #ifdef JS_DUMP_PROPTREE_STATS  #ifdef JS_DUMP_PROPTREE_STATS
# Line 276  Line 308 
308  #define SCOPE_HASH1(hash0,shift)        ((hash0) >> (shift))  #define SCOPE_HASH1(hash0,shift)        ((hash0) >> (shift))
309  #define SCOPE_HASH2(hash0,log2,shift)   ((((hash0) << (log2)) >> (shift)) | 1)  #define SCOPE_HASH2(hash0,log2,shift)   ((((hash0) << (log2)) >> (shift)) | 1)
310    
311  JS_FRIEND_API(JSScopeProperty **)  JSScopeProperty **
312  js_SearchScope(JSScope *scope, jsid id, JSBool adding)  JSScope::searchTable(jsid id, bool adding)
313  {  {
314      JSHashNumber hash0, hash1, hash2;      JSHashNumber hash0, hash1, hash2;
315      int hashShift, sizeLog2;      int sizeLog2;
316      JSScopeProperty *stored, *sprop, **spp, **firstRemoved;      JSScopeProperty *stored, *sprop, **spp, **firstRemoved;
317      uint32 sizeMask;      uint32 sizeMask;
318    
319      METER(searches);      JS_ASSERT(table);
     if (!scope->table) {  
         /* Not enough properties to justify hashing: search from lastProp. */  
         JS_ASSERT(!SCOPE_HAD_MIDDLE_DELETE(scope));  
         for (spp = &scope->lastProp; (sprop = *spp); spp = &sprop->parent) {  
             if (sprop->id == id) {  
                 METER(hits);  
                 return spp;  
             }  
         }  
         METER(misses);  
         return spp;  
     }  
320    
321      /* Compute the primary hash address. */      /* Compute the primary hash address. */
322      METER(hashes);      METER(hashes);
323      hash0 = SCOPE_HASH0(id);      hash0 = SCOPE_HASH0(id);
     hashShift = scope->hashShift;  
324      hash1 = SCOPE_HASH1(hash0, hashShift);      hash1 = SCOPE_HASH1(hash0, hashShift);
325      spp = scope->table + hash1;      spp = table + hash1;
326    
327      /* Miss: return space for a new entry. */      /* Miss: return space for a new entry. */
328      stored = *spp;      stored = *spp;
# Line 337  Line 356 
356          METER(steps);          METER(steps);
357          hash1 -= hash2;          hash1 -= hash2;
358          hash1 &= sizeMask;          hash1 &= sizeMask;
359          spp = scope->table + hash1;          spp = table + hash1;
360    
361          stored = *spp;          stored = *spp;
362          if (SPROP_IS_FREE(stored)) {          if (SPROP_IS_FREE(stored)) {
# Line 364  Line 383 
383      return NULL;      return NULL;
384  }  }
385    
386  static JSBool  bool
387  ChangeScope(JSContext *cx, JSScope *scope, int change)  JSScope::changeTable(JSContext *cx, int change)
388  {  {
389      int oldlog2, newlog2;      int oldlog2, newlog2;
390      uint32 oldsize, newsize, nbytes;      uint32 oldsize, newsize, nbytes;
391      JSScopeProperty **table, **oldtable, **spp, **oldspp, *sprop;      JSScopeProperty **newtable, **oldtable, **spp, **oldspp, *sprop;
392    
393      if (!scope->table)      if (!table)
394          return CreateScopeTable(cx, scope, JS_TRUE);          return createTable(cx, true);
395    
396      /* Grow, shrink, or compress by changing scope->table. */      /* Grow, shrink, or compress by changing this->table. */
397      oldlog2 = JS_DHASH_BITS - scope->hashShift;      oldlog2 = JS_DHASH_BITS - hashShift;
398      newlog2 = oldlog2 + change;      newlog2 = oldlog2 + change;
399      oldsize = JS_BIT(oldlog2);      oldsize = JS_BIT(oldlog2);
400      newsize = JS_BIT(newlog2);      newsize = JS_BIT(newlog2);
401      nbytes = SCOPE_TABLE_NBYTES(newsize);      nbytes = SCOPE_TABLE_NBYTES(newsize);
402      table = (JSScopeProperty **) calloc(nbytes, 1);      newtable = (JSScopeProperty **) cx->calloc(nbytes);
403      if (!table) {      if (!newtable)
404          JS_ReportOutOfMemory(cx);          return false;
         return JS_FALSE;  
     }  
405    
406      /* Now that we have a new table allocated, update scope members. */      /* Now that we have newtable allocated, update members. */
407      scope->hashShift = JS_DHASH_BITS - newlog2;      hashShift = JS_DHASH_BITS - newlog2;
408      scope->removedCount = 0;      removedCount = 0;
409      oldtable = scope->table;      oldtable = table;
410      scope->table = table;      table = newtable;
411    
412      /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */      /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */
413      cx->runtime->gcMallocBytes += nbytes;      cx->runtime->gcMallocBytes += nbytes;
# Line 399  Line 416 
416      for (oldspp = oldtable; oldsize != 0; oldspp++) {      for (oldspp = oldtable; oldsize != 0; oldspp++) {
417          sprop = SPROP_FETCH(oldspp);          sprop = SPROP_FETCH(oldspp);
418          if (sprop) {          if (sprop) {
419              spp = js_SearchScope(scope, sprop->id, JS_TRUE);              spp = search(sprop->id, true);
420              JS_ASSERT(SPROP_IS_FREE(*spp));              JS_ASSERT(SPROP_IS_FREE(*spp));
421              *spp = sprop;              *spp = sprop;
422          }          }
# Line 407  Line 424 
424      }      }
425    
426      /* Finally, free the old table storage. */      /* Finally, free the old table storage. */
427      JS_free(cx, oldtable);      cx->free(oldtable);
428      return JS_TRUE;      return true;
429  }  }
430    
431  /*  /*
# Line 557  Line 574 
574  {  {
575      PropTreeKidsChunk *chunk;      PropTreeKidsChunk *chunk;
576    
577      chunk = (PropTreeKidsChunk *) calloc(1, sizeof *chunk);      chunk = (PropTreeKidsChunk *) js_calloc(sizeof *chunk);
578      if (!chunk)      if (!chunk)
579          return NULL;          return NULL;
580      JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0);      JS_ASSERT(((jsuword)chunk & CHUNKY_KIDS_TAG) == 0);
# Line 571  Line 588 
588      JS_RUNTIME_UNMETER(rt, propTreeKidsChunks);      JS_RUNTIME_UNMETER(rt, propTreeKidsChunks);
589      if (chunk->table)      if (chunk->table)
590          JS_DHashTableDestroy(chunk->table);          JS_DHashTableDestroy(chunk->table);
591      free(chunk);      js_free(chunk);
592  }  }
593    
594  /* NB: Called with rt->gcLock held. */  /* NB: Called with rt->gcLock held. */
595  static JSBool  static bool
596  InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,  InsertPropertyTreeChild(JSRuntime *rt, JSScopeProperty *parent,
597                          JSScopeProperty *child, PropTreeKidsChunk *sweptChunk)                          JSScopeProperty *child, PropTreeKidsChunk *sweptChunk)
598  {  {
# Line 592  Line 609 
609          entry = (JSPropertyTreeEntry *)          entry = (JSPropertyTreeEntry *)
610                  JS_DHashTableOperate(table, child, JS_DHASH_ADD);                  JS_DHashTableOperate(table, child, JS_DHASH_ADD);
611          if (!entry)          if (!entry)
612              return JS_FALSE;              return false;
613          childp = &entry->child;          childp = &entry->child;
614          sprop = *childp;          sprop = *childp;
615          if (!sprop) {          if (!sprop) {
# Line 629  Line 646 
646                      entry = (JSPropertyTreeEntry *)                      entry = (JSPropertyTreeEntry *)
647                              JS_DHashTableOperate(table, child, JS_DHASH_ADD);                              JS_DHashTableOperate(table, child, JS_DHASH_ADD);
648                      if (!entry)                      if (!entry)
649                          return JS_FALSE;                          return false;
650                      if (!entry->child) {                      if (!entry->child) {
651                          entry->child = child;                          entry->child = child;
652                          while (chunk->next)                          while (chunk->next)
# Line 673  Line 690 
690                  } else {                  } else {
691                      chunk = NewPropTreeKidsChunk(rt);                      chunk = NewPropTreeKidsChunk(rt);
692                      if (!chunk)                      if (!chunk)
693                          return JS_FALSE;                          return false;
694                  }                  }
695                  *chunkp = chunk;                  *chunkp = chunk;
696                  childp = &chunk->kids[0];                  childp = &chunk->kids[0];
# Line 694  Line 711 
711                  } else {                  } else {
712                      chunk = NewPropTreeKidsChunk(rt);                      chunk = NewPropTreeKidsChunk(rt);
713                      if (!chunk)                      if (!chunk)
714                          return JS_FALSE;                          return false;
715                  }                  }
716                  parent->kids = CHUNK_TO_KIDS(chunk);                  parent->kids = CHUNK_TO_KIDS(chunk);
717                  chunk->kids[0] = sprop;                  chunk->kids[0] = sprop;
# Line 706  Line 723 
723      }      }
724    
725      child->parent = parent;      child->parent = parent;
726      return JS_TRUE;      return true;
727  }  }
728    
729  /* NB: Called with rt->gcLock held. */  /* NB: Called with rt->gcLock held. */
# Line 930  Line 947 
947      sprop->flags = child->flags;      sprop->flags = child->flags;
948      sprop->shortid = child->shortid;      sprop->shortid = child->shortid;
949      sprop->parent = sprop->kids = NULL;      sprop->parent = sprop->kids = NULL;
950      sprop->shape = js_GenerateShape(cx, JS_TRUE);      sprop->shape = js_GenerateShape(cx, true);
951    
952      if (!parent) {      if (!parent) {
953          entry->child = sprop;          entry->child = sprop;
# Line 956  Line 973 
973      JS_END_MACRO      JS_END_MACRO
974    
975  static void  static void
976  CheckAncestorLine(JSScope *scope, JSBool sparse)  CheckAncestorLine(JSScope *scope, bool sparse)
977  {  {
978      uint32 size;      uint32 size;
979      JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop;      JSScopeProperty **spp, **start, **end, *ancestorLine, *sprop, *aprop;
# Line 964  Line 981 
981    
982      ancestorLine = SCOPE_LAST_PROP(scope);      ancestorLine = SCOPE_LAST_PROP(scope);
983      if (ancestorLine)      if (ancestorLine)
984          JS_ASSERT(SCOPE_HAS_PROPERTY(scope, ancestorLine));          JS_ASSERT(scope->has(ancestorLine));
985    
986      entryCount = 0;      entryCount = 0;
987      size = SCOPE_CAPACITY(scope);      size = SCOPE_CAPACITY(scope);
# Line 984  Line 1001 
1001    
1002      ancestorCount = 0;      ancestorCount = 0;
1003      for (sprop = ancestorLine; sprop; sprop = sprop->parent) {      for (sprop = ancestorLine; sprop; sprop = sprop->parent) {
1004          if (SCOPE_HAD_MIDDLE_DELETE(scope) &&          if (scope->hadMiddleDelete() && !scope->has(sprop)) {
             !SCOPE_HAS_PROPERTY(scope, sprop)) {  
1005              JS_ASSERT(sparse);              JS_ASSERT(sparse);
1006              continue;              continue;
1007          }          }
# Line 997  Line 1013 
1013  #define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */  #define CHECK_ANCESTOR_LINE(scope, sparse) /* nothing */
1014  #endif  #endif
1015    
1016  static void  void
1017  ReportReadOnlyScope(JSContext *cx, JSScope *scope)  JSScope::reportReadOnlyScope(JSContext *cx)
1018  {  {
1019      JSString *str;      JSString *str;
1020      const char *bytes;      const char *bytes;
1021    
1022      str = js_ValueToString(cx, OBJECT_TO_JSVAL(scope->object));      str = js_ValueToString(cx, OBJECT_TO_JSVAL(object));
1023      if (!str)      if (!str)
1024          return;          return;
1025      bytes = js_GetStringBytes(cx, str);      bytes = js_GetStringBytes(cx, str);
# Line 1012  Line 1028 
1028      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes);      JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes);
1029  }  }
1030    
1031    void
1032    JSScope::generateOwnShape(JSContext *cx)
1033    {
1034        if (object)
1035            js_LeaveTraceIfGlobalObject(cx, object);
1036    
1037        shape = js_GenerateShape(cx, false);
1038        setOwnShape();
1039    }
1040    
1041  JSScopeProperty *  JSScopeProperty *
1042  js_AddScopeProperty(JSContext *cx, JSScope *scope, jsid id,  JSScope::add(JSContext *cx, jsid id,
1043                      JSPropertyOp getter, JSPropertyOp setter, uint32 slot,               JSPropertyOp getter, JSPropertyOp setter,
1044                      uintN attrs, uintN flags, intN shortid)               uint32 slot, uintN attrs,
1045                 uintN flags, intN shortid)
1046  {  {
1047      JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;      JSScopeProperty **spp, *sprop, *overwriting, **spvec, **spp2, child;
1048      uint32 size, splen, i;      uint32 size, splen, i;
1049      int change;      int change;
1050      JSTempValueRooter tvr;      JSTempValueRooter tvr;
1051    
1052      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this));
1053      CHECK_ANCESTOR_LINE(scope, JS_TRUE);      CHECK_ANCESTOR_LINE(this, true);
1054    
1055        JS_ASSERT(!JSVAL_IS_NULL(id));
1056        JS_ASSERT_IF(!cx->runtime->gcRegenShapes,
1057                     hasRegenFlag(cx->runtime->gcRegenShapesScopeFlag));
1058    
1059      /*      /*
1060       * You can't add properties to a sealed scope.  But note well that you can       * You can't add properties to a sealed scope.  But note well that you can
# Line 1031  Line 1062 
1062       * a JSScopeProperty * in the scope's hash table -- but no id is added, so       * a JSScopeProperty * in the scope's hash table -- but no id is added, so
1063       * the scope remains sealed.       * the scope remains sealed.
1064       */       */
1065      if (SCOPE_IS_SEALED(scope)) {      if (sealed()) {
1066          ReportReadOnlyScope(cx, scope);          reportReadOnlyScope(cx);
1067          return NULL;          return NULL;
1068      }      }
1069    
# Line 1049  Line 1080 
1080       * Search for id in order to claim its entry, allocating a property tree       * Search for id in order to claim its entry, allocating a property tree
1081       * node if one doesn't already exist for our parameters.       * node if one doesn't already exist for our parameters.
1082       */       */
1083      spp = js_SearchScope(scope, id, JS_TRUE);      spp = search(id, true);
1084      sprop = overwriting = SPROP_FETCH(spp);      sprop = overwriting = SPROP_FETCH(spp);
1085      if (!sprop) {      if (!sprop) {
1086          /* Check whether we need to grow, if the load factor is >= .75. */          /* Check whether we need to grow, if the load factor is >= .75. */
1087          size = SCOPE_CAPACITY(scope);          size = SCOPE_CAPACITY(this);
1088          if (scope->entryCount + scope->removedCount >= size - (size >> 2)) {          if (entryCount + removedCount >= size - (size >> 2)) {
1089              if (scope->removedCount >= size >> 2) {              if (removedCount >= size >> 2) {
1090                  METER(compresses);                  METER(compresses);
1091                  change = 0;                  change = 0;
1092              } else {              } else {
1093                  METER(grows);                  METER(grows);
1094                  change = 1;                  change = 1;
1095              }              }
1096              if (!ChangeScope(cx, scope, change) &&              if (!changeTable(cx, change) &&
1097                  scope->entryCount + scope->removedCount == size - 1) {                  entryCount + removedCount == size - 1) {
1098                  METER(addFailures);                  METER(addFailures);
1099                  return NULL;                  return NULL;
1100              }              }
1101              spp = js_SearchScope(scope, id, JS_TRUE);              spp = search(id, true);
1102              JS_ASSERT(!SPROP_FETCH(spp));              JS_ASSERT(!SPROP_FETCH(spp));
1103          }          }
1104      } else {      } else {
1105          /* Property exists: js_SearchScope must have returned a valid entry. */          /* Property exists: JSScope::search must have returned a valid *spp. */
1106          JS_ASSERT(!SPROP_IS_REMOVED(*spp));          JS_ASSERT(!SPROP_IS_REMOVED(*spp));
1107    
1108          /*          /*
# Line 1082  Line 1113 
1113           */           */
1114          if (!(attrs & JSPROP_SHARED) &&          if (!(attrs & JSPROP_SHARED) &&
1115              slot == SPROP_INVALID_SLOT &&              slot == SPROP_INVALID_SLOT &&
1116              SPROP_HAS_VALID_SLOT(sprop, scope)) {              SPROP_HAS_VALID_SLOT(sprop, this)) {
1117              slot = sprop->slot;              slot = sprop->slot;
1118          }          }
1119          if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs,          if (SPROP_MATCH_PARAMS_AFTER_ID(sprop, getter, setter, slot, attrs,
# Line 1094  Line 1125 
1125          /*          /*
1126           * If we are clearing sprop to force the existing property that it           * If we are clearing sprop to force the existing property that it
1127           * describes to be overwritten, then we have to unlink sprop from the           * describes to be overwritten, then we have to unlink sprop from the
1128           * ancestor line at scope->lastProp, lazily if sprop is not lastProp.           * ancestor line at this->lastProp, lazily if sprop is not lastProp.
1129           * And we must remove the entry at *spp, precisely so the lazy "middle           * And we must remove the entry at *spp, precisely so the lazy "middle
1130           * delete" fixup code further below won't find sprop in scope->table,           * delete" fixup code further below won't find sprop in this->table,
1131           * in spite of sprop being on the ancestor line.           * in spite of sprop being on the ancestor line.
1132           *           *
1133           * When we finally succeed in finding or creating a new sprop           * When we finally succeed in finding or creating a new sprop
# Line 1105  Line 1136 
1136           * indeed creating a new entry, or merely overwriting an existing           * indeed creating a new entry, or merely overwriting an existing
1137           * property.           * property.
1138           */           */
1139          if (sprop == SCOPE_LAST_PROP(scope)) {          if (sprop == SCOPE_LAST_PROP(this)) {
1140              do {              do {
1141                  SCOPE_REMOVE_LAST_PROP(scope);                  SCOPE_REMOVE_LAST_PROP(this);
1142                  if (!SCOPE_HAD_MIDDLE_DELETE(scope))                  if (!hadMiddleDelete())
1143                      break;                      break;
1144                  sprop = SCOPE_LAST_PROP(scope);                  sprop = SCOPE_LAST_PROP(this);
1145              } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));              } while (sprop && !has(sprop));
1146          } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {          } else if (!hadMiddleDelete()) {
1147              /*              /*
1148               * If we have no hash table yet, we need one now.  The middle               * If we have no hash table yet, we need one now.  The middle
1149               * delete code is simple-minded that way!               * delete code is simple-minded that way!
1150               */               */
1151              if (!scope->table) {              if (!table) {
1152                  if (!CreateScopeTable(cx, scope, JS_TRUE))                  if (!createTable(cx, true))
1153                      return NULL;                      return NULL;
1154                  spp = js_SearchScope(scope, id, JS_TRUE);                  spp = search(id, true);
1155                  sprop = overwriting = SPROP_FETCH(spp);                  sprop = overwriting = SPROP_FETCH(spp);
1156              }              }
1157              SCOPE_SET_MIDDLE_DELETE(scope);              setMiddleDelete();
1158          }          }
         js_MakeScopeShapeUnique(cx, scope);  
1159    
1160          /*          /*
1161           * If we fail later on trying to find or create a new sprop, we will           * If we fail later on trying to find or create a new sprop, we will
1162           * goto fail_overwrite and restore *spp from |overwriting|.  Note that           * goto fail_overwrite and restore *spp from |overwriting|.  Note that
1163           * we don't bother to keep scope->removedCount in sync, because we'll           * we don't bother to keep this->removedCount in sync, because we'll
1164           * fix up *spp and scope->entryCount shortly, no matter how control           * fix up *spp and this->entryCount shortly, no matter how control
1165           * flow returns from this function.           * flow returns from this function.
1166           */           */
1167          if (scope->table)          if (table)
1168              SPROP_STORE_PRESERVING_COLLISION(spp, NULL);              SPROP_STORE_PRESERVING_COLLISION(spp, NULL);
1169          scope->entryCount--;          entryCount--;
1170          CHECK_ANCESTOR_LINE(scope, JS_TRUE);          CHECK_ANCESTOR_LINE(this, true);
1171          sprop = NULL;          sprop = NULL;
1172      }      }
1173    
1174      if (!sprop) {      if (!sprop) {
1175          /*          /*
1176           * If properties were deleted from the middle of the list starting at           * If properties were deleted from the middle of the list starting at
1177           * scope->lastProp, we may need to fork the property tree and squeeze           * this->lastProp, we may need to fork the property tree and squeeze
1178           * all deleted properties out of scope's ancestor line.  Otherwise we           * all deleted properties out of this scope's ancestor line. Otherwise
1179           * risk adding a node with the same id as a "middle" node, violating           * we risk adding a node with the same id as a "middle" node, violating
1180           * the rule that properties along an ancestor line have distinct ids.           * the rule that properties along an ancestor line have distinct ids.
1181           */           */
1182          if (SCOPE_HAD_MIDDLE_DELETE(scope)) {          if (hadMiddleDelete()) {
1183              JS_ASSERT(scope->table);              JS_ASSERT(table);
1184              CHECK_ANCESTOR_LINE(scope, JS_TRUE);              CHECK_ANCESTOR_LINE(this, true);
1185    
1186              /*              /*
1187               * Our forking heuristic tries to balance the desire to avoid               * Our forking heuristic tries to balance the desire to avoid
# Line 1167  Line 1197 
1197    
1198              bool conflicts = false;              bool conflicts = false;
1199              uint32 count = 0;              uint32 count = 0;
1200              uint32 threshold = JS_BIT(JS_CeilingLog2(scope->entryCount));              uint32 threshold = JS_BIT(JS_CeilingLog2(entryCount));
1201              for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {              for (sprop = SCOPE_LAST_PROP(this); sprop; sprop = sprop->parent) {
1202                  ++count;                  ++count;
1203                  if (sprop->id == id) {                  if (sprop->id == id) {
1204                      conflicts = true;                      conflicts = true;
# Line 1178  Line 1208 
1208    
1209              if (conflicts || count > threshold) {              if (conflicts || count > threshold) {
1210                  /*                  /*
1211                   * Enumerate live entries in scope->table using a temporary                   * Enumerate live entries in this->table using a temporary
1212                   * vector, by walking the (possibly sparse, due to deletions)                   * vector, by walking the (possibly sparse, due to deletions)
1213                   * ancestor line from scope->lastProp.                   * ancestor line from this->lastProp.
1214                   */                   */
1215                  splen = scope->entryCount;                  splen = entryCount;
1216                  JS_ASSERT(splen != 0);                  JS_ASSERT(splen != 0);
1217                  spvec = (JSScopeProperty **)                  spvec = (JSScopeProperty **)
1218                          JS_malloc(cx, SCOPE_TABLE_NBYTES(splen));                          cx->malloc(SCOPE_TABLE_NBYTES(splen));
1219                  if (!spvec)                  if (!spvec)
1220                      goto fail_overwrite;                      goto fail_overwrite;
1221                  i = splen;                  i = splen;
1222                  sprop = SCOPE_LAST_PROP(scope);                  sprop = SCOPE_LAST_PROP(this);
1223                  JS_ASSERT(sprop);                  JS_ASSERT(sprop);
1224                  do {                  do {
1225                      /*                      /*
1226                       * NB: test SCOPE_GET_PROPERTY, not SCOPE_HAS_PROPERTY,                       * NB: use JSScope::lookup, not JSScope::has, as the latter
1227                       * as the latter macro insists that sprop->id maps to                       * macro insists that sprop->id maps to sprop, while the
1228                       * sprop, while the former simply tests whether sprop->id                       * former simply tests whether sprop->id is bound in this
1229                       * is bound in scope.                       * scope.
1230                       */                       */
1231                      if (!SCOPE_GET_PROPERTY(scope, sprop->id))                      if (!lookup(sprop->id))
1232                          continue;                          continue;
1233    
1234                      JS_ASSERT(sprop != overwriting);                      JS_ASSERT(sprop != overwriting);
# Line 1209  Line 1239 
1239    
1240                  /*                  /*
1241                   * Now loop forward through spvec, forking the property tree                   * Now loop forward through spvec, forking the property tree
1242                   * whenever we see a "parent gap" due to deletions from scope.                   * whenever we see a "parent gap" due to deletions from this
1243                   * NB: sprop is null on first entry to the loop body.                   * scope. NB: sprop is null on first entry to the loop body.
1244                   */                   */
1245                  do {                  do {
1246                      if (spvec[i]->parent == sprop) {                      if (spvec[i]->parent == sprop) {
# Line 1218  Line 1248 
1248                      } else {                      } else {
1249                          sprop = GetPropertyTreeChild(cx, sprop, spvec[i]);                          sprop = GetPropertyTreeChild(cx, sprop, spvec[i]);
1250                          if (!sprop) {                          if (!sprop) {
1251                              JS_free(cx, spvec);                              cx->free(spvec);
1252                              goto fail_overwrite;                              goto fail_overwrite;
1253                          }                          }
1254    
1255                          spp2 = js_SearchScope(scope, sprop->id, JS_FALSE);                          spp2 = search(sprop->id, false);
1256                          JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]);                          JS_ASSERT(SPROP_FETCH(spp2) == spvec[i]);
1257                          SPROP_STORE_PRESERVING_COLLISION(spp2, sprop);                          SPROP_STORE_PRESERVING_COLLISION(spp2, sprop);
1258                      }                      }
1259                  } while (++i < splen);                  } while (++i < splen);
1260                  JS_free(cx, spvec);                  cx->free(spvec);
1261    
1262                  /*                  /*
1263                   * Now sprop points to the last property in scope, where the                   * Now sprop points to the last property in this scope, where
1264                   * ancestor line from sprop to the root is dense w.r.t. scope:                   * the ancestor line from sprop to the root is dense w.r.t.
1265                   * it contains no nodes not mapped by scope->table.                   * this scope: it contains no nodes not mapped by this->table.
1266                   */                   */
1267                  scope->lastProp = sprop;                  lastProp = sprop;
1268                  CHECK_ANCESTOR_LINE(scope, JS_FALSE);                  CHECK_ANCESTOR_LINE(this, false);
1269                  JS_RUNTIME_METER(cx->runtime, middleDeleteFixups);                  JS_RUNTIME_METER(cx->runtime, middleDeleteFixups);
1270                  SCOPE_CLR_MIDDLE_DELETE(scope);                  clearMiddleDelete();
1271              }              }
1272          }          }
1273    
# Line 1258  Line 1288 
1288                   * Similarly, we use a specific slot if provided by the caller.                   * Similarly, we use a specific slot if provided by the caller.
1289                   */                   */
1290                  if (slot == SPROP_INVALID_SLOT &&                  if (slot == SPROP_INVALID_SLOT &&
1291                      !js_AllocSlot(cx, scope->object, &slot)) {                      !js_AllocSlot(cx, object, &slot)) {
1292                      goto fail_overwrite;                      goto fail_overwrite;
1293                  }                  }
1294              }              }
# Line 1270  Line 1300 
1300           * XXXbe this could get expensive with lots of watchpoints...           * XXXbe this could get expensive with lots of watchpoints...
1301           */           */
1302          if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&          if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList) &&
1303              js_FindWatchPoint(cx->runtime, scope, id)) {              js_FindWatchPoint(cx->runtime, this, id)) {
1304              if (overwriting)              if (overwriting)
1305                  JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr);                  JS_PUSH_TEMP_ROOT_SPROP(cx, overwriting, &tvr);
1306              setter = js_WrapWatchedSetter(cx, id, attrs, setter);              setter = js_WrapWatchedSetter(cx, id, attrs, setter);
# Line 1288  Line 1318 
1318          child.attrs = attrs;          child.attrs = attrs;
1319          child.flags = flags;          child.flags = flags;
1320          child.shortid = shortid;          child.shortid = shortid;
1321          sprop = GetPropertyTreeChild(cx, scope->lastProp, &child);          sprop = GetPropertyTreeChild(cx, lastProp, &child);
1322          if (!sprop)          if (!sprop)
1323              goto fail_overwrite;              goto fail_overwrite;
1324    
1325          /*          /*
1326           * The scope's shape defaults to its last property's shape, but may           * This scope's shape defaults to its last property's shape, but may be
1327           * be regenerated later as the scope diverges (from the property cache           * regenerated later as this scope diverges (from the property cache
1328           * point of view) from the structural type associated with sprop.           * point of view) from the structural type associated with sprop.
1329           */           */
1330          js_ExtendScopeShape(cx, scope, sprop);          extend(cx, sprop);
1331    
1332          /* Store the tree node pointer in the table entry for id. */          /* Store the tree node pointer in the table entry for id. */
1333          if (scope->table)          if (table)
1334              SPROP_STORE_PRESERVING_COLLISION(spp, sprop);              SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
1335          scope->entryCount++;          CHECK_ANCESTOR_LINE(this, false);
         scope->lastProp = sprop;  
         CHECK_ANCESTOR_LINE(scope, JS_FALSE);  
1336  #ifdef DEBUG  #ifdef DEBUG
1337          if (!overwriting) {          if (!overwriting) {
1338              LIVE_SCOPE_METER(cx, ++cx->runtime->liveScopeProps);              LIVE_SCOPE_METER(cx, ++cx->runtime->liveScopeProps);
# Line 1313  Line 1341 
1341  #endif  #endif
1342    
1343          /*          /*
1344           * If we reach the hashing threshold, try to allocate scope->table.           * If we reach the hashing threshold, try to allocate this->table.
1345           * If we can't (a rare event, preceded by swapping to death on most           * If we can't (a rare event, preceded by swapping to death on most
1346           * modern OSes), stick with linear search rather than whining about           * modern OSes), stick with linear search rather than whining about
1347           * this little set-back.  Therefore we must test !scope->table and           * this little set-back.  Therefore we must test !this->table and
1348           * scope->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the           * this->entryCount >= SCOPE_HASH_THRESHOLD, not merely whether the
1349           * entry count just reached the threshold.           * entry count just reached the threshold.
1350           */           */
1351          if (!scope->table && scope->entryCount >= SCOPE_HASH_THRESHOLD)          if (!table && entryCount >= SCOPE_HASH_THRESHOLD)
1352              (void) CreateScopeTable(cx, scope, JS_FALSE);              (void) createTable(cx, false);
1353      }      }
1354    
1355      jsuint index;      jsuint index;
1356      if (js_IdIsIndex(sprop->id, &index))      if (js_IdIsIndex(sprop->id, &index))
1357          SCOPE_SET_INDEXED_PROPERTIES(scope);          setIndexedProperties();
1358    
1359      METER(adds);      METER(adds);
1360      return sprop;      return sprop;
# Line 1334  Line 1362 
1362  fail_overwrite:  fail_overwrite:
1363      if (overwriting) {      if (overwriting) {
1364          /*          /*
1365           * We may or may not have forked overwriting out of scope's ancestor           * We may or may not have forked overwriting out of this scope's
1366           * line, so we must check (the alternative is to set a flag above, but           * ancestor line, so we must check (the alternative is to set a flag
1367           * that hurts the common, non-error case).  If we did fork overwriting           * above, but that hurts the common, non-error case). If we did fork
1368           * out, we'll add it back at scope->lastProp.  This means enumeration           * overwriting out, we'll add it back at scope->lastProp. This means
1369           * order can change due to a failure to overwrite an id.           * enumeration order can change due to a failure to overwrite an id.
1370           * XXXbe very minor incompatibility           * XXXbe minor(?) incompatibility
1371           */           */
1372          for (sprop = SCOPE_LAST_PROP(scope); ; sprop = sprop->parent) {          for (sprop = SCOPE_LAST_PROP(this); ; sprop = sprop->parent) {
1373              if (!sprop) {              if (!sprop) {
1374                  sprop = SCOPE_LAST_PROP(scope);                  sprop = SCOPE_LAST_PROP(this);
1375                  if (overwriting->parent == sprop) {                  if (overwriting->parent == sprop) {
1376                      scope->lastProp = overwriting;                      lastProp = overwriting;
1377                  } else {                  } else {
1378                      sprop = GetPropertyTreeChild(cx, sprop, overwriting);                      sprop = GetPropertyTreeChild(cx, sprop, overwriting);
1379                      if (sprop) {                      if (sprop) {
1380                          JS_ASSERT(sprop != overwriting);                          JS_ASSERT(sprop != overwriting);
1381                          scope->lastProp = sprop;                          lastProp = sprop;
1382                      }                      }
1383                      overwriting = sprop;                      overwriting = sprop;
1384                  }                  }
# Line 1360  Line 1388 
1388                  break;                  break;
1389          }          }
1390          if (overwriting) {          if (overwriting) {
1391              if (scope->table)              if (table)
1392                  SPROP_STORE_PRESERVING_COLLISION(spp, overwriting);                  SPROP_STORE_PRESERVING_COLLISION(spp, overwriting);
1393              scope->entryCount++;              entryCount++;
1394          }          }
1395          CHECK_ANCESTOR_LINE(scope, JS_TRUE);          CHECK_ANCESTOR_LINE(this, true);
1396      }      }
1397      METER(addFailures);      METER(addFailures);
1398      return NULL;      return NULL;
1399  }  }
1400    
1401  JSScopeProperty *  JSScopeProperty *
1402  js_ChangeScopePropertyAttrs(JSContext *cx, JSScope *scope,  JSScope::change(JSContext *cx, JSScopeProperty *sprop,
1403                              JSScopeProperty *sprop, uintN attrs, uintN mask,                  uintN attrs, uintN mask,
1404                              JSPropertyOp getter, JSPropertyOp setter)                  JSPropertyOp getter, JSPropertyOp setter)
1405  {  {
1406      JSScopeProperty child, *newsprop, **spp;      JSScopeProperty child, *newsprop, **spp;
1407    
1408      CHECK_ANCESTOR_LINE(scope, JS_TRUE);      CHECK_ANCESTOR_LINE(this, true);
1409    
1410      /* Allow only shared (slot-less) => unshared (slot-full) transition. */      /* Allow only shared (slot-less) => unshared (slot-full) transition. */
1411      attrs |= sprop->attrs & mask;      attrs |= sprop->attrs & mask;
# Line 1401  Line 1429 
1429      child.flags = sprop->flags;      child.flags = sprop->flags;
1430      child.shortid = sprop->shortid;      child.shortid = sprop->shortid;
1431    
1432      if (SCOPE_LAST_PROP(scope) == sprop) {      if (SCOPE_LAST_PROP(this) == sprop) {
1433          /*          /*
1434           * Optimize the case where the last property added to scope is changed           * Optimize the case where the last property added to this scope is
1435           * to have a different attrs, getter, or setter.  In the last property           * changed to have a different attrs, getter, or setter. In the last
1436           * case, we need not fork the property tree.  But since we do not call           * property case, we need not fork the property tree. But since we do
1437           * js_AddScopeProperty, we may need to allocate a new slot directly.           * not call JSScope::addProperty, we may need to allocate a new slot
1438             * directly.
1439           */           */
1440          if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) {          if ((sprop->attrs & JSPROP_SHARED) && !(attrs & JSPROP_SHARED)) {
1441              JS_ASSERT(child.slot == SPROP_INVALID_SLOT);              JS_ASSERT(child.slot == SPROP_INVALID_SLOT);
1442              if (!js_AllocSlot(cx, scope->object, &child.slot))              if (!js_AllocSlot(cx, object, &child.slot))
1443                  return NULL;                  return NULL;
1444          }          }
1445    
1446          newsprop = GetPropertyTreeChild(cx, sprop->parent, &child);          newsprop = GetPropertyTreeChild(cx, sprop->parent, &child);
1447          if (newsprop) {          if (newsprop) {
1448              spp = js_SearchScope(scope, sprop->id, JS_FALSE);              spp = search(sprop->id, false);
1449              JS_ASSERT(SPROP_FETCH(spp) == sprop);              JS_ASSERT(SPROP_FETCH(spp) == sprop);
1450    
1451              if (scope->table)              if (table)
1452                  SPROP_STORE_PRESERVING_COLLISION(spp, newsprop);                  SPROP_STORE_PRESERVING_COLLISION(spp, newsprop);
1453              scope->lastProp = newsprop;              lastProp = newsprop;
1454              CHECK_ANCESTOR_LINE(scope, JS_TRUE);              CHECK_ANCESTOR_LINE(this, true);
1455          }          }
1456      } else {      } else {
1457          /*          /*
1458           * Let js_AddScopeProperty handle this |overwriting| case, including           * Let JSScope::addProperty handle this |overwriting| case, including
1459           * the conservation of sprop->slot (if it's valid).  We must not call           * the conservation of sprop->slot (if it's valid). We must not call
1460           * js_RemoveScopeProperty here, it will free a valid sprop->slot and           * JSScope::remove here, because it will free a valid sprop->slot and
1461           * js_AddScopeProperty won't re-allocate it.           * JSScope::add won't re-allocate it.
1462           */           */
1463          newsprop = js_AddScopeProperty(cx, scope, child.id,          newsprop = add(cx, child.id, child.getter, child.setter, child.slot,
1464                                         child.getter, child.setter, child.slot,                         child.attrs, child.flags, child.shortid);
                                        child.attrs, child.flags, child.shortid);  
1465      }      }
1466    
1467      if (newsprop) {      if (newsprop) {
1468          js_LeaveTraceIfGlobalObject(cx, scope->object);          js_LeaveTraceIfGlobalObject(cx, object);
1469          if (scope->shape == sprop->shape)          replacingShapeChange(cx, sprop, newsprop);
             scope->shape = newsprop->shape;  
         else  
             js_MakeScopeShapeUnique(cx, scope);  
1470      }      }
1471  #ifdef JS_DUMP_PROPTREE_STATS  #ifdef JS_DUMP_PROPTREE_STATS
1472      else      else
# Line 1450  Line 1475 
1475      return newsprop;      return newsprop;
1476  }  }
1477    
1478  JSBool  bool
1479  js_RemoveScopeProperty(JSContext *cx, JSScope *scope, jsid id)  JSScope::remove(JSContext *cx, jsid id)
1480  {  {
1481      JSScopeProperty **spp, *stored, *sprop;      JSScopeProperty **spp, *stored, *sprop;
1482      uint32 size;      uint32 size;
1483    
1484      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));      JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, this));
1485      CHECK_ANCESTOR_LINE(scope, JS_TRUE);      CHECK_ANCESTOR_LINE(this, true);
1486      if (SCOPE_IS_SEALED(scope)) {      if (sealed()) {
1487          ReportReadOnlyScope(cx, scope);          reportReadOnlyScope(cx);
1488          return JS_FALSE;          return false;
1489      }      }
1490      METER(removes);      METER(removes);
1491    
1492      spp = js_SearchScope(scope, id, JS_FALSE);      spp = search(id, false);
1493      stored = *spp;      stored = *spp;
1494      sprop = SPROP_CLEAR_COLLISION(stored);      sprop = SPROP_CLEAR_COLLISION(stored);
1495      if (!sprop) {      if (!sprop) {
1496          METER(uselessRemoves);          METER(uselessRemoves);
1497          return JS_TRUE;          return true;
1498      }      }
1499    
1500      /* Convert from a list to a hash so we can handle "middle deletes". */      /* Convert from a list to a hash so we can handle "middle deletes". */
1501      if (!scope->table && sprop != scope->lastProp) {      if (!table && sprop != lastProp) {
1502          if (!CreateScopeTable(cx, scope, JS_TRUE))          if (!createTable(cx, true))
1503              return JS_FALSE;              return false;
1504          spp = js_SearchScope(scope, id, JS_FALSE);          spp = search(id, false);
1505          stored = *spp;          stored = *spp;
1506          sprop = SPROP_CLEAR_COLLISION(stored);          sprop = SPROP_CLEAR_COLLISION(stored);
1507      }      }
1508    
1509      /* First, if sprop is unshared and not cleared, free its slot number. */      /* First, if sprop is unshared and not cleared, free its slot number. */
1510      if (SPROP_HAS_VALID_SLOT(sprop, scope)) {      if (SPROP_HAS_VALID_SLOT(sprop, this)) {
1511          js_FreeSlot(cx, scope->object, sprop->slot);          js_FreeSlot(cx, object, sprop->slot);
1512          JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);          JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
1513      }      }
1514    
1515      /* Next, remove id by setting its entry to a removed or free sentinel. */      /* Next, remove id by setting its entry to a removed or free sentinel. */
1516      if (SPROP_HAD_COLLISION(stored)) {      if (SPROP_HAD_COLLISION(stored)) {
1517          JS_ASSERT(scope->table);          JS_ASSERT(table);
1518          *spp = SPROP_REMOVED;          *spp = SPROP_REMOVED;
1519          scope->removedCount++;          removedCount++;
1520      } else {      } else {
1521          METER(removeFrees);          METER(removeFrees);
1522          if (scope->table)          if (table)
1523              *spp = NULL;              *spp = NULL;
1524      }      }
1525      scope->entryCount--;      entryCount--;
1526      LIVE_SCOPE_METER(cx, --cx->runtime->liveScopeProps);      LIVE_SCOPE_METER(cx, --cx->runtime->liveScopeProps);
1527    
1528      /* Update scope->lastProp directly, or set its deferred update flag. */      /* Update this->lastProp directly, or set its deferred update flag. */
1529      if (sprop == SCOPE_LAST_PROP(scope)) {      if (sprop == SCOPE_LAST_PROP(this)) {
1530          do {          do {
1531              SCOPE_REMOVE_LAST_PROP(scope);              SCOPE_REMOVE_LAST_PROP(this);
1532              if (!SCOPE_HAD_MIDDLE_DELETE(scope))              if (!hadMiddleDelete())
1533                  break;                  break;
1534              sprop = SCOPE_LAST_PROP(scope);              sprop = SCOPE_LAST_PROP(this);
1535          } while (sprop && !SCOPE_HAS_PROPERTY(scope, sprop));          } while (sprop && !has(sprop));
1536          if (!SCOPE_LAST_PROP(scope))          if (!SCOPE_LAST_PROP(this))
1537              SCOPE_CLR_MIDDLE_DELETE(scope);              clearMiddleDelete();
1538      } else if (!SCOPE_HAD_MIDDLE_DELETE(scope)) {      } else if (!hadMiddleDelete()) {
1539          SCOPE_SET_MIDDLE_DELETE(scope);          setMiddleDelete();
1540      }      }
1541      js_MakeScopeShapeUnique(cx, scope);      generateOwnShape(cx);
1542      CHECK_ANCESTOR_LINE(scope, JS_TRUE);      CHECK_ANCESTOR_LINE(this, true);
1543    
1544      /* Last, consider shrinking scope's table if its load factor is <= .25. */      /* Last, consider shrinking this->table if its load factor is <= .25. */
1545      size = SCOPE_CAPACITY(scope);      size = SCOPE_CAPACITY(this);
1546      if (size > MIN_SCOPE_SIZE && scope->entryCount <= size >> 2) {      if (size > MIN_SCOPE_SIZE && entryCount <= size >> 2) {
1547          METER(shrinks);          METER(shrinks);
1548          (void) ChangeScope(cx, scope, -1);          (void) changeTable(cx, -1);
1549      }      }
1550    
1551      return JS_TRUE;      return true;
1552  }  }
1553    
1554  void  void
1555  js_ClearScope(JSContext *cx, JSScope *scope)  JSScope::clear(JSContext *cx)
1556  {  {
1557      CHECK_ANCESTOR_LINE(scope, JS_TRUE);      CHECK_ANCESTOR_LINE(this, true);
1558      LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= scope->entryCount);      LIVE_SCOPE_METER(cx, cx->runtime->liveScopeProps -= entryCount);
1559    
1560        if (table)
1561            js_free(table);
1562        clearMiddleDelete();
1563        js_LeaveTraceIfGlobalObject(cx, object);
1564    
1565        JSClass *clasp = object->getClass();
1566        JSObject *proto = object->getProto();
1567        JSEmptyScope *emptyScope;
1568        uint32 newShape;
1569        if (proto &&
1570            OBJ_IS_NATIVE(proto) &&
1571            (emptyScope = OBJ_SCOPE(proto)->emptyScope) &&
1572            emptyScope->clasp == clasp) {
1573            newShape = emptyScope->shape;
1574        } else {
1575            newShape = js_GenerateShape(cx, false);
1576        }
1577        initMinimal(cx, newShape);
1578    
     if (scope->table)  
         free(scope->table);  
     SCOPE_CLR_MIDDLE_DELETE(scope);  
     InitMinimalScope(cx, scope);  
1579      JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);      JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
1580  }  }
1581    
1582  void  void
1583    JSScope::brandingShapeChange(JSContext *cx, uint32 slot, jsval v)
1584    {
1585        generateOwnShape(cx);
1586    }
1587    
1588    void
1589    JSScope::deletingShapeChange(JSContext *cx, JSScopeProperty *sprop)
1590    {
1591        generateOwnShape(cx);
1592    }
1593    
1594    void
1595    JSScope::methodShapeChange(JSContext *cx, uint32 slot, jsval toval)
1596    {
1597        generateOwnShape(cx);
1598    }
1599    
1600    void
1601    JSScope::protoShapeChange(JSContext *cx)
1602    {
1603        generateOwnShape(cx);
1604    }
1605    
1606    void
1607    JSScope::replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProperty *newsprop)
1608    {
1609        if (shape == sprop->shape)
1610            shape = newsprop->shape;
1611        else
1612            generateOwnShape(cx);
1613    }
1614    
1615    void
1616    JSScope::sealingShapeChange(JSContext *cx)
1617    {
1618        generateOwnShape(cx);
1619    }
1620    
1621    void
1622    JSScope::shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop)
1623    {
1624        generateOwnShape(cx);
1625    }
1626    
1627    void
1628  js_TraceId(JSTracer *trc, jsid id)  js_TraceId(JSTracer *trc, jsid id)
1629  {  {
1630      jsval v;      jsval v;
# Line 1575  Line 1660 
1660  }  }
1661  #endif  #endif
1662    
   
1663  void  void
1664  js_TraceScopeProperty(JSTracer *trc, JSScopeProperty *sprop)  JSScopeProperty::trace(JSTracer *trc)
1665  {  {
1666      if (IS_GC_MARKING_TRACER(trc))      if (IS_GC_MARKING_TRACER(trc))
1667          sprop->flags |= SPROP_MARK;          flags |= SPROP_MARK;
1668      TRACE_ID(trc, sprop->id);      js_TraceId(trc, id);
1669    
1670  #if JS_HAS_GETTER_SETTER  #if JS_HAS_GETTER_SETTER
1671      if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {      if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1672          if (sprop->attrs & JSPROP_GETTER) {          if (attrs & JSPROP_GETTER) {
1673              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 0);              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0);
1674              JS_CallTracer(trc, js_CastAsObject(sprop->getter), JSTRACE_OBJECT);              JS_CallTracer(trc, js_CastAsObject(getter), JSTRACE_OBJECT);
1675          }          }
1676          if (sprop->attrs & JSPROP_SETTER) {          if (attrs & JSPROP_SETTER) {
1677              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, sprop, 1);              JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1);
1678              JS_CallTracer(trc, js_CastAsObject(sprop->setter), JSTRACE_OBJECT);              JS_CallTracer(trc, js_CastAsObject(setter), JSTRACE_OBJECT);
1679          }          }
1680      }      }
1681  #endif /* JS_HAS_GETTER_SETTER */  #endif /* JS_HAS_GETTER_SETTER */
# Line 1757  Line 1841 
1841               */               */
1842              if (sprop->flags & SPROP_MARK) {              if (sprop->flags & SPROP_MARK) {
1843                  sprop->flags &= ~SPROP_MARK;                  sprop->flags &= ~SPROP_MARK;
1844                  if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)                  if (rt->gcRegenShapes) {
1845                      sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;                      if (sprop->flags & SPROP_FLAG_SHAPE_REGEN)
1846                  else                          sprop->flags &= ~SPROP_FLAG_SHAPE_REGEN;
1847                      sprop->shape = js_RegenerateShapeForGC(cx);                      else
1848                            sprop->shape = js_RegenerateShapeForGC(cx);
1849                    }
1850                  liveCount++;                  liveCount++;
1851                  continue;                  continue;
1852              }              }
# Line 1800  Line 1886 
1886                  sprop->kids = NULL;                  sprop->kids = NULL;
1887                  parent = sprop->parent;                  parent = sprop->parent;
1888    
1889                  /* Assert that grandparent has no kids or chunky kids. */                  /* The grandparent must have either no kids or chunky kids. */
1890                  JS_ASSERT(!parent || !parent->kids ||                  JS_ASSERT(!parent || !parent->kids ||
1891                            KIDS_IS_CHUNKY(parent->kids));                            KIDS_IS_CHUNKY(parent->kids));
1892                  if (KIDS_IS_CHUNKY(kids)) {                  if (KIDS_IS_CHUNKY(kids)) {
# Line 1819  Line 1905 
1905                               * re-use by InsertPropertyTreeChild.                               * re-use by InsertPropertyTreeChild.
1906                               */                               */
1907                              chunk->kids[i] = NULL;                              chunk->kids[i] = NULL;
1908                              if (!InsertPropertyTreeChild(rt, parent, kid,                              if (!InsertPropertyTreeChild(rt, parent, kid, chunk)) {
                                                          chunk)) {  
1909                                  /*                                  /*
1910                                   * This can happen only if we failed to add an                                   * This can happen only if we failed to add an
1911                                   * entry to the root property hash table.                                   * entry to the root property hash table.
# Line 1942  Line 2027 
2027  #endif  #endif
2028  }  }
2029    
2030  JSBool  bool
2031  js_InitPropertyTree(JSRuntime *rt)  js_InitPropertyTree(JSRuntime *rt)
2032  {  {
2033      if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL,      if (!JS_DHashTableInit(&rt->propertyTreeHash, &PropertyTreeHashOps, NULL,
2034                             sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) {                             sizeof(JSPropertyTreeEntry), JS_DHASH_MIN_SIZE)) {
2035          rt->propertyTreeHash.ops = NULL;          rt->propertyTreeHash.ops = NULL;
2036          return JS_FALSE;          return false;
2037      }      }
2038      JS_INIT_ARENA_POOL(&rt->propertyArenaPool, "properties",      JS_INIT_ARENA_POOL(&rt->propertyArenaPool, "properties",
2039                         256 * sizeof(JSScopeProperty), sizeof(void *), NULL);                         256 * sizeof(JSScopeProperty), sizeof(void *), NULL);
2040      return JS_TRUE;      return true;
2041  }  }
2042    
2043  void  void

Legend:
Removed from v.460  
changed lines
  Added in v.507

  ViewVC Help
Powered by ViewVC 1.1.24