LCOV - code coverage report
Current view: directory - js/src - jsscope.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 665 582 87.5 %
Date: 2012-04-07 Functions: 44 44 100.0 %

       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 "jsutil.h"
      50                 : #include "jsapi.h"
      51                 : #include "jsatom.h"
      52                 : #include "jscntxt.h"
      53                 : #include "jsdbgapi.h"
      54                 : #include "jslock.h"
      55                 : #include "jsnum.h"
      56                 : #include "jsobj.h"
      57                 : #include "jsscope.h"
      58                 : #include "jsstr.h"
      59                 : 
      60                 : #include "js/MemoryMetrics.h"
      61                 : 
      62                 : #include "jsatominlines.h"
      63                 : #include "jsobjinlines.h"
      64                 : #include "jsscopeinlines.h"
      65                 : 
      66                 : using namespace js;
      67                 : using namespace js::gc;
      68                 : 
      69                 : bool
      70          127390 : PropertyTable::init(JSRuntime *rt, Shape *lastProp)
      71                 : {
      72                 :     /*
      73                 :      * Either we're creating a table for a large scope that was populated
      74                 :      * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
      75                 :      * JSOP_SETPROP; or else calloc failed at least once already. In any
      76                 :      * event, let's try to grow, overallocating to hold at least twice the
      77                 :      * current population.
      78                 :      */
      79          127390 :     uint32_t sizeLog2 = JS_CEILING_LOG2W(2 * entryCount);
      80          127390 :     if (sizeLog2 < MIN_SIZE_LOG2)
      81           17127 :         sizeLog2 = MIN_SIZE_LOG2;
      82                 : 
      83                 :     /*
      84                 :      * Use rt->calloc_ for memory accounting and overpressure handling
      85                 :      * without OOM reporting. See PropertyTable::change.
      86                 :      */
      87          127390 :     entries = (Shape **) rt->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
      88          127390 :     if (!entries)
      89               0 :         return false;
      90                 : 
      91          127390 :     hashShift = HASH_BITS - sizeLog2;
      92         3066744 :     for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
      93         2939354 :         const Shape &shape = r.front();
      94         2939354 :         Shape **spp = search(shape.propid(), true);
      95                 : 
      96                 :         /*
      97                 :          * Beware duplicate args and arg vs. var conflicts: the youngest shape
      98                 :          * (nearest to lastProp) must win. See bug 600067.
      99                 :          */
     100         2939354 :         if (!SHAPE_FETCH(spp))
     101         2939354 :             SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
     102                 :     }
     103          127390 :     return true;
     104                 : }
     105                 : 
     106                 : bool
     107          127390 : Shape::makeOwnBaseShape(JSContext *cx)
     108                 : {
     109          127390 :     JS_ASSERT(!base()->isOwned());
     110          127390 :     assertSameCompartment(cx, compartment());
     111                 : 
     112          254780 :     RootedVarShape self(cx, this);
     113                 : 
     114          127390 :     BaseShape *nbase = js_NewGCBaseShape(cx);
     115          127390 :     if (!nbase)
     116               0 :         return false;
     117                 : 
     118          127390 :     new (nbase) BaseShape(*self->base());
     119          127390 :     nbase->setOwned(self->base()->toUnowned());
     120                 : 
     121          127390 :     self->base_ = nbase;
     122                 : 
     123          127390 :     return true;
     124                 : }
     125                 : 
     126                 : void
     127         1965616 : Shape::handoffTableTo(Shape *shape)
     128                 : {
     129         1965616 :     JS_ASSERT(inDictionary() && shape->inDictionary());
     130                 : 
     131         1965616 :     if (this == shape)
     132            2133 :         return;
     133                 : 
     134         1963483 :     JS_ASSERT(base()->isOwned() && !shape->base()->isOwned());
     135                 : 
     136         1963483 :     BaseShape *nbase = base();
     137                 : 
     138         1963483 :     JS_ASSERT_IF(shape->hasSlot(), nbase->slotSpan() > shape->slot());
     139                 : 
     140         1963483 :     this->base_ = nbase->baseUnowned();
     141         1963483 :     nbase->adoptUnowned(shape->base()->toUnowned());
     142                 : 
     143         1963483 :     shape->base_ = nbase;
     144                 : }
     145                 : 
     146                 : bool
     147          127390 : Shape::hashify(JSContext *cx)
     148                 : {
     149          127390 :     JS_ASSERT(!hasTable());
     150                 : 
     151          254780 :     RootedVarShape self(cx, this);
     152                 : 
     153          127390 :     if (!ensureOwnBaseShape(cx))
     154               0 :         return false;
     155                 : 
     156          127390 :     JSRuntime *rt = cx->runtime;
     157          127390 :     PropertyTable *table = rt->new_<PropertyTable>(self->entryCount());
     158          127390 :     if (!table)
     159               0 :         return false;
     160                 : 
     161          127390 :     if (!table->init(rt, self)) {
     162               0 :         rt->free_(table);
     163               0 :         return false;
     164                 :     }
     165                 : 
     166          127390 :     self->base()->setTable(table);
     167          127390 :     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        55237604 : 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        55237604 :     JS_ASSERT(entries);
     186        55237604 :     JS_ASSERT(!JSID_IS_EMPTY(id));
     187                 : 
     188                 :     /* Compute the primary hash address. */
     189        55237604 :     hash0 = HashId(id);
     190        55237604 :     hash1 = HASH1(hash0, hashShift);
     191        55237604 :     spp = entries + hash1;
     192                 : 
     193                 :     /* Miss: return space for a new entry. */
     194        55237604 :     stored = *spp;
     195        55237604 :     if (SHAPE_IS_FREE(stored))
     196        13840400 :         return spp;
     197                 : 
     198                 :     /* Hit: return entry. */
     199        41397204 :     shape = SHAPE_CLEAR_COLLISION(stored);
     200        41397204 :     if (shape && shape->propid() == id)
     201        25582412 :         return spp;
     202                 : 
     203                 :     /* Collision: double hash. */
     204        15814792 :     sizeLog2 = HASH_BITS - hashShift;
     205        15814792 :     hash2 = HASH2(hash0, sizeLog2, hashShift);
     206        15814792 :     sizeMask = JS_BITMASK(sizeLog2);
     207                 : 
     208                 : #ifdef DEBUG
     209        15814792 :     uintptr_t collision_flag = SHAPE_COLLISION;
     210                 : #endif
     211                 : 
     212                 :     /* Save the first removed entry pointer so we can recycle it if adding. */
     213        15814792 :     if (SHAPE_IS_REMOVED(stored)) {
     214           16299 :         firstRemoved = spp;
     215                 :     } else {
     216        15798493 :         firstRemoved = NULL;
     217        15798493 :         if (adding && !SHAPE_HAD_COLLISION(stored))
     218          855947 :             SHAPE_FLAG_COLLISION(spp, shape);
     219                 : #ifdef DEBUG
     220        15798493 :         collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION;
     221                 : #endif
     222                 :     }
     223                 : 
     224        10128544 :     for (;;) {
     225        25943336 :         hash1 -= hash2;
     226        25943336 :         hash1 &= sizeMask;
     227        25943336 :         spp = entries + hash1;
     228                 : 
     229        25943336 :         stored = *spp;
     230        25943336 :         if (SHAPE_IS_FREE(stored))
     231         6074524 :             return (adding && firstRemoved) ? firstRemoved : spp;
     232                 : 
     233        19868812 :         shape = SHAPE_CLEAR_COLLISION(stored);
     234        19868812 :         if (shape && shape->propid() == id) {
     235         9740268 :             JS_ASSERT(collision_flag);
     236         9740268 :             return spp;
     237                 :         }
     238                 : 
     239        10128544 :         if (SHAPE_IS_REMOVED(stored)) {
     240           10242 :             if (!firstRemoved)
     241            8739 :                 firstRemoved = spp;
     242                 :         } else {
     243        10118302 :             if (adding && !SHAPE_HAD_COLLISION(stored))
     244          689321 :                 SHAPE_FLAG_COLLISION(spp, shape);
     245                 : #ifdef DEBUG
     246        10118302 :             collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION;
     247                 : #endif
     248                 :         }
     249                 :     }
     250                 : 
     251                 :     /* NOTREACHED */
     252                 :     return NULL;
     253                 : }
     254                 : 
     255                 : bool
     256            4797 : PropertyTable::change(int log2Delta, JSContext *cx)
     257                 : {
     258            4797 :     JS_ASSERT(entries);
     259                 : 
     260                 :     /*
     261                 :      * Grow, shrink, or compress by changing this->entries.
     262                 :      */
     263            4797 :     int oldlog2 = HASH_BITS - hashShift;
     264            4797 :     int newlog2 = oldlog2 + log2Delta;
     265            4797 :     uint32_t oldsize = JS_BIT(oldlog2);
     266            4797 :     uint32_t newsize = JS_BIT(newlog2);
     267            4797 :     Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize));
     268            4797 :     if (!newTable)
     269               0 :         return false;
     270                 : 
     271                 :     /* Now that we have newTable allocated, update members. */
     272            4797 :     hashShift = HASH_BITS - newlog2;
     273            4797 :     removedCount = 0;
     274            4797 :     Shape **oldTable = entries;
     275            4797 :     entries = newTable;
     276                 : 
     277                 :     /* Copy only live entries, leaving removed and free ones behind. */
     278         2560845 :     for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) {
     279         2556048 :         Shape *shape = SHAPE_FETCH(oldspp);
     280         2556048 :         if (shape) {
     281         1917036 :             Shape **spp = search(shape->propid(), true);
     282         1917036 :             JS_ASSERT(SHAPE_IS_FREE(*spp));
     283         1917036 :             *spp = shape;
     284                 :         }
     285         2556048 :         oldsize--;
     286                 :     }
     287                 : 
     288                 :     /* Finally, free the old entries storage. */
     289            4797 :     cx->free_(oldTable);
     290            4797 :     return true;
     291                 : }
     292                 : 
     293                 : bool
     294            4797 : PropertyTable::grow(JSContext *cx)
     295                 : {
     296            4797 :     JS_ASSERT(needsToGrow());
     297                 : 
     298            4797 :     uint32_t size = capacity();
     299            4797 :     int delta = removedCount < size >> 2;
     300                 : 
     301            4797 :     if (!change(delta, cx) && entryCount + removedCount == size - 1) {
     302               0 :         JS_ReportOutOfMemory(cx);
     303               0 :         return false;
     304                 :     }
     305            4797 :     return true;
     306                 : }
     307                 : 
     308                 : Shape *
     309          355342 : Shape::getChildBinding(JSContext *cx, const StackShape &child)
     310                 : {
     311          355342 :     JS_ASSERT(!inDictionary());
     312                 : 
     313          355342 :     Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, numFixedSlots(), child);
     314          355342 :     if (shape) {
     315          355342 :         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          355342 :         uint32_t slots = child.slotSpan() + 1;  /* Add one for private data. */
     325          355342 :         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          355342 :         uint32_t nfixed = gc::GetGCKindSlots(kind);
     334          355342 :         if (nfixed < slots) {
     335          225225 :             nfixed = CallObject::RESERVED_SLOTS + 1;
     336          225225 :             JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS + 1);
     337                 :         }
     338                 : 
     339          355342 :         shape->setNumFixedSlots(nfixed - 1);
     340                 :     }
     341          355342 :     return shape;
     342                 : }
     343                 : 
     344                 : /* static */ Shape *
     345         1843423 : Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, JSObject *proto, Shape *shape)
     346                 : {
     347         1843423 :     JS_ASSERT(!shape->inDictionary());
     348                 : 
     349         1843423 :     if (!shape->parent) {
     350                 :         /* Treat as resetting the initial property of the shape hierarchy. */
     351         1058785 :         AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
     352                 :         return EmptyShape::getInitialShape(cx, base.clasp, proto,
     353                 :                                            base.parent, kind,
     354         1058785 :                                            base.flags & BaseShape::OBJECT_FLAG_MASK);
     355                 :     }
     356                 : 
     357         1569276 :     RootShape root(cx, &shape);
     358                 : 
     359          784638 :     UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     360          784638 :     if (!nbase)
     361               0 :         return NULL;
     362                 : 
     363          784638 :     StackShape child(shape);
     364          784638 :     child.base = nbase;
     365                 : 
     366          784638 :     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        30109677 : 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        30109677 :     if (!child.hasSlot()) {
     383        11505189 :         child.setSlot(parent->maybeSlot());
     384                 :     } else {
     385        18604488 :         if (child.hasMissingSlot()) {
     386                 :             uint32_t slot;
     387        18130374 :             if (!allocSlot(cx, &slot))
     388               0 :                 return NULL;
     389        18130374 :             child.setSlot(slot);
     390                 :         } else {
     391                 :             /* Slots can only be allocated out of order on objects in dictionary mode. */
     392         1140413 :             JS_ASSERT(inDictionaryMode() ||
     393                 :                       parent->hasMissingSlot() ||
     394         1140413 :                       child.slot() == parent->maybeSlot() + 1);
     395                 :         }
     396                 :     }
     397                 : 
     398                 :     Shape *shape;
     399                 : 
     400        60219354 :     RootedVarObject self(cx, this);
     401                 : 
     402        30109677 :     if (inDictionaryMode()) {
     403         1663231 :         JS_ASSERT(parent == lastProperty());
     404         3326462 :         RootStackShape childRoot(cx, &child);
     405         1663231 :         shape = js_NewGCShape(cx);
     406         1663231 :         if (!shape)
     407               0 :             return NULL;
     408         1663231 :         if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) {
     409               0 :             if (!self->setSlotSpan(cx, child.slot() + 1))
     410               0 :                 return NULL;
     411                 :         }
     412         3326462 :         shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_);
     413                 :     } else {
     414        28446446 :         shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, self->numFixedSlots(), child);
     415        28446446 :         if (!shape)
     416               0 :             return NULL;
     417                 :         //JS_ASSERT(shape->parent == parent);
     418                 :         //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
     419        28446446 :         if (!self->setLastProperty(cx, shape))
     420               0 :             return NULL;
     421                 :     }
     422                 : 
     423        30109677 :     return shape;
     424                 : }
     425                 : 
     426                 : bool
     427          119895 : JSObject::toDictionaryMode(JSContext *cx)
     428                 : {
     429          119895 :     JS_ASSERT(!inDictionaryMode());
     430                 : 
     431                 :     /* We allocate the shapes from cx->compartment, so make sure it's right. */
     432          119895 :     JS_ASSERT(compartment() == cx->compartment);
     433                 : 
     434          119895 :     uint32_t span = slotSpan();
     435                 : 
     436          239790 :     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          239790 :     RootedVarShape root(cx);
     445          239790 :     RootedVarShape dictionaryShape(cx);
     446                 : 
     447          239790 :     RootedVarShape shape(cx);
     448          119895 :     shape = lastProperty();
     449                 : 
     450         3000569 :     while (shape) {
     451         2760779 :         JS_ASSERT(!shape->inDictionary());
     452                 : 
     453         2760779 :         Shape *dprop = js_NewGCShape(cx);
     454         2760779 :         if (!dprop) {
     455               0 :             js_ReportOutOfMemory(cx);
     456               0 :             return false;
     457                 :         }
     458                 : 
     459                 :         HeapPtrShape *listp = dictionaryShape
     460         2640884 :                               ? &dictionaryShape->parent
     461         5401663 :                               : (HeapPtrShape *) root.address();
     462                 : 
     463         2760779 :         StackShape child(shape);
     464         2760779 :         dprop->initDictionaryShape(child, self->numFixedSlots(), listp);
     465                 : 
     466         2760779 :         JS_ASSERT(!dprop->hasTable());
     467         2760779 :         dictionaryShape = dprop;
     468         2760779 :         shape = shape->previous();
     469                 :     }
     470                 : 
     471          119895 :     if (!root->hashify(cx)) {
     472               0 :         js_ReportOutOfMemory(cx);
     473               0 :         return false;
     474                 :     }
     475                 : 
     476          119895 :     JS_ASSERT((Shape **) root->listp == root.address());
     477          119895 :     root->listp = &self->shape_;
     478          119895 :     self->shape_ = root;
     479                 : 
     480          119895 :     JS_ASSERT(self->inDictionaryMode());
     481          119895 :     root->base()->setSlotSpan(span);
     482                 : 
     483          119895 :     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        31985760 : NormalizeGetterAndSetter(JSContext *cx, JSObject *obj,
     492                 :                          jsid id, unsigned attrs, unsigned flags,
     493                 :                          PropertyOp &getter,
     494                 :                          StrictPropertyOp &setter)
     495                 : {
     496        31985760 :     if (setter == JS_StrictPropertyStub) {
     497        31074965 :         JS_ASSERT(!(attrs & JSPROP_SETTER));
     498        31074965 :         setter = NULL;
     499                 :     }
     500        31985760 :     if (getter == JS_PropertyStub) {
     501        19598878 :         JS_ASSERT(!(attrs & JSPROP_GETTER));
     502        19598878 :         getter = NULL;
     503                 :     }
     504                 : 
     505        31985760 :     return true;
     506                 : }
     507                 : 
     508                 : Shape *
     509          377836 : JSObject::addProperty(JSContext *cx, jsid id,
     510                 :                       PropertyOp getter, StrictPropertyOp setter,
     511                 :                       uint32_t slot, unsigned attrs,
     512                 :                       unsigned flags, int shortid, bool allowDictionary)
     513                 : {
     514          377836 :     JS_ASSERT(!JSID_IS_VOID(id));
     515                 : 
     516          377836 :     if (!isExtensible()) {
     517               0 :         reportNotExtensible(cx);
     518               0 :         return NULL;
     519                 :     }
     520                 : 
     521          377836 :     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
     522                 : 
     523          377836 :     Shape **spp = NULL;
     524          377836 :     if (inDictionaryMode())
     525           52689 :         spp = lastProperty()->table().search(id, true);
     526                 : 
     527                 :     return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid,
     528          377836 :                                spp, allowDictionary);
     529                 : }
     530                 : 
     531                 : Shape *
     532        30109396 : JSObject::addPropertyInternal(JSContext *cx, jsid id,
     533                 :                               PropertyOp getter, StrictPropertyOp setter,
     534                 :                               uint32_t slot, unsigned attrs,
     535                 :                               unsigned flags, int shortid, Shape **spp,
     536                 :                               bool allowDictionary)
     537                 : {
     538        30109396 :     JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
     539                 : 
     540        60218792 :     RootId idRoot(cx, &id);
     541        60218792 :     RootedVarObject self(cx, this);
     542                 : 
     543        30109396 :     PropertyTable *table = NULL;
     544        30109396 :     if (!inDictionaryMode()) {
     545                 :         bool stableSlot =
     546                 :             (slot == SHAPE_INVALID_SLOT) ||
     547          421397 :             lastProperty()->hasMissingSlot() ||
     548        28888654 :             (slot == lastProperty()->maybeSlot() + 1);
     549        28467257 :         JS_ASSERT_IF(!allowDictionary, stableSlot);
     550        84719216 :         if (allowDictionary &&
     551        56251959 :             (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
     552           21092 :             if (!toDictionaryMode(cx))
     553               0 :                 return NULL;
     554           21092 :             table = &lastProperty()->table();
     555           21092 :             spp = table->search(id, true);
     556                 :         }
     557                 :     } else {
     558         1642139 :         table = &lastProperty()->table();
     559         1642139 :         if (table->needsToGrow()) {
     560            4797 :             if (!table->grow(cx))
     561               0 :                 return NULL;
     562            4797 :             spp = table->search(id, true);
     563            4797 :             JS_ASSERT(!SHAPE_FETCH(spp));
     564                 :         }
     565                 :     }
     566                 : 
     567        30109396 :     JS_ASSERT(!!table == !!spp);
     568                 : 
     569                 :     /* Find or create a property tree node labeled by our arguments. */
     570                 :     Shape *shape;
     571                 :     {
     572        30109396 :         shape = self->lastProperty();
     573                 : 
     574                 :         uint32_t index;
     575        30109396 :         bool indexed = js_IdIsIndex(id, &index);
     576                 :         UnownedBaseShape *nbase;
     577        30109396 :         if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
     578        16406365 :             nbase = shape->base()->unowned();
     579                 :         } else {
     580        13703031 :             StackBaseShape base(shape->base());
     581        13703031 :             base.updateGetterSetter(attrs, getter, setter);
     582        13703031 :             if (indexed)
     583         1730848 :                 base.flags |= BaseShape::INDEXED;
     584        13703031 :             nbase = BaseShape::getUnowned(cx, base);
     585        13703031 :             if (!nbase)
     586               0 :                 return NULL;
     587                 :         }
     588                 : 
     589        30109396 :         StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
     590        30109396 :         shape = self->getChildProperty(cx, self->lastProperty(), child);
     591                 :     }
     592                 : 
     593        30109396 :     if (shape) {
     594        30109396 :         JS_ASSERT(shape == self->lastProperty());
     595                 : 
     596        30109396 :         if (table) {
     597                 :             /* Store the tree node pointer in the table entry for id. */
     598         1663231 :             SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
     599         1663231 :             ++table->entryCount;
     600                 : 
     601                 :             /* Pass the table along to the new last property, namely shape. */
     602         1663231 :             JS_ASSERT(&shape->parent->table() == table);
     603         1663231 :             shape->parent->handoffTableTo(shape);
     604                 :         }
     605                 : 
     606        30109396 :         self->checkShapeConsistency();
     607        30109396 :         return shape;
     608                 :     }
     609                 : 
     610               0 :     self->checkShapeConsistency();
     611               0 :     return NULL;
     612                 : }
     613                 : 
     614                 : /*
     615                 :  * Check and adjust the new attributes for the shape to make sure that our
     616                 :  * slot access optimizations are sound. It is responsibility of the callers to
     617                 :  * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
     618                 :  */
     619                 : inline bool
     620         2239267 : CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, unsigned *attrsp)
     621                 : {
     622         2239267 :     if (shape->configurable())
     623         2079985 :         return true;
     624                 : 
     625                 :     /* A permanent property must stay permanent. */
     626          159282 :     *attrsp |= JSPROP_PERMANENT;
     627                 : 
     628                 :     /* Reject attempts to remove a slot from the permanent data property. */
     629          159282 :     if (shape->isDataDescriptor() && shape->hasSlot() &&
     630                 :         (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
     631               0 :         obj->reportNotConfigurable(cx, shape->propid());
     632               0 :         return false;
     633                 :     }
     634                 : 
     635          159282 :     return true;
     636                 : }
     637                 : 
     638                 : Shape *
     639        31607924 : JSObject::putProperty(JSContext *cx, jsid id,
     640                 :                       PropertyOp getter, StrictPropertyOp setter,
     641                 :                       uint32_t slot, unsigned attrs,
     642                 :                       unsigned flags, int shortid)
     643                 : {
     644        31607924 :     JS_ASSERT(!JSID_IS_VOID(id));
     645                 : 
     646        63215848 :     RootId idRoot(cx, &id);
     647                 : 
     648        31607924 :     NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
     649                 : 
     650        63215848 :     RootedVarObject self(cx, this);
     651                 : 
     652                 :     /* Search for id in order to claim its entry if table has been allocated. */
     653                 :     Shape **spp;
     654        31607924 :     Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true);
     655        31607924 :     if (!shape) {
     656                 :         /*
     657                 :          * You can't add properties to a non-extensible object, but you can change
     658                 :          * attributes of properties in such objects.
     659                 :          */
     660        29400779 :         if (!self->isExtensible()) {
     661               0 :             self->reportNotExtensible(cx);
     662               0 :             return NULL;
     663                 :         }
     664                 : 
     665        29400779 :         return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
     666                 :     }
     667                 : 
     668                 :     /* Property exists: search must have returned a valid *spp. */
     669         2207145 :     JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp));
     670                 : 
     671         4414290 :     RootShape shapeRoot(cx, &shape);
     672                 : 
     673         2207145 :     if (!CheckCanChangeAttrs(cx, self, shape, &attrs))
     674               0 :         return NULL;
     675                 :     
     676                 :     /*
     677                 :      * If the caller wants to allocate a slot, but doesn't care which slot,
     678                 :      * copy the existing shape's slot into slot so we can match shape, if all
     679                 :      * other members match.
     680                 :      */
     681         2207145 :     bool hadSlot = shape->hasSlot();
     682         2207145 :     uint32_t oldSlot = shape->maybeSlot();
     683         2207145 :     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
     684         2174887 :         slot = oldSlot;
     685                 : 
     686         4414290 :     RootedVar<UnownedBaseShape*> nbase(cx);
     687                 :     {
     688                 :         uint32_t index;
     689         2207145 :         bool indexed = js_IdIsIndex(id, &index);
     690         2207145 :         StackBaseShape base(self->lastProperty()->base());
     691         2207145 :         base.updateGetterSetter(attrs, getter, setter);
     692         2207145 :         if (indexed)
     693             126 :             base.flags |= BaseShape::INDEXED;
     694         2207145 :         nbase = BaseShape::getUnowned(cx, base);
     695         2207145 :         if (!nbase)
     696               0 :             return NULL;
     697                 :     }
     698                 : 
     699                 :     /*
     700                 :      * Now that we've possibly preserved slot, check whether all members match.
     701                 :      * If so, this is a redundant "put" and we can return without more work.
     702                 :      */
     703         2207145 :     if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
     704         2128074 :         return shape;
     705                 : 
     706                 :     /*
     707                 :      * Overwriting a non-last property requires switching to dictionary mode.
     708                 :      * The shape tree is shared immutable, and we can't removeProperty and then
     709                 :      * addPropertyInternal because a failure under add would lose data.
     710                 :      */
     711           79071 :     if (shape != self->lastProperty() && !self->inDictionaryMode()) {
     712           46701 :         if (!self->toDictionaryMode(cx))
     713               0 :             return NULL;
     714           46701 :         spp = self->lastProperty()->table().search(shape->propid(), false);
     715           46701 :         shape = SHAPE_FETCH(spp);
     716                 :     }
     717                 : 
     718           79071 :     JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
     719                 : 
     720           79071 :     if (self->inDictionaryMode()) {
     721                 :         /*
     722                 :          * Updating some property in a dictionary-mode object. Create a new
     723                 :          * shape for the existing property, and also generate a new shape for
     724                 :          * the last property of the dictionary (unless the modified property
     725                 :          * is also the last property).
     726                 :          */
     727           78790 :         bool updateLast = (shape == self->lastProperty());
     728           78790 :         shape = self->replaceWithNewEquivalentShape(cx, shape);
     729           78790 :         if (!shape)
     730               0 :             return NULL;
     731           78790 :         if (!updateLast && !self->generateOwnShape(cx))
     732               0 :             return NULL;
     733                 : 
     734                 :         /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
     735           78790 :         if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
     736               9 :             if (!self->allocSlot(cx, &slot))
     737               0 :                 return NULL;
     738                 :         }
     739                 : 
     740           78790 :         if (updateLast)
     741           13014 :             shape->base()->adoptUnowned(nbase);
     742                 :         else
     743           65776 :             shape->base_ = nbase;
     744                 : 
     745           78790 :         shape->setSlot(slot);
     746           78790 :         shape->attrs = uint8_t(attrs);
     747           78790 :         shape->flags = flags | Shape::IN_DICTIONARY;
     748           78790 :         shape->shortid_ = int16_t(shortid);
     749                 :     } else {
     750                 :         /*
     751                 :          * Updating the last property in a non-dictionary-mode object. Find an
     752                 :          * alternate shared child of the last property's previous shape.
     753                 :          */
     754             281 :         StackBaseShape base(self->lastProperty()->base());
     755             281 :         base.updateGetterSetter(attrs, getter, setter);
     756             281 :         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     757             281 :         if (!nbase)
     758               0 :             return NULL;
     759                 : 
     760             281 :         JS_ASSERT(shape == self->lastProperty());
     761                 : 
     762                 :         /* Find or create a property tree node labeled by our arguments. */
     763             281 :         StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
     764             281 :         Shape *newShape = self->getChildProperty(cx, shape->parent, child);
     765                 : 
     766             281 :         if (!newShape) {
     767               0 :             self->checkShapeConsistency();
     768               0 :             return NULL;
     769                 :         }
     770                 : 
     771             281 :         shape = newShape;
     772                 :     }
     773                 : 
     774                 :     /*
     775                 :      * Can't fail now, so free the previous incarnation's slot if the new shape
     776                 :      * has no slot. But we do not need to free oldSlot (and must not, as trying
     777                 :      * to will botch an assertion in JSObject::freeSlot) if the new last
     778                 :      * property (shape here) has a slotSpan that does not cover it.
     779                 :      */
     780           79071 :     if (hadSlot && !shape->hasSlot()) {
     781             108 :         if (oldSlot < self->slotSpan())
     782              54 :             self->freeSlot(cx, oldSlot);
     783             108 :         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     784                 :     }
     785                 : 
     786           79071 :     self->checkShapeConsistency();
     787                 : 
     788           79071 :     return shape;
     789                 : }
     790                 : 
     791                 : Shape *
     792           32122 : JSObject::changeProperty(JSContext *cx, Shape *shape, unsigned attrs, unsigned mask,
     793                 :                          PropertyOp getter, StrictPropertyOp setter)
     794                 : {
     795           32122 :     JS_ASSERT(nativeContains(cx, *shape));
     796                 : 
     797           32122 :     attrs |= shape->attrs & mask;
     798                 : 
     799                 :     /* Allow only shared (slotless) => unshared (slotful) transition. */
     800           32122 :     JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
     801           32122 :               !(attrs & JSPROP_SHARED));
     802                 : 
     803           32122 :     types::MarkTypePropertyConfigured(cx, this, shape->propid());
     804           32122 :     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
     805             208 :         types::AddTypePropertyId(cx, this, shape->propid(), types::Type::UnknownType());
     806                 : 
     807           32122 :     if (getter == JS_PropertyStub)
     808               0 :         getter = NULL;
     809           32122 :     if (setter == JS_StrictPropertyStub)
     810               0 :         setter = NULL;
     811                 : 
     812           32122 :     if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
     813               0 :         return NULL;
     814                 :     
     815           32122 :     if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
     816               0 :         return shape;
     817                 : 
     818                 :     /*
     819                 :      * Let JSObject::putProperty handle this |overwriting| case, including
     820                 :      * the conservation of shape->slot (if it's valid). We must not call
     821                 :      * removeProperty because it will free an allocated shape->slot, and
     822                 :      * putProperty won't re-allocate it.
     823                 :      */
     824           32122 :     Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
     825           64244 :                                   attrs, shape->flags, shape->maybeShortid());
     826                 : 
     827           32122 :     checkShapeConsistency();
     828           32122 :     return newShape;
     829                 : }
     830                 : 
     831                 : bool
     832            3591 : JSObject::removeProperty(JSContext *cx, jsid id)
     833                 : {
     834            7182 :     RootedVarObject self(cx, this);
     835                 : 
     836            7182 :     RootId idRoot(cx, &id);
     837            7182 :     RootedVarShape shape(cx);
     838                 : 
     839                 :     Shape **spp;
     840            3591 :     shape = Shape::search(cx, lastProperty(), id, &spp);
     841            3591 :     if (!shape)
     842               0 :         return true;
     843                 : 
     844                 :     /*
     845                 :      * If shape is not the last property added, or the last property cannot
     846                 :      * be removed, switch to dictionary mode.
     847                 :      */
     848            3591 :     if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
     849             517 :         if (!self->toDictionaryMode(cx))
     850               0 :             return false;
     851             517 :         spp = self->lastProperty()->table().search(shape->propid(), false);
     852             517 :         shape = SHAPE_FETCH(spp);
     853                 :     }
     854                 : 
     855                 :     /*
     856                 :      * If in dictionary mode, get a new shape for the last property after the
     857                 :      * removal. We need a fresh shape for all dictionary deletions, even of
     858                 :      * the last property. Otherwise, a shape could replay and caches might
     859                 :      * return deleted DictionaryShapes! See bug 595365. Do this before changing
     860                 :      * the object or table, so the remaining removal is infallible.
     861                 :      */
     862            3591 :     Shape *spare = NULL;
     863            3591 :     if (self->inDictionaryMode()) {
     864            2412 :         spare = js_NewGCShape(cx);
     865            2412 :         if (!spare)
     866               0 :             return false;
     867            2412 :         new (spare) Shape(shape->base()->unowned(), 0);
     868            2412 :         if (shape == lastProperty()) {
     869                 :             /*
     870                 :              * Get an up to date unowned base shape for the new last property
     871                 :              * when removing the dictionary's last property. Information in
     872                 :              * base shapes for non-last properties may be out of sync with the
     873                 :              * object's state.
     874                 :              */
     875             279 :             Shape *previous = lastProperty()->parent;
     876             279 :             StackBaseShape base(lastProperty()->base());
     877             279 :             base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
     878             279 :             BaseShape *nbase = BaseShape::getUnowned(cx, base);
     879             279 :             if (!nbase)
     880               0 :                 return false;
     881             279 :             previous->base_ = nbase;
     882                 :         }
     883                 :     }
     884                 : 
     885                 :     /* If shape has a slot, free its slot number. */
     886            3591 :     if (shape->hasSlot()) {
     887            2709 :         self->freeSlot(cx, shape->slot());
     888            2709 :         JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     889                 :     }
     890                 : 
     891                 :     /*
     892                 :      * A dictionary-mode object owns mutable, unique shapes on a non-circular
     893                 :      * doubly linked list, hashed by lastProperty()->table. So we can edit the
     894                 :      * list and hash in place.
     895                 :      */
     896            3591 :     if (self->inDictionaryMode()) {
     897            2412 :         PropertyTable &table = self->lastProperty()->table();
     898                 : 
     899            2412 :         if (SHAPE_HAD_COLLISION(*spp)) {
     900             369 :             *spp = SHAPE_REMOVED;
     901             369 :             ++table.removedCount;
     902             369 :             --table.entryCount;
     903                 :         } else {
     904            2043 :             *spp = NULL;
     905            2043 :             --table.entryCount;
     906                 : 
     907                 : #ifdef DEBUG
     908                 :             /*
     909                 :              * Check the consistency of the table but limit the number of
     910                 :              * checks not to alter significantly the complexity of the
     911                 :              * delete in debug builds, see bug 534493.
     912                 :              */
     913            2043 :             const Shape *aprop = self->lastProperty();
     914           30114 :             for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
     915           28071 :                 JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, *aprop));
     916                 : #endif
     917                 :         }
     918                 : 
     919                 :         /* Remove shape from its non-circular doubly linked list. */
     920            2412 :         Shape *oldLastProp = self->lastProperty();
     921            2412 :         shape->removeFromDictionary(self);
     922                 : 
     923                 :         /* Hand off table from the old to new last property. */
     924            2412 :         oldLastProp->handoffTableTo(self->lastProperty());
     925                 : 
     926                 :         /* Generate a new shape for the object, infallibly. */
     927            2412 :         JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
     928                 : 
     929                 :         /* Consider shrinking table if its load factor is <= .25. */
     930            2412 :         uint32_t size = table.capacity();
     931            2412 :         if (size > PropertyTable::MIN_SIZE && table.entryCount <= size >> 2)
     932               0 :             (void) table.change(-1, cx);
     933                 :     } else {
     934                 :         /*
     935                 :          * Non-dictionary-mode property tables are shared immutables, so all we
     936                 :          * need do is retract the last property and we'll either get or else
     937                 :          * lazily make via a later hashify the exact table for the new property
     938                 :          * lineage.
     939                 :          */
     940            1179 :         JS_ASSERT(shape == self->lastProperty());
     941            1179 :         self->removeLastProperty(cx);
     942                 :     }
     943                 : 
     944            3591 :     self->checkShapeConsistency();
     945            3591 :     return true;
     946                 : }
     947                 : 
     948                 : void
     949              10 : JSObject::clear(JSContext *cx)
     950                 : {
     951              10 :     Shape *shape = lastProperty();
     952              10 :     JS_ASSERT(inDictionaryMode() == shape->inDictionary());
     953                 : 
     954              20 :     while (shape->parent) {
     955               0 :         shape = shape->parent;
     956               0 :         JS_ASSERT(inDictionaryMode() == shape->inDictionary());
     957                 :     }
     958              10 :     JS_ASSERT(shape->isEmptyShape());
     959                 : 
     960              10 :     if (inDictionaryMode())
     961               0 :         shape->listp = &shape_;
     962                 : 
     963              10 :     JS_ALWAYS_TRUE(setLastProperty(cx, shape));
     964                 : 
     965              10 :     JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
     966              10 :     checkShapeConsistency();
     967              10 : }
     968                 : 
     969                 : void
     970              20 : JSObject::rollbackProperties(JSContext *cx, uint32_t slotSpan)
     971                 : {
     972                 :     /*
     973                 :      * Remove properties from this object until it has a matching slot span.
     974                 :      * The object cannot have escaped in a way which would prevent safe
     975                 :      * removal of the last properties.
     976                 :      */
     977              20 :     JS_ASSERT(!inDictionaryMode() && slotSpan <= this->slotSpan());
     978              85 :     while (this->slotSpan() != slotSpan) {
     979              45 :         JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
     980              45 :         removeLastProperty(cx);
     981                 :     }
     982              20 : }
     983                 : 
     984                 : Shape *
     985          365749 : JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape)
     986                 : {
     987          497301 :     JS_ASSERT_IF(oldShape != lastProperty(),
     988                 :                  inDictionaryMode() &&
     989          497301 :                  nativeLookup(cx, oldShape->propidRef()) == oldShape);
     990                 : 
     991          365749 :     JSObject *self = this;
     992                 : 
     993          365749 :     if (!inDictionaryMode()) {
     994           98700 :         RootObject selfRoot(cx, &self);
     995           98700 :         RootShape newRoot(cx, &newShape);
     996           49350 :         if (!toDictionaryMode(cx))
     997               0 :             return NULL;
     998           98700 :         oldShape = lastProperty();
     999                 :     }
    1000                 : 
    1001          365749 :     if (!newShape) {
    1002          726674 :         RootObject selfRoot(cx, &self);
    1003          726674 :         RootShape oldRoot(cx, &oldShape);
    1004          363337 :         newShape = js_NewGCShape(cx);
    1005          363337 :         if (!newShape)
    1006               0 :             return NULL;
    1007          363337 :         new (newShape) Shape(oldShape->base()->unowned(), 0);
    1008                 :     }
    1009                 : 
    1010          365749 :     PropertyTable &table = self->lastProperty()->table();
    1011          365749 :     Shape **spp = oldShape->isEmptyShape()
    1012                 :                   ? NULL
    1013          365749 :                   : table.search(oldShape->propidRef(), false);
    1014                 : 
    1015                 :     /*
    1016                 :      * Splice the new shape into the same position as the old shape, preserving
    1017                 :      * enumeration order (see bug 601399).
    1018                 :      */
    1019          365749 :     StackShape nshape(oldShape);
    1020          365749 :     newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp);
    1021                 : 
    1022          365749 :     JS_ASSERT(newShape->parent == oldShape);
    1023          365749 :     oldShape->removeFromDictionary(self);
    1024                 : 
    1025          365749 :     if (newShape == lastProperty())
    1026          299973 :         oldShape->handoffTableTo(newShape);
    1027                 : 
    1028          365749 :     if (spp)
    1029          352603 :         SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
    1030          365749 :     return newShape;
    1031                 : }
    1032                 : 
    1033                 : bool
    1034          141972 : JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
    1035                 : {
    1036          141972 :     return generateOwnShape(cx);
    1037                 : }
    1038                 : 
    1039                 : bool
    1040            2967 : JSObject::clearParent(JSContext *cx)
    1041                 : {
    1042            2967 :     return setParent(cx, NULL);
    1043                 : }
    1044                 : 
    1045                 : bool
    1046           66672 : JSObject::setParent(JSContext *cx, JSObject *parent)
    1047                 : {
    1048           66672 :     if (parent && !parent->setDelegate(cx))
    1049               0 :         return false;
    1050                 : 
    1051           66672 :     if (inDictionaryMode()) {
    1052               0 :         StackBaseShape base(lastProperty());
    1053               0 :         base.parent = parent;
    1054               0 :         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
    1055               0 :         if (!nbase)
    1056               0 :             return false;
    1057                 : 
    1058               0 :         lastProperty()->base()->adoptUnowned(nbase);
    1059               0 :         return true;
    1060                 :     }
    1061                 : 
    1062           66672 :     Shape *newShape = Shape::setObjectParent(cx, parent, getProto(), shape_);
    1063           66672 :     if (!newShape)
    1064               0 :         return false;
    1065                 : 
    1066           66672 :     shape_ = newShape;
    1067           66672 :     return true;
    1068                 : }
    1069                 : 
    1070                 : /* static */ Shape *
    1071          212710 : Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, Shape *last)
    1072                 : {
    1073          212710 :     if (last->getObjectParent() == parent)
    1074           60190 :         return last;
    1075                 : 
    1076          152520 :     StackBaseShape base(last);
    1077          152520 :     base.parent = parent;
    1078                 : 
    1079          152520 :     return replaceLastProperty(cx, base, proto, last);
    1080                 : }
    1081                 : 
    1082                 : bool
    1083         1243024 : JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
    1084                 : {
    1085         1243024 :     JS_ASSERT(isExtensible());
    1086                 : 
    1087         2486048 :     RootedVarObject self(cx, this);
    1088                 : 
    1089         1243024 :     if (props) {
    1090         1243024 :         if (js::FixOp fix = getOps()->fix) {
    1091                 :             bool success;
    1092              82 :             if (!fix(cx, this, &success, props))
    1093               0 :                 return false;
    1094              82 :             if (!success) {
    1095               0 :                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
    1096               0 :                 return false;
    1097                 :             }
    1098                 :         } else {
    1099         1242942 :             if (!js::GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
    1100               0 :                 return false;
    1101                 :         }
    1102                 :     }
    1103                 : 
    1104         1243024 :     return self->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE);
    1105                 : }
    1106                 : 
    1107                 : bool
    1108         4228376 : JSObject::setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag_, GenerateShape generateShape)
    1109                 : {
    1110         4228376 :     BaseShape::Flag flag = (BaseShape::Flag) flag_;
    1111                 : 
    1112         4228376 :     if (lastProperty()->getObjectFlags() & flag)
    1113         2499873 :         return true;
    1114                 : 
    1115         3457006 :     RootedVarObject self(cx, this);
    1116                 : 
    1117         1728503 :     if (inDictionaryMode()) {
    1118           39384 :         if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx))
    1119               0 :             return false;
    1120           39384 :         StackBaseShape base(self->lastProperty());
    1121           39384 :         base.flags |= flag;
    1122           39384 :         UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
    1123           39384 :         if (!nbase)
    1124               0 :             return false;
    1125                 : 
    1126           39384 :         self->lastProperty()->base()->adoptUnowned(nbase);
    1127           39384 :         return true;
    1128                 :     }
    1129                 : 
    1130         1689119 :     Shape *newShape = Shape::setObjectFlag(cx, flag, getProto(), lastProperty());
    1131         1689119 :     if (!newShape)
    1132               0 :         return false;
    1133                 : 
    1134         1689119 :     self->shape_ = newShape;
    1135         1689119 :     return true;
    1136                 : }
    1137                 : 
    1138                 : /* static */ Shape *
    1139         1689119 : Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last)
    1140                 : {
    1141         1689119 :     if (last->getObjectFlags() & flag)
    1142               0 :         return last;
    1143                 : 
    1144         1689119 :     StackBaseShape base(last);
    1145         1689119 :     base.flags |= flag;
    1146                 : 
    1147         1689119 :     return replaceLastProperty(cx, base, proto, last);
    1148                 : }
    1149                 : 
    1150                 : /* static */ inline HashNumber
    1151        18950141 : StackBaseShape::hash(const StackBaseShape *base)
    1152                 : {
    1153        18950141 :     HashNumber hash = base->flags;
    1154        18950141 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
    1155        18950141 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3);
    1156        18950141 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawGetter);
    1157        18950141 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawSetter);
    1158        18950141 :     return hash;
    1159                 : }
    1160                 : 
    1161                 : /* static */ inline bool
    1162        14988376 : StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
    1163                 : {
    1164                 :     return key->flags == lookup->flags
    1165                 :         && key->clasp == lookup->clasp
    1166        14987833 :         && key->parent == lookup->parent
    1167                 :         && key->rawGetter == lookup->rawGetter
    1168        29976209 :         && key->rawSetter == lookup->rawSetter;
    1169                 : }
    1170                 : 
    1171                 : /* Root for stack allocated base shapes. */
    1172                 : class RootStackBaseShape
    1173         3962586 : {
    1174                 :     Root<const JSObject*> parentRoot;
    1175                 :     Maybe<RootObject> getterRoot;
    1176                 :     Maybe<RootObject> setterRoot;
    1177                 : 
    1178                 :   public:
    1179         3962586 :     RootStackBaseShape(JSContext *cx, const StackBaseShape *base)
    1180         3962586 :         : parentRoot(cx, &base->parent)
    1181                 :     {
    1182         3962586 :         if (base->flags & BaseShape::HAS_GETTER_OBJECT)
    1183         1044856 :             getterRoot.construct(cx, (JSObject **) &base->rawGetter);
    1184         3962586 :         if (base->flags & BaseShape::HAS_SETTER_OBJECT)
    1185          187437 :             setterRoot.construct(cx, (JSObject **) &base->rawSetter);
    1186         3962586 :     }
    1187                 : };
    1188                 : 
    1189                 : /* static */ UnownedBaseShape *
    1190        18950141 : BaseShape::getUnowned(JSContext *cx, const StackBaseShape &base)
    1191                 : {
    1192        18950141 :     BaseShapeSet &table = cx->compartment->baseShapes;
    1193                 : 
    1194        18950141 :     if (!table.initialized() && !table.init())
    1195               0 :         return NULL;
    1196                 : 
    1197        37900282 :     BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
    1198                 : 
    1199        18950141 :     if (p)
    1200        14987555 :         return *p;
    1201                 : 
    1202         7925172 :     RootStackBaseShape root(cx, &base);
    1203                 : 
    1204         3962586 :     BaseShape *nbase_ = js_NewGCBaseShape(cx);
    1205         3962586 :     if (!nbase_)
    1206               0 :         return NULL;
    1207         3962586 :     new (nbase_) BaseShape(base);
    1208                 : 
    1209         3962586 :     UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
    1210                 : 
    1211         3962586 :     if (!table.relookupOrAdd(p, &base, nbase))
    1212               0 :         return NULL;
    1213                 : 
    1214         3962586 :     return nbase;
    1215                 : }
    1216                 : 
    1217                 : void
    1218           83697 : JSCompartment::sweepBaseShapeTable()
    1219                 : {
    1220           83697 :     if (baseShapes.initialized()) {
    1221         9766789 :         for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
    1222         9721375 :             UnownedBaseShape *base = e.front();
    1223         9721375 :             if (!base->isMarked())
    1224         3962586 :                 e.removeFront();
    1225                 :         }
    1226                 :     }
    1227           83697 : }
    1228                 : 
    1229                 : void
    1230         4089976 : BaseShape::finalize(FreeOp *fop)
    1231                 : {
    1232         4089976 :     if (table_) {
    1233          127390 :         fop->delete_(table_);
    1234          127390 :         table_ = NULL;
    1235                 :     }
    1236         4089976 : }
    1237                 : 
    1238                 : /* static */ Shape *
    1239            1784 : Shape::setExtensibleParents(JSContext *cx, Shape *shape)
    1240                 : {
    1241            1784 :     JS_ASSERT(!shape->inDictionary());
    1242                 : 
    1243            1784 :     StackBaseShape base(shape);
    1244            1784 :     base.flags |= BaseShape::EXTENSIBLE_PARENTS;
    1245                 : 
    1246                 :     /* This is only used for Block and Call objects, which have a NULL proto. */
    1247            1784 :     return replaceLastProperty(cx, base, NULL, shape);
    1248                 : }
    1249                 : 
    1250                 : bool
    1251             559 : Bindings::setExtensibleParents(JSContext *cx)
    1252                 : {
    1253             559 :     if (!ensureShape(cx))
    1254               0 :         return false;
    1255             559 :     Shape *newShape = Shape::setExtensibleParents(cx, lastBinding);
    1256             559 :     if (!newShape)
    1257               0 :         return false;
    1258             559 :     lastBinding = newShape;
    1259             559 :     return true;
    1260                 : }
    1261                 : 
    1262                 : bool
    1263          146038 : Bindings::setParent(JSContext *cx, JSObject *obj)
    1264                 : {
    1265                 :     /*
    1266                 :      * This may be invoked on GC heap allocated bindings, in which case this
    1267                 :      * is pointing to an internal value of a JSScript that can't itself be
    1268                 :      * relocated. The script itself will be rooted, and will not be moved, so
    1269                 :      * mark the stack value as non-relocatable for the stack root analysis.
    1270                 :      */
    1271          146038 :     Bindings *self = this;
    1272          292076 :     CheckRoot root(cx, &self);
    1273                 : 
    1274          292076 :     RootObject rootObj(cx, &obj);
    1275                 : 
    1276          146038 :     if (!ensureShape(cx))
    1277               0 :         return false;
    1278                 : 
    1279                 :     /* This is only used for Block objects, which have a NULL proto. */
    1280          146038 :     Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding);
    1281          146038 :     if (!newShape)
    1282               0 :         return false;
    1283          146038 :     self->lastBinding = newShape;
    1284          146038 :     return true;
    1285                 : }
    1286                 : 
    1287                 : /* static */ inline HashNumber
    1288         8611900 : InitialShapeEntry::hash(const Lookup &lookup)
    1289                 : {
    1290         8611900 :     HashNumber hash = uintptr_t(lookup.clasp) >> 3;
    1291         8611900 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.proto) >> 3);
    1292         8611900 :     hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.parent) >> 3);
    1293         8611900 :     return hash + lookup.nfixed;
    1294                 : }
    1295                 : 
    1296                 : /* static */ inline bool
    1297         9537229 : InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
    1298                 : {
    1299         9537229 :     return lookup.clasp == key.shape->getObjectClass()
    1300                 :         && lookup.proto == key.proto
    1301         9537132 :         && lookup.parent == key.shape->getObjectParent()
    1302         9537132 :         && lookup.nfixed == key.shape->numFixedSlots()
    1303        38148722 :         && lookup.baseFlags == key.shape->getObjectFlags();
    1304                 : }
    1305                 : 
    1306                 : /* static */ Shape *
    1307         8605500 : EmptyShape::getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
    1308                 :                             AllocKind kind, uint32_t objectFlags)
    1309                 : {
    1310         8605500 :     InitialShapeSet &table = cx->compartment->initialShapes;
    1311                 : 
    1312         8605500 :     if (!table.initialized() && !table.init())
    1313               0 :         return NULL;
    1314                 : 
    1315         8605500 :     size_t nfixed = GetGCKindSlots(kind, clasp);
    1316         8605500 :     InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags);
    1317                 : 
    1318        17211000 :     InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
    1319                 : 
    1320         8605500 :     if (p)
    1321         6745450 :         return p->shape;
    1322                 : 
    1323         3720100 :     RootedVar<UnownedBaseShape*> nbase(cx);
    1324                 : 
    1325         1860050 :     StackBaseShape base(clasp, parent, objectFlags);
    1326         1860050 :     nbase = BaseShape::getUnowned(cx, base);
    1327         1860050 :     if (!nbase)
    1328               0 :         return NULL;
    1329                 : 
    1330         1860050 :     Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx);
    1331         1860050 :     if (!shape)
    1332               0 :         return NULL;
    1333         1860050 :     new (shape) EmptyShape(nbase, nfixed);
    1334                 : 
    1335         1860050 :     InitialShapeEntry entry;
    1336         1860050 :     entry.shape = shape;
    1337         1860050 :     entry.proto = proto;
    1338                 : 
    1339         1860050 :     if (!table.relookupOrAdd(p, lookup, entry))
    1340               0 :         return NULL;
    1341                 : 
    1342         1860050 :     return shape;
    1343                 : }
    1344                 : 
    1345                 : void
    1346            6400 : NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto)
    1347                 : {
    1348            6400 :     Class *clasp = shape->getObjectClass();
    1349                 : 
    1350            6400 :     gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
    1351            6400 :     if (CanBeFinalizedInBackground(kind, clasp))
    1352            6400 :         kind = GetBackgroundAllocKind(kind);
    1353                 : 
    1354            6400 :     GlobalObject *global = &shape->getObjectParent()->global();
    1355            6400 :     types::TypeObject *type = proto->getNewType(cx);
    1356                 : 
    1357                 :     EntryIndex entry;
    1358            6400 :     if (lookupGlobal(clasp, global, kind, &entry))
    1359            2556 :         PodZero(&entries[entry]);
    1360            6400 :     if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry))
    1361            3844 :         PodZero(&entries[entry]);
    1362            6400 :     if (lookupType(clasp, type, kind, &entry))
    1363               0 :         PodZero(&entries[entry]);
    1364            6400 : }
    1365                 : 
    1366                 : /* static */ void
    1367            6400 : EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
    1368                 : {
    1369                 :     InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
    1370            6400 :                                      shape->numFixedSlots(), shape->getObjectFlags());
    1371                 : 
    1372            6400 :     InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
    1373            6400 :     JS_ASSERT(p);
    1374                 : 
    1375            6400 :     InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
    1376            6400 :     JS_ASSERT(entry.shape->isEmptyShape());
    1377                 : 
    1378                 :     /* The new shape had better be rooted at the old one. */
    1379                 : #ifdef DEBUG
    1380            6400 :     const Shape *nshape = shape;
    1381           32595 :     while (!nshape->isEmptyShape())
    1382           19795 :         nshape = nshape->previous();
    1383            6400 :     JS_ASSERT(nshape == entry.shape);
    1384                 : #endif
    1385                 : 
    1386            6400 :     entry.shape = shape;
    1387                 : 
    1388                 :     /*
    1389                 :      * This affects the shape that will be produced by the various NewObject
    1390                 :      * methods, so clear any cache entry referring to the old shape. This is
    1391                 :      * not required for correctness (though it may bust on the above asserts):
    1392                 :      * the NewObject must always check for a nativeEmpty() result and generate
    1393                 :      * the appropriate properties if found. Clearing the cache entry avoids
    1394                 :      * this duplicate regeneration.
    1395                 :      */
    1396            6400 :     cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
    1397            6400 : }
    1398                 : 
    1399                 : void
    1400           83697 : JSCompartment::sweepInitialShapeTable()
    1401                 : {
    1402           83697 :     if (initialShapes.initialized()) {
    1403         4078121 :         for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
    1404         4032707 :             const InitialShapeEntry &entry = e.front();
    1405         4032707 :             if (!entry.shape->isMarked() || (entry.proto && !entry.proto->isMarked()))
    1406         1860050 :                 e.removeFront();
    1407                 :         }
    1408                 :     }
    1409           83697 : }

Generated by: LCOV version 1.7