LCOV - app.info - js/src/jsscope.cpp

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