LCOV - code coverage report
Current view: directory - js/src - jsscope.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 729 592 81.2 %
Date: 2012-06-02 Functions: 46 45 97.8 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=78:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla Communicator client code, released
      18                 :  * March 31, 1998.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  * Netscape Communications Corporation.
      22                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      23                 :  * the Initial Developer. All Rights Reserved.
      24                 :  *
      25                 :  * Contributor(s):
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : /*
      42                 :  * JS symbol tables.
      43                 :  */
      44                 : #include <new>
      45                 : #include <stdlib.h>
      46                 : #include <string.h>
      47                 : #include "jstypes.h"
      48                 : #include "jsclist.h"
      49                 : #include "jsdhash.h"
      50                 : #include "jsutil.h"
      51                 : #include "jsapi.h"
      52                 : #include "jsatom.h"
      53                 : #include "jscntxt.h"
      54                 : #include "jsdbgapi.h"
      55                 : #include "jslock.h"
      56                 : #include "jsnum.h"
      57                 : #include "jsobj.h"
      58                 : #include "jsscope.h"
      59                 : #include "jsstr.h"
      60                 : 
      61                 : #include "js/MemoryMetrics.h"
      62                 : 
      63                 : #include "jsatominlines.h"
      64                 : #include "jsobjinlines.h"
      65                 : #include "jsscopeinlines.h"
      66                 : 
      67                 : using namespace js;
      68                 : using namespace js::gc;
      69                 : 
      70                 : bool
      71          351675 : PropertyTable::init(JSRuntime *rt, Shape *lastProp)
      72                 : {
      73                 :     /*
      74                 :      * Either we're creating a table for a large scope that was populated
      75                 :      * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
      76                 :      * JSOP_SETPROP; or else calloc failed at least once already. In any
      77                 :      * event, let's try to grow, overallocating to hold at least twice the
      78                 :      * current population.
      79                 :      */
      80          351675 :     uint32_t sizeLog2 = JS_CEILING_LOG2W(2 * entryCount);
      81          351675 :     if (sizeLog2 < MIN_SIZE_LOG2)
      82           22596 :         sizeLog2 = MIN_SIZE_LOG2;
      83                 : 
      84                 :     /*
      85                 :      * Use rt->calloc_ for memory accounting and overpressure handling
      86                 :      * without OOM reporting. See PropertyTable::change.
      87                 :      */
      88          351675 :     entries = (Shape **) rt->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
      89          351675 :     if (!entries)
      90               0 :         return false;
      91                 : 
      92          351675 :     hashShift = JS_DHASH_BITS - sizeLog2;
      93         7104046 :     for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
      94         6752371 :         const Shape &shape = r.front();
      95         6752371 :         Shape **spp = search(shape.propid(), true);
      96                 : 
      97                 :         /*
      98                 :          * Beware duplicate args and arg vs. var conflicts: the youngest shape
      99                 :          * (nearest to lastProp) must win. See bug 600067.
     100                 :          */
     101         6752371 :         if (!SHAPE_FETCH(spp))
     102         6752371 :             SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
     103                 :     }
     104          351675 :     return true;
     105                 : }
     106                 : 
     107                 : bool
     108          351675 : Shape::makeOwnBaseShape(JSContext *cx)
     109                 : {
     110          351675 :     JS_ASSERT(!base()->isOwned());
     111                 : 
     112          703350 :     RootedVarShape self(cx, this);
     113                 : 
     114          351675 :     BaseShape *nbase = js_NewGCBaseShape(cx);
     115          351675 :     if (!nbase)
     116               0 :         return false;
     117                 : 
     118          351675 :     new (nbase) BaseShape(*self->base());
     119          351675 :     nbase->setOwned(self->base()->toUnowned());
     120                 : 
     121          351675 :     self->base_ = nbase;
     122                 : 
     123          351675 :     return true;
     124                 : }
     125                 : 
     126                 : void
     127         5004595 : Shape::handoffTableTo(Shape *shape)
     128                 : {
     129         5004595 :     JS_ASSERT(inDictionary() && shape->inDictionary());
     130                 : 
     131         5004595 :     if (this == shape)
     132          287809 :         return;
     133                 : 
     134         4716786 :     JS_ASSERT(base()->isOwned() && !shape->base()->isOwned());
     135                 : 
     136         4716786 :     BaseShape *nbase = base();
     137                 : 
     138         4716786 :     JS_ASSERT_IF(shape->hasSlot(), nbase->slotSpan() > shape->slot());
     139                 : 
     140         4716786 :     this->base_ = nbase->baseUnowned();
     141         4716786 :     nbase->adoptUnowned(shape->base()->toUnowned());
     142                 : 
     143         4716786 :     shape->base_ = nbase;
     144                 : }
     145                 : 
     146                 : bool
     147          351675 : Shape::hashify(JSContext *cx)
     148                 : {
     149          351675 :     JS_ASSERT(!hasTable());
     150                 : 
     151          703350 :     RootedVarShape self(cx, this);
     152                 : 
     153          351675 :     if (!ensureOwnBaseShape(cx))
     154               0 :         return false;
     155                 : 
     156          351675 :     JSRuntime *rt = cx->runtime;
     157          351675 :     PropertyTable *table = rt->new_<PropertyTable>(self->entryCount());
     158          351675 :     if (!table)
     159               0 :         return false;
     160                 : 
     161          351675 :     if (!table->init(rt, self)) {
     162               0 :         rt->free_(table);
     163               0 :         return false;
     164                 :     }
     165                 : 
     166          351675 :     self->base()->setTable(table);
     167          351675 :     return true;
     168                 : }
     169                 : 
     170                 : /*
     171                 :  * Double hashing needs the second hash code to be relatively prime to table
     172                 :  * size, so we simply make hash2 odd.
     173                 :  */
     174                 : #define HASH1(hash0,shift)      ((hash0) >> (shift))
     175                 : #define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
     176                 : 
     177                 : Shape **
     178       146489419 : PropertyTable::search(jsid id, bool adding)
     179                 : {
     180                 :     JSHashNumber hash0, hash1, hash2;
     181                 :     int sizeLog2;
     182                 :     Shape *stored, *shape, **spp, **firstRemoved;
     183                 :     uint32_t sizeMask;
     184                 : 
     185       146489419 :     JS_ASSERT(entries);
     186       146489419 :     JS_ASSERT(!JSID_IS_EMPTY(id));
     187                 : 
     188                 :     /* Compute the primary hash address. */
     189       146489419 :     hash0 = HashId(id);
     190       146489419 :     hash1 = HASH1(hash0, hashShift);
     191       146489419 :     spp = entries + hash1;
     192                 : 
     193                 :     /* Miss: return space for a new entry. */
     194       146489419 :     stored = *spp;
     195       146489419 :     if (SHAPE_IS_FREE(stored))
     196        27373845 :         return spp;
     197                 : 
     198                 :     /* Hit: return entry. */
     199       119115574 :     shape = SHAPE_CLEAR_COLLISION(stored);
     200       119115574 :     if (shape && shape->propid() == id)
     201        79061880 :         return spp;
     202                 : 
     203                 :     /* Collision: double hash. */
     204        40053694 :     sizeLog2 = JS_DHASH_BITS - hashShift;
     205        40053694 :     hash2 = HASH2(hash0, sizeLog2, hashShift);
     206        40053694 :     sizeMask = JS_BITMASK(sizeLog2);
     207                 : 
     208                 : #ifdef DEBUG
     209        40053694 :     uintptr_t collision_flag = SHAPE_COLLISION;
     210                 : #endif
     211                 : 
     212                 :     /* Save the first removed entry pointer so we can recycle it if adding. */
     213        40053694 :     if (SHAPE_IS_REMOVED(stored)) {
     214          361655 :         firstRemoved = spp;
     215                 :     } else {
     216        39692039 :         firstRemoved = NULL;
     217        39692039 :         if (adding && !SHAPE_HAD_COLLISION(stored))
     218         2464486 :             SHAPE_FLAG_COLLISION(spp, shape);
     219                 : #ifdef DEBUG
     220        39692039 :         collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION;
     221                 : #endif
     222                 :     }
     223                 : 
     224        31881217 :     for (;;) {
     225        71934911 :         hash1 -= hash2;
     226        71934911 :         hash1 &= sizeMask;
     227        71934911 :         spp = entries + hash1;
     228                 : 
     229        71934911 :         stored = *spp;
     230        71934911 :         if (SHAPE_IS_FREE(stored))
     231        13832412 :             return (adding && firstRemoved) ? firstRemoved : spp;
     232                 : 
     233        58102499 :         shape = SHAPE_CLEAR_COLLISION(stored);
     234        58102499 :         if (shape && shape->propid() == id) {
     235        26221282 :             JS_ASSERT(collision_flag);
     236        26221282 :             return spp;
     237                 :         }
     238                 : 
     239        31881217 :         if (SHAPE_IS_REMOVED(stored)) {
     240          246179 :             if (!firstRemoved)
     241          149668 :                 firstRemoved = spp;
     242                 :         } else {
     243        31635038 :             if (adding && !SHAPE_HAD_COLLISION(stored))
     244         1822187 :                 SHAPE_FLAG_COLLISION(spp, shape);
     245                 : #ifdef DEBUG
     246        31635038 :             collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION;
     247                 : #endif
     248                 :         }
     249                 :     }
     250                 : 
     251                 :     /* NOTREACHED */
     252                 :     return NULL;
     253                 : }
     254                 : 
     255                 : bool
     256           34849 : PropertyTable::change(int log2Delta, JSContext *cx)
     257                 : {
     258           34849 :     JS_ASSERT(entries);
     259                 : 
     260                 :     /*
     261                 :      * Grow, shrink, or compress by changing this->entries.
     262                 :      */
     263           34849 :     int oldlog2 = JS_DHASH_BITS - hashShift;
     264           34849 :     int newlog2 = oldlog2 + log2Delta;
     265           34849 :     uint32_t oldsize = JS_BIT(oldlog2);
     266           34849 :     uint32_t newsize = JS_BIT(newlog2);
     267           34849 :     Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize));
     268           34849 :     if (!newTable)
     269               0 :         return false;
     270                 : 
     271                 :     /* Now that we have newTable allocated, update members. */
     272           34849 :     hashShift = JS_DHASH_BITS - newlog2;
     273           34849 :     removedCount = 0;
     274           34849 :     Shape **oldTable = entries;
     275           34849 :     entries = newTable;
     276                 : 
     277                 :     /* Copy only live entries, leaving removed and free ones behind. */
     278         6628513 :     for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) {
     279         6593664 :         Shape *shape = SHAPE_FETCH(oldspp);
     280         6593664 :         if (shape) {
     281         4427901 :             Shape **spp = search(shape->propid(), true);
     282         4427901 :             JS_ASSERT(SHAPE_IS_FREE(*spp));
     283         4427901 :             *spp = shape;
     284                 :         }
     285         6593664 :         oldsize--;
     286                 :     }
     287                 : 
     288                 :     /* Finally, free the old entries storage. */
     289           34849 :     cx->free_(oldTable);
     290           34849 :     return true;
     291                 : }
     292                 : 
     293                 : bool
     294           23531 : PropertyTable::grow(JSContext *cx)
     295                 : {
     296           23531 :     JS_ASSERT(needsToGrow());
     297                 : 
     298           23531 :     uint32_t size = capacity();
     299           23531 :     int delta = removedCount < size >> 2;
     300                 : 
     301           23531 :     if (!change(delta, cx) && entryCount + removedCount == size - 1) {
     302               0 :         JS_ReportOutOfMemory(cx);
     303               0 :         return false;
     304                 :     }
     305           23531 :     return true;
     306                 : }
     307                 : 
     308                 : Shape *
     309         2573523 : Shape::getChildBinding(JSContext *cx, const StackShape &child)
     310                 : {
     311         2573523 :     JS_ASSERT(!inDictionary());
     312                 : 
     313         2573523 :     Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, numFixedSlots(), child);
     314         2573523 :     if (shape) {
     315         2573523 :         JS_ASSERT(shape->parent == this);
     316                 : 
     317                 :         /*
     318                 :          * Update the number of fixed slots which bindings of this shape will
     319                 :          * have. Bindings are constructed as new properties come in, so the
     320                 :          * call object allocation class is not known ahead of time. Compute
     321                 :          * the fixed slot count here, which will feed into call objects created
     322                 :          * off of the bindings.
     323                 :          */
     324         2573523 :         uint32_t slots = child.slotSpan() + 1;  /* Add one for private data. */
     325         2573523 :         gc::AllocKind kind = gc::GetGCObjectKind(slots);
     326                 : 
     327                 :         /*
     328                 :          * Make sure that the arguments and variables in the call object all
     329                 :          * end up in a contiguous range of slots. We need this to be able to
     330                 :          * embed the args/vars arrays in the TypeScriptNesting for the function
     331                 :          * after the call object's frame has finished.
     332                 :          */
     333         2573523 :         uint32_t nfixed = gc::GetGCKindSlots(kind);
     334         2573523 :         if (nfixed < slots) {
     335          250937 :             nfixed = CallObject::RESERVED_SLOTS + 1;
     336          250937 :             JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS + 1);
     337                 :         }
     338                 : 
     339         2573523 :         shape->setNumFixedSlots(nfixed - 1);
     340                 :     }
     341         2573523 :     return shape;
     342                 : }
     343                 : 
     344                 : /* static */ Shape *
     345         5355055 : Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, JSObject *proto, Shape *shape)
     346                 : {
     347         5355055 :     JS_ASSERT(!shape->inDictionary());
     348                 : 
     349         5355055 :     if (!shape->parent) {
     350                 :         /* Treat as resetting the initial property of the shape hierarchy. */
     351         4039299 :         AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     352                 :         return EmptyShape::getInitialShape(cx, base.clasp, proto,
     353                 :                                            base.parent, kind,
     354         4039299 :                                            base.flags & BaseShape::OBJECT_FLAG_MASK);
     355                 :     }
     356                 : 
     357         2631512 :     RootShape root(cx, &shape);
     358                 : 
     359         1315756 :     UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     360         1315756 :     if (!nbase)
     361               0 :         return NULL;
     362                 : 
     363         1315756 :     StackShape child(shape);
     364         1315756 :     child.base = nbase;
     365                 : 
     366         1315756 :     return JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, shape->numFixedSlots(), child);
     367                 : }
     368                 : 
     369                 : /*
     370                 :  * Get or create a property-tree or dictionary child property of |parent|,
     371                 :  * which must be lastProperty() if inDictionaryMode(), else parent must be
     372                 :  * one of lastProperty() or lastProperty()->parent.
     373                 :  */
     374                 : Shape *
     375        41082663 : JSObject::getChildProperty(JSContext *cx, Shape *parent, StackShape &child)
     376                 : {
     377                 :     /*
     378                 :      * Shared properties have no slot, but slot_ will reflect that of parent.
     379                 :      * Unshared properties allocate a slot here but may lose it due to a
     380                 :      * JS_ClearScope call.
     381                 :      */
     382        41082663 :     if (!child.hasSlot()) {
     383        13071096 :         child.setSlot(parent->maybeSlot());
     384                 :     } else {
     385        28011567 :         if (child.hasMissingSlot()) {
     386                 :             uint32_t slot;
     387        25306732 :             if (!allocSlot(cx, &slot))
     388               0 :                 return NULL;
     389        25306732 :             child.setSlot(slot);
     390                 :         } else {
     391                 :             /* Slots can only be allocated out of order on objects in dictionary mode. */
     392         5101891 :             JS_ASSERT(inDictionaryMode() ||
     393                 :                       parent->hasMissingSlot() ||
     394         5101891 :                       child.slot() == parent->maybeSlot() + 1);
     395                 :         }
     396                 :     }
     397                 : 
     398                 :     Shape *shape;
     399                 : 
     400        82165326 :     RootedVarObject self(cx, this);
     401                 : 
     402        41082663 :     if (inDictionaryMode()) {
     403         3597360 :         JS_ASSERT(parent == lastProperty());
     404         7194720 :         RootStackShape childRoot(cx, &child);
     405         3597360 :         shape = js_NewGCShape(cx);
     406         3597360 :         if (!shape)
     407               0 :             return NULL;
     408         3597360 :         if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) {
     409         1067788 :             if (!self->setSlotSpan(cx, child.slot() + 1))
     410               0 :                 return NULL;
     411                 :         }
     412         7194720 :         shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_);
     413                 :     } else {
     414        37485303 :         shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, self->numFixedSlots(), child);
     415        37485303 :         if (!shape)
     416               0 :             return NULL;
     417                 :         //JS_ASSERT(shape->parent == parent);
     418                 :         //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
     419        37485303 :         if (!self->setLastProperty(cx, shape))
     420               0 :             return NULL;
     421                 :     }
     422                 : 
     423        41082663 :     return shape;
     424                 : }
     425                 : 
     426                 : bool
     427          167286 : JSObject::toDictionaryMode(JSContext *cx)
     428                 : {
     429          167286 :     JS_ASSERT(!inDictionaryMode());
     430                 : 
     431                 :     /* We allocate the shapes from cx->compartment, so make sure it's right. */
     432          167286 :     JS_ASSERT(compartment() == cx->compartment);
     433                 : 
     434          167286 :     uint32_t span = slotSpan();
     435                 : 
     436          334572 :     RootedVarObject self(cx, this);
     437                 : 
     438                 :     /*
     439                 :      * Clone the shapes into a new dictionary list. Don't update the
     440                 :      * last property of this object until done, otherwise a GC
     441                 :      * triggered while creating the dictionary will get the wrong
     442                 :      * slot span for this object.
     443                 :      */
     444          334572 :     RootedVarShape root(cx);
     445          334572 :     RootedVarShape dictionaryShape(cx);
     446                 : 
     447          334572 :     RootedVarShape shape(cx);
     448          167286 :     shape = lastProperty();
     449                 : 
     450         4410969 :     while (shape) {
     451         4076397 :         JS_ASSERT(!shape->inDictionary());
     452                 : 
     453         4076397 :         Shape *dprop = js_NewGCShape(cx);
     454         4076397 :         if (!dprop) {
     455               0 :             js_ReportOutOfMemory(cx);
     456               0 :             return false;
     457                 :         }
     458                 : 
     459                 :         HeapPtrShape *listp = dictionaryShape
     460         3909111 :                               ? &dictionaryShape->parent
     461         7985508 :                               : (HeapPtrShape *) root.address();
     462                 : 
     463         4076397 :         StackShape child(shape);
     464         4076397 :         dprop->initDictionaryShape(child, self->numFixedSlots(), listp);
     465                 : 
     466         4076397 :         JS_ASSERT(!dprop->hasTable());
     467         4076397 :         dictionaryShape = dprop;
     468         4076397 :         shape = shape->previous();
     469                 :     }
     470                 : 
     471          167286 :     if (!root->hashify(cx)) {
     472               0 :         js_ReportOutOfMemory(cx);
     473               0 :         return false;
     474                 :     }
     475                 : 
     476          167286 :     JS_ASSERT((Shape **) root->listp == root.address());
     477          167286 :     root->listp = &self->shape_;
     478          167286 :     self->shape_ = root;
     479                 : 
     480          167286 :     JS_ASSERT(self->inDictionaryMode());
     481          167286 :     root->base()->setSlotSpan(span);
     482                 : 
     483          167286 :     return true;
     484                 : }
     485                 : 
     486                 : /*
     487                 :  * Normalize stub getter and setter values for faster is-stub testing in the
     488                 :  * SHAPE_CALL_[GS]ETTER macros.
     489                 :  */
     490                 : static inline bool
     491        42580218 : NormalizeGetterAndSetter(JSContext *cx, JSObject *obj,
     492                 :                          jsid id, unsigned attrs, unsigned flags,
     493                 :                          PropertyOp &getter,
     494                 :                          StrictPropertyOp &setter)
     495                 : {
     496        42580218 :     if (setter == JS_StrictPropertyStub) {
     497        37889958 :         JS_ASSERT(!(attrs & JSPROP_SETTER));
     498        37889958 :         setter = NULL;
     499                 :     }
     500        42580218 :     if (flags & Shape::METHOD) {
     501               0 :         JS_ASSERT_IF(getter, getter == JS_PropertyStub);
     502               0 :         JS_ASSERT(!setter);
     503               0 :         JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     504               0 :         getter = NULL;
     505                 :     } else {
     506        42580218 :         if (getter == JS_PropertyStub) {
     507        26872472 :             JS_ASSERT(!(attrs & JSPROP_GETTER));
     508        26872472 :             getter = NULL;
     509                 :         }
     510                 :     }
     511                 : 
     512        42580218 :     return true;
     513                 : }
     514                 : 
     515                 : #ifdef DEBUG
     516                 : # define CHECK_SHAPE_CONSISTENCY(obj) obj->checkShapeConsistency()
     517                 : 
     518                 : void
     519        41756760 : JSObject::checkShapeConsistency()
     520                 : {
     521                 :     static int throttle = -1;
     522        41756760 :     if (throttle < 0) {
     523           19811 :         if (const char *var = getenv("JS_CHECK_SHAPE_THROTTLE"))
     524               0 :             throttle = atoi(var);
     525           19811 :         if (throttle < 0)
     526           19811 :             throttle = 0;
     527                 :     }
     528        41756760 :     if (throttle == 0)
     529        41756760 :         return;
     530                 : 
     531               0 :     JS_ASSERT(isNative());
     532                 : 
     533               0 :     Shape *shape = lastProperty();
     534               0 :     Shape *prev = NULL;
     535                 : 
     536               0 :     if (inDictionaryMode()) {
     537               0 :         JS_ASSERT(shape->hasTable());
     538                 : 
     539               0 :         PropertyTable &table = shape->table();
     540               0 :         for (uint32_t fslot = table.freelist; fslot != SHAPE_INVALID_SLOT;
     541               0 :              fslot = getSlot(fslot).toPrivateUint32()) {
     542               0 :             JS_ASSERT(fslot < slotSpan());
     543                 :         }
     544                 : 
     545               0 :         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
     546               0 :             JS_ASSERT_IF(shape != lastProperty(), !shape->hasTable());
     547                 : 
     548               0 :             Shape **spp = table.search(shape->propid(), false);
     549               0 :             JS_ASSERT(SHAPE_FETCH(spp) == shape);
     550                 :         }
     551                 : 
     552               0 :         shape = lastProperty();
     553               0 :         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
     554               0 :             JS_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
     555               0 :             if (!prev) {
     556               0 :                 JS_ASSERT(shape == lastProperty());
     557               0 :                 JS_ASSERT(shape->listp == &shape_);
     558                 :             } else {
     559               0 :                 JS_ASSERT(shape->listp == &prev->parent);
     560                 :             }
     561               0 :             prev = shape;
     562                 :         }
     563                 :     } else {
     564               0 :         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
     565               0 :             if (shape->hasTable()) {
     566               0 :                 PropertyTable &table = shape->table();
     567               0 :                 JS_ASSERT(shape->parent);
     568               0 :                 for (Shape::Range r(shape); !r.empty(); r.popFront()) {
     569               0 :                     Shape **spp = table.search(r.front().propid(), false);
     570               0 :                     JS_ASSERT(SHAPE_FETCH(spp) == &r.front());
     571                 :                 }
     572                 :             }
     573               0 :             if (prev) {
     574               0 :                 JS_ASSERT(prev->maybeSlot() >= shape->maybeSlot());
     575               0 :                 shape->kids.checkConsistency(prev);
     576                 :             }
     577               0 :             prev = shape;
     578                 :         }
     579                 :     }
     580                 : }
     581                 : #else
     582                 : # define CHECK_SHAPE_CONSISTENCY(obj) ((void)0)
     583                 : #endif
     584                 : 
     585                 : Shape *
     586         2073662 : JSObject::addProperty(JSContext *cx, jsid id,
     587                 :                       PropertyOp getter, StrictPropertyOp setter,
     588                 :                       uint32_t slot, unsigned attrs,
     589                 :                       unsigned flags, int shortid, bool allowDictionary)
     590                 : {
     591         2073662 :     JS_ASSERT(!JSID_IS_VOID(id));
     592                 : 
     593         2073662 :     if (!isExtensible()) {
     594               0 :         reportNotExtensible(cx);
     595               0 :         return NULL;
     596                 :     }
     597                 : 
     598         2073662 :     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
     599                 : 
     600         2073662 :     Shape **spp = NULL;
     601         2073662 :     if (inDictionaryMode())
     602         1147873 :         spp = lastProperty()->table().search(id, true);
     603                 : 
     604                 :     return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid,
     605         2073662 :                                spp, allowDictionary);
     606                 : }
     607                 : 
     608                 : Shape *
     609        41063073 : JSObject::addPropertyInternal(JSContext *cx, jsid id,
     610                 :                               PropertyOp getter, StrictPropertyOp setter,
     611                 :                               uint32_t slot, unsigned attrs,
     612                 :                               unsigned flags, int shortid, Shape **spp,
     613                 :                               bool allowDictionary)
     614                 : {
     615        41063073 :     JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
     616                 : 
     617        82126146 :     RootId idRoot(cx, &id);
     618        82126146 :     RootedVarObject self(cx, this);
     619                 : 
     620        41063073 :     PropertyTable *table = NULL;
     621        41063073 :     if (!inDictionaryMode()) {
     622                 :         bool stableSlot =
     623                 :             (slot == SHAPE_INVALID_SLOT) ||
     624         1556934 :             lastProperty()->hasMissingSlot() ||
     625        39060500 :             (slot == lastProperty()->maybeSlot() + 1);
     626        37503566 :         JS_ASSERT_IF(!allowDictionary, stableSlot);
     627       110691636 :         if (allowDictionary &&
     628        73188070 :             (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
     629           37853 :             if (!toDictionaryMode(cx))
     630               0 :                 return NULL;
     631           37853 :             table = &lastProperty()->table();
     632           37853 :             spp = table->search(id, true);
     633                 :         }
     634                 :     } else {
     635         3559507 :         table = &lastProperty()->table();
     636         3559507 :         if (table->needsToGrow()) {
     637           23531 :             if (!table->grow(cx))
     638               0 :                 return NULL;
     639           23531 :             spp = table->search(id, true);
     640           23531 :             JS_ASSERT(!SHAPE_FETCH(spp));
     641                 :         }
     642                 :     }
     643                 : 
     644        41063073 :     JS_ASSERT(!!table == !!spp);
     645                 : 
     646                 :     /* Find or create a property tree node labeled by our arguments. */
     647                 :     Shape *shape;
     648                 :     {
     649        41063073 :         shape = self->lastProperty();
     650                 : 
     651                 :         uint32_t index;
     652        41063073 :         bool indexed = js_IdIsIndex(id, &index);
     653                 :         UnownedBaseShape *nbase;
     654        41063073 :         if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
     655        22586688 :             nbase = shape->base()->unowned();
     656                 :         } else {
     657        18476385 :             StackBaseShape base(shape->base());
     658        18476385 :             base.updateGetterSetter(attrs, getter, setter);
     659        18476385 :             if (indexed)
     660         3870495 :                 base.flags |= BaseShape::INDEXED;
     661        18476385 :             nbase = BaseShape::getUnowned(cx, base);
     662        18476385 :             if (!nbase)
     663               0 :                 return NULL;
     664                 :         }
     665                 : 
     666        41063073 :         StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
     667        41063073 :         shape = self->getChildProperty(cx, self->lastProperty(), child);
     668                 :     }
     669                 : 
     670        41063073 :     if (shape) {
     671        41063073 :         JS_ASSERT(shape == self->lastProperty());
     672                 : 
     673        41063073 :         if (table) {
     674                 :             /* Store the tree node pointer in the table entry for id. */
     675         3597360 :             SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
     676         3597360 :             ++table->entryCount;
     677                 : 
     678                 :             /* Pass the table along to the new last property, namely shape. */
     679         3597360 :             JS_ASSERT(&shape->parent->table() == table);
     680         3597360 :             shape->parent->handoffTableTo(shape);
     681                 :         }
     682                 : 
     683        41063073 :         CHECK_SHAPE_CONSISTENCY(self);
     684        41063073 :         return shape;
     685                 :     }
     686                 : 
     687               0 :     CHECK_SHAPE_CONSISTENCY(self);
     688               0 :     return NULL;
     689                 : }
     690                 : 
     691                 : /*
     692                 :  * Check and adjust the new attributes for the shape to make sure that our
     693                 :  * slot access optimizations are sound. It is responsibility of the callers to
     694                 :  * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
     695                 :  */
     696                 : inline bool
     697         2479732 : CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, unsigned *attrsp)
     698                 : {
     699         2479732 :     if (shape->configurable())
     700         2194289 :         return true;
     701                 : 
     702                 :     /* A permanent property must stay permanent. */
     703          285443 :     *attrsp |= JSPROP_PERMANENT;
     704                 : 
     705                 :     /* Reject attempts to remove a slot from the permanent data property. */
     706          285443 :     if (shape->isDataDescriptor() && shape->hasSlot() &&
     707                 :         (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
     708               0 :         obj->reportNotConfigurable(cx, shape->propid());
     709               0 :         return false;
     710                 :     }
     711                 : 
     712          285443 :     return true;
     713                 : }
     714                 : 
     715                 : Shape *
     716        40506556 : JSObject::putProperty(JSContext *cx, jsid id,
     717                 :                       PropertyOp getter, StrictPropertyOp setter,
     718                 :                       uint32_t slot, unsigned attrs,
     719                 :                       unsigned flags, int shortid)
     720                 : {
     721        40506556 :     JS_ASSERT(!JSID_IS_VOID(id));
     722                 : 
     723        81013112 :     RootId idRoot(cx, &id);
     724                 : 
     725        40506556 :     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
     726                 : 
     727        81013112 :     RootedVarObject self(cx, this);
     728                 : 
     729                 :     /* Search for id in order to claim its entry if table has been allocated. */
     730                 :     Shape **spp;
     731        40506556 :     Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true);
     732        40506556 :     if (!shape) {
     733                 :         /*
     734                 :          * You can't add properties to a non-extensible object, but you can change
     735                 :          * attributes of properties in such objects.
     736                 :          */
     737        38097111 :         if (!self->isExtensible()) {
     738               0 :             self->reportNotExtensible(cx);
     739               0 :             return NULL;
     740                 :         }
     741                 : 
     742        38097111 :         return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
     743                 :     }
     744                 : 
     745                 :     /* Property exists: search must have returned a valid *spp. */
     746         2409445 :     JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp));
     747                 : 
     748         4818890 :     RootShape shapeRoot(cx, &shape);
     749                 : 
     750         2409445 :     if (!CheckCanChangeAttrs(cx, self, shape, &attrs))
     751               0 :         return NULL;
     752                 :     
     753                 :     /*
     754                 :      * If the caller wants to allocate a slot, but doesn't care which slot,
     755                 :      * copy the existing shape's slot into slot so we can match shape, if all
     756                 :      * other members match.
     757                 :      */
     758         2409445 :     bool hadSlot = shape->hasSlot();
     759         2409445 :     uint32_t oldSlot = shape->maybeSlot();
     760         2409445 :     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
     761         2320495 :         slot = oldSlot;
     762                 : 
     763         4818890 :     RootedVar<UnownedBaseShape*> nbase(cx);
     764                 :     {
     765                 :         uint32_t index;
     766         2409445 :         bool indexed = js_IdIsIndex(id, &index);
     767         2409445 :         StackBaseShape base(self->lastProperty()->base());
     768         2409445 :         base.updateGetterSetter(attrs, getter, setter);
     769         2409445 :         if (indexed)
     770             126 :             base.flags |= BaseShape::INDEXED;
     771         2409445 :         nbase = BaseShape::getUnowned(cx, base);
     772         2409445 :         if (!nbase)
     773               0 :             return NULL;
     774                 :     }
     775                 : 
     776                 :     /*
     777                 :      * Now that we've possibly preserved slot, check whether all members match.
     778                 :      * If so, this is a redundant "put" and we can return without more work.
     779                 :      */
     780         2409445 :     if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
     781         2271937 :         return shape;
     782                 : 
     783                 :     /*
     784                 :      * Overwriting a non-last property requires switching to dictionary mode.
     785                 :      * The shape tree is shared immutable, and we can't removeProperty and then
     786                 :      * addPropertyInternal because a failure under add would lose data.
     787                 :      */
     788          137508 :     if (shape != self->lastProperty() && !self->inDictionaryMode()) {
     789           50562 :         if (!self->toDictionaryMode(cx))
     790               0 :             return NULL;
     791           50562 :         spp = self->lastProperty()->table().search(shape->propid(), false);
     792           50562 :         shape = SHAPE_FETCH(spp);
     793                 :     }
     794                 : 
     795          137508 :     JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
     796                 : 
     797          137508 :     if (self->inDictionaryMode()) {
     798                 :         /*
     799                 :          * Updating some property in a dictionary-mode object. Create a new
     800                 :          * shape for the existing property, and also generate a new shape for
     801                 :          * the last property of the dictionary (unless the modified property
     802                 :          * is also the last property).
     803                 :          */
     804          117918 :         bool updateLast = (shape == self->lastProperty());
     805          117918 :         shape = self->replaceWithNewEquivalentShape(cx, shape);
     806          117918 :         if (!shape)
     807               0 :             return NULL;
     808          117918 :         if (!updateLast && !self->generateOwnShape(cx))
     809               0 :             return NULL;
     810                 : 
     811                 :         /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
     812          117918 :         if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
     813               9 :             if (!self->allocSlot(cx, &slot))
     814               0 :                 return NULL;
     815                 :         }
     816                 : 
     817          117918 :         if (updateLast)
     818           16799 :             shape->base()->adoptUnowned(nbase);
     819                 :         else
     820          101119 :             shape->base_ = nbase;
     821                 : 
     822          117918 :         shape->setSlot(slot);
     823          117918 :         shape->attrs = uint8_t(attrs);
     824          117918 :         shape->flags = flags | Shape::IN_DICTIONARY;
     825          117918 :         shape->shortid_ = int16_t(shortid);
     826                 :     } else {
     827                 :         /*
     828                 :          * Updating the last property in a non-dictionary-mode object. Find an
     829                 :          * alternate shared child of the last property's previous shape.
     830                 :          */
     831           19590 :         StackBaseShape base(self->lastProperty()->base());
     832           19590 :         base.updateGetterSetter(attrs, getter, setter);
     833           19590 :         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     834           19590 :         if (!nbase)
     835               0 :             return NULL;
     836                 : 
     837           19590 :         JS_ASSERT(shape == self->lastProperty());
     838                 : 
     839                 :         /* Find or create a property tree node labeled by our arguments. */
     840           19590 :         StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
     841           19590 :         Shape *newShape = self->getChildProperty(cx, shape->parent, child);
     842                 : 
     843           19590 :         if (!newShape) {
     844               0 :             CHECK_SHAPE_CONSISTENCY(self);
     845               0 :             return NULL;
     846                 :         }
     847                 : 
     848           19590 :         shape = newShape;
     849                 :     }
     850                 : 
     851                 :     /*
     852                 :      * Can't fail now, so free the previous incarnation's slot if the new shape
     853                 :      * has no slot. But we do not need to free oldSlot (and must not, as trying
     854                 :      * to will botch an assertion in JSObject::freeSlot) if the new last
     855                 :      * property (shape here) has a slotSpan that does not cover it.
     856                 :      */
     857          137508 :     if (hadSlot && !shape->hasSlot()) {
     858             348 :         if (oldSlot < self->slotSpan())
     859             294 :             self->freeSlot(cx, oldSlot);
     860             348 :         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     861                 :     }
     862                 : 
     863          137508 :     CHECK_SHAPE_CONSISTENCY(self);
     864                 : 
     865          137508 :     return shape;
     866                 : }
     867                 : 
     868                 : Shape *
     869           70287 : JSObject::changeProperty(JSContext *cx, Shape *shape, unsigned attrs, unsigned mask,
     870                 :                          PropertyOp getter, StrictPropertyOp setter)
     871                 : {
     872           70287 :     JS_ASSERT(nativeContains(cx, *shape));
     873                 : 
     874           70287 :     attrs |= shape->attrs & mask;
     875                 : 
     876                 :     /* Allow only shared (slotless) => unshared (slotful) transition. */
     877           70287 :     JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
     878           70287 :               !(attrs & JSPROP_SHARED));
     879                 : 
     880                 :     /* Don't allow method properties to be changed to have a getter or setter. */
     881           70287 :     JS_ASSERT_IF(shape->isMethod(), !getter && !setter);
     882                 : 
     883           70287 :     types::MarkTypePropertyConfigured(cx, this, shape->propid());
     884           70287 :     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
     885           29662 :         types::AddTypePropertyId(cx, this, shape->propid(), types::Type::UnknownType());
     886                 : 
     887           70287 :     if (getter == JS_PropertyStub)
     888               0 :         getter = NULL;
     889           70287 :     if (setter == JS_StrictPropertyStub)
     890               0 :         setter = NULL;
     891                 : 
     892           70287 :     if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
     893               0 :         return NULL;
     894                 :     
     895           70287 :     if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
     896               0 :         return shape;
     897                 : 
     898                 :     /*
     899                 :      * Let JSObject::putProperty handle this |overwriting| case, including
     900                 :      * the conservation of shape->slot (if it's valid). We must not call
     901                 :      * removeProperty because it will free an allocated shape->slot, and
     902                 :      * putProperty won't re-allocate it.
     903                 :      */
     904           70287 :     Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
     905          140574 :                                   attrs, shape->flags, shape->maybeShortid());
     906                 : 
     907           70287 :     CHECK_SHAPE_CONSISTENCY(this);
     908           70287 :     return newShape;
     909                 : }
     910                 : 
     911                 : bool
     912          485882 : JSObject::removeProperty(JSContext *cx, jsid id)
     913                 : {
     914          971764 :     RootedVarObject self(cx, this);
     915                 : 
     916          971764 :     RootId idRoot(cx, &id);
     917          971764 :     RootedVarShape shape(cx);
     918                 : 
     919                 :     Shape **spp;
     920          485882 :     shape = Shape::search(cx, lastProperty(), id, &spp);
     921          485882 :     if (!shape)
     922               0 :         return true;
     923                 : 
     924                 :     /*
     925                 :      * If shape is not the last property added, or the last property cannot
     926                 :      * be removed, switch to dictionary mode.
     927                 :      */
     928          485882 :     if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
     929            9499 :         if (!self->toDictionaryMode(cx))
     930               0 :             return false;
     931            9499 :         spp = self->lastProperty()->table().search(shape->propid(), false);
     932            9499 :         shape = SHAPE_FETCH(spp);
     933                 :     }
     934                 : 
     935                 :     /*
     936                 :      * If in dictionary mode, get a new shape for the last property after the
     937                 :      * removal. We need a fresh shape for all dictionary deletions, even of
     938                 :      * the last property. Otherwise, a shape could replay and caches might
     939                 :      * return deleted DictionaryShapes! See bug 595365. Do this before changing
     940                 :      * the object or table, so the remaining removal is infallible.
     941                 :      */
     942          485882 :     Shape *spare = NULL;
     943          485882 :     if (self->inDictionaryMode()) {
     944          481706 :         spare = js_NewGCShape(cx);
     945          481706 :         if (!spare)
     946               0 :             return false;
     947          481706 :         new (spare) Shape(shape->base()->unowned(), 0);
     948          481706 :         if (shape == lastProperty()) {
     949                 :             /*
     950                 :              * Get an up to date unowned base shape for the new last property
     951                 :              * when removing the dictionary's last property. Information in
     952                 :              * base shapes for non-last properties may be out of sync with the
     953                 :              * object's state.
     954                 :              */
     955          193897 :             Shape *previous = lastProperty()->parent;
     956          193897 :             StackBaseShape base(lastProperty()->base());
     957          193897 :             base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
     958          193897 :             BaseShape *nbase = BaseShape::getUnowned(cx, base);
     959          193897 :             if (!nbase)
     960               0 :                 return false;
     961          193897 :             previous->base_ = nbase;
     962                 :         }
     963                 :     }
     964                 : 
     965                 :     /* If shape has a slot, free its slot number. */
     966          485882 :     if (shape->hasSlot()) {
     967          447843 :         self->freeSlot(cx, shape->slot());
     968          447843 :         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     969                 :     }
     970                 : 
     971                 :     /*
     972                 :      * A dictionary-mode object owns mutable, unique shapes on a non-circular
     973                 :      * doubly linked list, hashed by lastProperty()->table. So we can edit the
     974                 :      * list and hash in place.
     975                 :      */
     976          485882 :     if (self->inDictionaryMode()) {
     977          481706 :         PropertyTable &table = self->lastProperty()->table();
     978                 : 
     979          481706 :         if (SHAPE_HAD_COLLISION(*spp)) {
     980          149983 :             *spp = SHAPE_REMOVED;
     981          149983 :             ++table.removedCount;
     982          149983 :             --table.entryCount;
     983                 :         } else {
     984          331723 :             *spp = NULL;
     985          331723 :             --table.entryCount;
     986                 : 
     987                 : #ifdef DEBUG
     988                 :             /*
     989                 :              * Check the consistency of the table but limit the number of
     990                 :              * checks not to alter significantly the complexity of the
     991                 :              * delete in debug builds, see bug 534493.
     992                 :              */
     993          331723 :             const Shape *aprop = self->lastProperty();
     994        13268528 :             for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
     995        12936805 :                 JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, *aprop));
     996                 : #endif
     997                 :         }
     998                 : 
     999                 :         /* Remove shape from its non-circular doubly linked list. */
    1000          481706 :         Shape *oldLastProp = self->lastProperty();
    1001          481706 :         shape->removeFromDictionary(self);
    1002                 : 
    1003                 :         /* Hand off table from the old to new last property. */
    1004          481706 :         oldLastProp->handoffTableTo(self->lastProperty());
    1005                 : 
    1006                 :         /* Generate a new shape for the object, infallibly. */
    1007          481706 :         JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
    1008                 : 
    1009                 :         /* Consider shrinking table if its load factor is <= .25. */
    1010          481706 :         uint32_t size = table.capacity();
    1011          481706 :         if (size > PropertyTable::MIN_SIZE && table.entryCount <= size >> 2)
    1012           11318 :             (void) table.change(-1, cx);
    1013                 :     } else {
    1014                 :         /*
    1015                 :          * Non-dictionary-mode property tables are shared immutables, so all we
    1016                 :          * need do is retract the last property and we'll either get or else
    1017                 :          * lazily make via a later hashify the exact table for the new property
    1018                 :          * lineage.
    1019                 :          */
    1020            4176 :         JS_ASSERT(shape == self->lastProperty());
    1021            4176 :         self->removeLastProperty(cx);
    1022                 :     }
    1023                 : 
    1024          485882 :     CHECK_SHAPE_CONSISTENCY(self);
    1025          485882 :     return true;
    1026                 : }
    1027                 : 
    1028                 : void
    1029              10 : JSObject::clear(JSContext *cx)
    1030                 : {
    1031              10 :     Shape *shape = lastProperty();
    1032              10 :     JS_ASSERT(inDictionaryMode() == shape->inDictionary());
    1033                 : 
    1034              20 :     while (shape->parent) {
    1035               0 :         shape = shape->parent;
    1036               0 :         JS_ASSERT(inDictionaryMode() == shape->inDictionary());
    1037                 :     }
    1038              10 :     JS_ASSERT(shape->isEmptyShape());
    1039                 : 
    1040              10 :     if (inDictionaryMode())
    1041               0 :         shape->listp = &shape_;
    1042                 : 
    1043              10 :     JS_ALWAYS_TRUE(setLastProperty(cx, shape));
    1044                 : 
    1045              10 :     JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
    1046              10 :     CHECK_SHAPE_CONSISTENCY(this);
    1047              10 : }
    1048                 : 
    1049                 : void
    1050              20 : JSObject::rollbackProperties(JSContext *cx, uint32_t slotSpan)
    1051                 : {
    1052                 :     /*
    1053                 :      * Remove properties from this object until it has a matching slot span.
    1054                 :      * The object cannot have escaped in a way which would prevent safe
    1055                 :      * removal of the last properties.
    1056                 :      */
    1057              20 :     JS_ASSERT(!inDictionaryMode() && slotSpan <= this->slotSpan());
    1058              85 :     while (this->slotSpan() != slotSpan) {
    1059              45 :         JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
    1060              45 :         removeLastProperty(cx);
    1061                 :     }
    1062              20 : }
    1063                 : 
    1064                 : Shape *
    1065         1026648 : JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape)
    1066                 : {
    1067         1228886 :     JS_ASSERT_IF(oldShape != lastProperty(),
    1068                 :                  inDictionaryMode() &&
    1069         1228886 :                  nativeLookup(cx, oldShape->propidRef()) == oldShape);
    1070                 : 
    1071         1026648 :     JSObject *self = this;
    1072                 : 
    1073         1026648 :     if (!inDictionaryMode()) {
    1074          127768 :         RootObject selfRoot(cx, &self);
    1075          127768 :         RootShape newRoot(cx, &newShape);
    1076           63884 :         if (!toDictionaryMode(cx))
    1077               0 :             return NULL;
    1078          127768 :         oldShape = lastProperty();
    1079                 :     }
    1080                 : 
    1081         1026648 :     if (!newShape) {
    1082         1089884 :         RootObject selfRoot(cx, &self);
    1083         1089884 :         RootShape oldRoot(cx, &oldShape);
    1084          544942 :         newShape = js_NewGCShape(cx);
    1085          544942 :         if (!newShape)
    1086               0 :             return NULL;
    1087          544942 :         new (newShape) Shape(oldShape->base()->unowned(), 0);
    1088                 :     }
    1089                 : 
    1090         1026648 :     PropertyTable &table = self->lastProperty()->table();
    1091         1026648 :     Shape **spp = oldShape->isEmptyShape()
    1092                 :                   ? NULL
    1093         1026648 :                   : table.search(oldShape->propidRef(), false);
    1094                 : 
    1095                 :     /*
    1096                 :      * Splice the new shape into the same position as the old shape, preserving
    1097                 :      * enumeration order (see bug 601399).
    1098                 :      */
    1099         1026648 :     StackShape nshape(oldShape);
    1100         1026648 :     newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp);
    1101                 : 
    1102         1026648 :     JS_ASSERT(newShape->parent == oldShape);
    1103         1026648 :     oldShape->removeFromDictionary(self);
    1104                 : 
    1105         1026648 :     if (newShape == lastProperty())
    1106          925529 :         oldShape->handoffTableTo(newShape);
    1107                 : 
    1108         1026648 :     if (spp)
    1109         1009981 :         SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
    1110         1026648 :     return newShape;
    1111                 : }
    1112                 : 
    1113                 : Shape *
    1114               0 : JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
    1115                 : {
    1116               0 :     JS_ASSERT(shape.isMethod());
    1117                 : 
    1118               0 :     if (!inDictionaryMode() && !toDictionaryMode(cx))
    1119               0 :         return NULL;
    1120                 : 
    1121               0 :     Shape *spare = js_NewGCShape(cx);
    1122               0 :     if (!spare)
    1123               0 :         return NULL;
    1124               0 :     new (spare) Shape(shape.base()->unowned(), 0);
    1125                 : 
    1126                 : #ifdef DEBUG
    1127               0 :     JS_ASSERT(canHaveMethodBarrier());
    1128               0 :     JS_ASSERT(!shape.setter());
    1129               0 :     JS_ASSERT(!shape.hasShortID());
    1130                 : #endif
    1131                 : 
    1132                 :     /*
    1133                 :      * Clear Shape::METHOD from flags as we are despecializing from a
    1134                 :      * method memoized in the property tree to a plain old function-valued
    1135                 :      * property.
    1136                 :      */
    1137                 :     Shape *result =
    1138               0 :         putProperty(cx, shape.propid(), NULL, NULL, shape.slot(),
    1139                 :                     shape.attrs,
    1140               0 :                     shape.getFlags() & ~Shape::METHOD,
    1141               0 :                     0);
    1142               0 :     if (!result)
    1143               0 :         return NULL;
    1144                 : 
    1145               0 :     if (result != lastProperty())
    1146               0 :         JS_ALWAYS_TRUE(generateOwnShape(cx, spare));
    1147                 : 
    1148               0 :     return result;
    1149                 : }
    1150                 : 
    1151                 : bool
    1152          231984 : JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
    1153                 : {
    1154          231984 :     return generateOwnShape(cx);
    1155                 : }
    1156                 : 
    1157                 : bool
    1158         1074117 : JSObject::clearParent(JSContext *cx)
    1159                 : {
    1160         1074117 :     return setParent(cx, NULL);
    1161                 : }
    1162                 : 
    1163                 : bool
    1164         1439594 : JSObject::setParent(JSContext *cx, JSObject *parent)
    1165                 : {
    1166         1439594 :     if (parent && !parent->setDelegate(cx))
    1167               0 :         return false;
    1168                 : 
    1169         1439594 :     if (inDictionaryMode()) {
    1170               0 :         StackBaseShape base(lastProperty());
    1171               0 :         base.parent = parent;
    1172               0 :         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
    1173               0 :         if (!nbase)
    1174               0 :             return false;
    1175                 : 
    1176               0 :         lastProperty()->base()->adoptUnowned(nbase);
    1177               0 :         return true;
    1178                 :     }
    1179                 : 
    1180         1439594 :     Shape *newShape = Shape::setObjectParent(cx, parent, getProto(), shape_);
    1181         1439594 :     if (!newShape)
    1182               0 :         return false;
    1183                 : 
    1184         1439594 :     shape_ = newShape;
    1185         1439594 :     return true;
    1186                 : }
    1187                 : 
    1188                 : /* static */ Shape *
    1189         1594787 : Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, Shape *last)
    1190                 : {
    1191         1594787 :     if (last->getObjectParent() == parent)
    1192           69708 :         return last;
    1193                 : 
    1194         1525079 :     StackBaseShape base(last);
    1195         1525079 :     base.parent = parent;
    1196                 : 
    1197         1525079 :     return replaceLastProperty(cx, base, proto, last);
    1198                 : }
    1199                 : 
    1200                 : bool
    1201         1261940 : JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
    1202                 : {
    1203         1261940 :     JS_ASSERT(isExtensible());
    1204                 : 
    1205         2523880 :     RootedVarObject self(cx, this);
    1206                 : 
    1207         1261940 :     if (props) {
    1208         1261940 :         if (js::FixOp fix = getOps()->fix) {
    1209                 :             bool success;
    1210              86 :             if (!fix(cx, this, &success, props))
    1211               0 :                 return false;
    1212              86 :             if (!success) {
    1213               0 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
    1214               0 :                 return false;
    1215                 :             }
    1216                 :         } else {
    1217         1261854 :             if (!js::GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
    1218               0 :                 return false;
    1219                 :         }
    1220                 :     }
    1221                 : 
    1222         1261940 :     return self->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE);
    1223                 : }
    1224                 : 
    1225                 : bool
    1226         7970313 : JSObject::setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag_, GenerateShape generateShape)
    1227                 : {
    1228         7970313 :     BaseShape::Flag flag = (BaseShape::Flag) flag_;
    1229                 : 
    1230         7970313 :     if (lastProperty()->getObjectFlags() & flag)
    1231         4095856 :         return true;
    1232                 : 
    1233         7748914 :     RootedVarObject self(cx, this);
    1234                 : 
    1235         3874457 :     if (inDictionaryMode()) {
    1236           61426 :         if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx))
    1237               0 :             return false;
    1238           61426 :         StackBaseShape base(self->lastProperty());
    1239           61426 :         base.flags |= flag;
    1240           61426 :         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
    1241           61426 :         if (!nbase)
    1242               0 :             return false;
    1243                 : 
    1244           61426 :         self->lastProperty()->base()->adoptUnowned(nbase);
    1245           61426 :         return true;
    1246                 :     }
    1247                 : 
    1248         3813031 :     Shape *newShape = Shape::setObjectFlag(cx, flag, getProto(), lastProperty());
    1249         3813031 :     if (!newShape)
    1250               0 :         return false;
    1251                 : 
    1252         3813031 :     self->shape_ = newShape;
    1253         3813031 :     return true;
    1254                 : }
    1255                 : 
    1256                 : /* static */ Shape *
    1257         3813031 : Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last)
    1258                 : {
    1259         3813031 :     if (last->getObjectFlags() & flag)
    1260               0 :         return last;
    1261                 : 
    1262         3813031 :     StackBaseShape base(last);
    1263         3813031 :     base.flags |= flag;
    1264                 : 
    1265         3813031 :     return replaceLastProperty(cx, base, proto, last);
    1266                 : }
    1267                 : 
    1268                 : /* static */ inline HashNumber
    1269        28762737 : StackBaseShape::hash(const StackBaseShape *base)
    1270                 : {
    1271        28762737 :     JSDHashNumber hash = base->flags;
    1272        28762737 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
    1273        28762737 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3);
    1274        28762737 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawGetter);
    1275        28762737 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawSetter);
    1276        28762737 :     return hash;
    1277                 : }
    1278                 : 
    1279                 : /* static */ inline bool
    1280        21564424 : StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
    1281                 : {
    1282                 :     return key->flags == lookup->flags
    1283                 :         && key->clasp == lookup->clasp
    1284        21558846 :         && key->parent == lookup->parent
    1285                 :         && key->rawGetter == lookup->rawGetter
    1286        43123270 :         && key->rawSetter == lookup->rawSetter;
    1287                 : }
    1288                 : 
    1289                 : /* Root for stack allocated base shapes. */
    1290                 : class RootStackBaseShape
    1291         7215781 : {
    1292                 :     Root<const JSObject*> parentRoot;
    1293                 :     Maybe<RootObject> getterRoot;
    1294                 :     Maybe<RootObject> setterRoot;
    1295                 : 
    1296                 :   public:
    1297         7215781 :     RootStackBaseShape(JSContext *cx, const StackBaseShape *base)
    1298         7215781 :         : parentRoot(cx, &base->parent)
    1299                 :     {
    1300         7215781 :         if (base->flags & BaseShape::HAS_GETTER_OBJECT)
    1301         2423342 :             getterRoot.construct(cx, (JSObject **) &base->rawGetter);
    1302         7215781 :         if (base->flags & BaseShape::HAS_SETTER_OBJECT)
    1303          438964 :             setterRoot.construct(cx, (JSObject **) &base->rawSetter);
    1304         7215781 :     }
    1305                 : };
    1306                 : 
    1307                 : /* static */ UnownedBaseShape *
    1308        28762737 : BaseShape::getUnowned(JSContext *cx, const StackBaseShape &base)
    1309                 : {
    1310        28762737 :     BaseShapeSet &table = cx->compartment->baseShapes;
    1311                 : 
    1312        28762737 :     if (!table.initialized() && !table.init())
    1313               0 :         return NULL;
    1314                 : 
    1315        28762737 :     BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
    1316                 : 
    1317        28762737 :     if (p)
    1318        21546956 :         return *p;
    1319                 : 
    1320        14431562 :     RootStackBaseShape root(cx, &base);
    1321                 : 
    1322         7215781 :     BaseShape *nbase_ = js_NewGCBaseShape(cx);
    1323         7215781 :     if (!nbase_)
    1324               0 :         return NULL;
    1325         7215781 :     new (nbase_) BaseShape(base);
    1326                 : 
    1327         7215781 :     UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
    1328                 : 
    1329         7215781 :     if (!table.relookupOrAdd(p, &base, nbase))
    1330               0 :         return NULL;
    1331                 : 
    1332         7215781 :     return nbase;
    1333                 : }
    1334                 : 
    1335                 : void
    1336          122478 : JSCompartment::sweepBaseShapeTable(JSContext *cx)
    1337                 : {
    1338          122478 :     if (baseShapes.initialized()) {
    1339        21127213 :         for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
    1340        21055611 :             UnownedBaseShape *base = e.front();
    1341        21055611 :             if (!base->isMarked())
    1342         7214926 :                 e.removeFront();
    1343                 :         }
    1344                 :     }
    1345          122478 : }
    1346                 : 
    1347                 : void
    1348         7566440 : BaseShape::finalize(JSContext *cx, bool background)
    1349                 : {
    1350         7566440 :     if (table_) {
    1351          351514 :         cx->delete_(table_);
    1352          351514 :         table_ = NULL;
    1353                 :     }
    1354         7566440 : }
    1355                 : 
    1356                 : /* static */ Shape *
    1357           16945 : Shape::setExtensibleParents(JSContext *cx, Shape *shape)
    1358                 : {
    1359           16945 :     JS_ASSERT(!shape->inDictionary());
    1360                 : 
    1361           16945 :     StackBaseShape base(shape);
    1362           16945 :     base.flags |= BaseShape::EXTENSIBLE_PARENTS;
    1363                 : 
    1364                 :     /* This is only used for Block and Call objects, which have a NULL proto. */
    1365           16945 :     return replaceLastProperty(cx, base, NULL, shape);
    1366                 : }
    1367                 : 
    1368                 : bool
    1369            8413 : Bindings::setExtensibleParents(JSContext *cx)
    1370                 : {
    1371            8413 :     if (!ensureShape(cx))
    1372               0 :         return false;
    1373            8413 :     Shape *newShape = Shape::setExtensibleParents(cx, lastBinding);
    1374            8413 :     if (!newShape)
    1375               0 :         return false;
    1376            8413 :     lastBinding = newShape;
    1377            8413 :     return true;
    1378                 : }
    1379                 : 
    1380                 : bool
    1381          155193 : Bindings::setParent(JSContext *cx, JSObject *obj)
    1382                 : {
    1383                 :     /*
    1384                 :      * This may be invoked on GC heap allocated bindings, in which case this
    1385                 :      * is pointing to an internal value of a JSScript that can't itself be
    1386                 :      * relocated. The script itself will be rooted, and will not be moved, so
    1387                 :      * mark the stack value as non-relocatable for the stack root analysis.
    1388                 :      */
    1389          155193 :     Bindings *self = this;
    1390          310386 :     CheckRoot root(cx, &self);
    1391                 : 
    1392          310386 :     RootObject rootObj(cx, &obj);
    1393                 : 
    1394          155193 :     if (!ensureShape(cx))
    1395               0 :         return false;
    1396                 : 
    1397                 :     /* This is only used for Block objects, which have a NULL proto. */
    1398          155193 :     Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding);
    1399          155193 :     if (!newShape)
    1400               0 :         return false;
    1401          155193 :     self->lastBinding = newShape;
    1402          155193 :     return true;
    1403                 : }
    1404                 : 
    1405                 : /* static */ inline HashNumber
    1406        17387944 : InitialShapeEntry::hash(const Lookup &lookup)
    1407                 : {
    1408        17387944 :     JSDHashNumber hash = uintptr_t(lookup.clasp) >> 3;
    1409        17387944 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.proto) >> 3);
    1410        17387944 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.parent) >> 3);
    1411        17387944 :     return hash + lookup.nfixed;
    1412                 : }
    1413                 : 
    1414                 : /* static */ inline bool
    1415        20279258 : InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
    1416                 : {
    1417        20279258 :     return lookup.clasp == key.shape->getObjectClass()
    1418                 :         && lookup.proto == key.proto
    1419        20275989 :         && lookup.parent == key.shape->getObjectParent()
    1420        20275989 :         && lookup.nfixed == key.shape->numFixedSlots()
    1421        81110494 :         && lookup.baseFlags == key.shape->getObjectFlags();
    1422                 : }
    1423                 : 
    1424                 : /* static */ Shape *
    1425        17352641 : EmptyShape::getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
    1426                 :                             AllocKind kind, uint32_t objectFlags)
    1427                 : {
    1428        17352641 :     InitialShapeSet &table = cx->compartment->initialShapes;
    1429                 : 
    1430        17352641 :     if (!table.initialized() && !table.init())
    1431               0 :         return NULL;
    1432                 : 
    1433        17352641 :     size_t nfixed = GetGCKindSlots(kind, clasp);
    1434        17352641 :     InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags);
    1435                 : 
    1436        17352641 :     InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
    1437                 : 
    1438        17352641 :     if (p)
    1439        13639917 :         return p->shape;
    1440                 : 
    1441         7425448 :     RootedVar<UnownedBaseShape*> nbase(cx);
    1442                 : 
    1443         3712724 :     StackBaseShape base(clasp, parent, objectFlags);
    1444         3712724 :     nbase = BaseShape::getUnowned(cx, base);
    1445         3712724 :     if (!nbase)
    1446               0 :         return NULL;
    1447                 : 
    1448         3712724 :     Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx);
    1449         3712724 :     if (!shape)
    1450               0 :         return NULL;
    1451         3712724 :     new (shape) EmptyShape(nbase, nfixed);
    1452                 : 
    1453         3712724 :     InitialShapeEntry entry;
    1454         3712724 :     entry.shape = shape;
    1455         3712724 :     entry.proto = proto;
    1456                 : 
    1457         3712724 :     if (!table.relookupOrAdd(p, lookup, entry))
    1458               0 :         return NULL;
    1459                 : 
    1460         3712724 :     return shape;
    1461                 : }
    1462                 : 
    1463                 : void
    1464           35303 : NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto)
    1465                 : {
    1466           35303 :     Class *clasp = shape->getObjectClass();
    1467                 : 
    1468           35303 :     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
    1469           35303 :     if (CanBeFinalizedInBackground(kind, clasp))
    1470           35303 :         kind = GetBackgroundAllocKind(kind);
    1471                 : 
    1472           35303 :     GlobalObject *global = &shape->getObjectParent()->global();
    1473           35303 :     types::TypeObject *type = proto->getNewType(cx);
    1474                 : 
    1475                 :     EntryIndex entry;
    1476           35303 :     if (lookupGlobal(clasp, global, kind, &entry))
    1477           20101 :         PodZero(&entries[entry]);
    1478           35303 :     if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry))
    1479           15202 :         PodZero(&entries[entry]);
    1480           35303 :     if (lookupType(clasp, type, kind, &entry))
    1481               0 :         PodZero(&entries[entry]);
    1482           35303 : }
    1483                 : 
    1484                 : /* static */ void
    1485           35303 : EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
    1486                 : {
    1487                 :     InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
    1488           35303 :                                      shape->numFixedSlots(), shape->getObjectFlags());
    1489                 : 
    1490           35303 :     InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
    1491           35303 :     JS_ASSERT(p);
    1492                 : 
    1493           35303 :     InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
    1494           35303 :     JS_ASSERT(entry.shape->isEmptyShape());
    1495                 : 
    1496                 :     /* The new shape had better be rooted at the old one. */
    1497                 : #ifdef DEBUG
    1498           35303 :     const Shape *nshape = shape;
    1499          183504 :     while (!nshape->isEmptyShape())
    1500          112898 :         nshape = nshape->previous();
    1501           35303 :     JS_ASSERT(nshape == entry.shape);
    1502                 : #endif
    1503                 : 
    1504           35303 :     entry.shape = shape;
    1505                 : 
    1506                 :     /*
    1507                 :      * This affects the shape that will be produced by the various NewObject
    1508                 :      * methods, so clear any cache entry referring to the old shape. This is
    1509                 :      * not required for correctness (though it may bust on the above asserts):
    1510                 :      * the NewObject must always check for a nativeEmpty() result and generate
    1511                 :      * the appropriate properties if found. Clearing the cache entry avoids
    1512                 :      * this duplicate regeneration.
    1513                 :      */
    1514           35303 :     cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
    1515           35303 : }
    1516                 : 
    1517                 : void
    1518          122478 : JSCompartment::sweepInitialShapeTable(JSContext *cx)
    1519                 : {
    1520          122478 :     if (initialShapes.initialized()) {
    1521        10259386 :         for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
    1522        10187784 :             const InitialShapeEntry &entry = e.front();
    1523        10187784 :             if (!entry.shape->isMarked() || (entry.proto && !entry.proto->isMarked()))
    1524         3712279 :                 e.removeFront();
    1525                 :         }
    1526                 :     }
    1527          122478 : }

Generated by: LCOV version 1.7