LCOV - code coverage report
Current view: directory - js/src - jspropertytree.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 176 93 52.8 %
Date: 2012-06-02 Functions: 13 10 76.9 %

       1                 : /* -*- Mode: c++; c-basic-offset: 4; tab-width: 40; indent-tabs-mode: nil -*- */
       2                 : /* vim: set ts=40 sw=4 et tw=99: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is the Mozilla SpiderMonkey property tree implementation
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  *   Mozilla Foundation
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2002-2010
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Brendan Eich <brendan@mozilla.org>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include <new>
      41                 : 
      42                 : #include "jstypes.h"
      43                 : #include "jsprf.h"
      44                 : #include "jsapi.h"
      45                 : #include "jscntxt.h"
      46                 : #include "jsgc.h"
      47                 : #include "jspropertytree.h"
      48                 : #include "jsscope.h"
      49                 : 
      50                 : #include "jsobjinlines.h"
      51                 : #include "jsscopeinlines.h"
      52                 : 
      53                 : using namespace js;
      54                 : 
      55                 : inline HashNumber
      56        12426392 : ShapeHasher::hash(const Lookup &l)
      57                 : {
      58        12426392 :     return l.hash();
      59                 : }
      60                 : 
      61                 : inline bool
      62         7710225 : ShapeHasher::match(const Key k, const Lookup &l)
      63                 : {
      64         7710225 :     return k->matches(l);
      65                 : }
      66                 : 
      67                 : Shape *
      68        24012359 : PropertyTree::newShape(JSContext *cx)
      69                 : {
      70        24012359 :     Shape *shape = js_NewGCShape(cx);
      71        24012359 :     if (!shape) {
      72               0 :         JS_ReportOutOfMemory(cx);
      73               0 :         return NULL;
      74                 :     }
      75        24012359 :     return shape;
      76                 : }
      77                 : 
      78                 : static KidsHash *
      79          973294 : HashChildren(Shape *kid1, Shape *kid2)
      80                 : {
      81          973294 :     KidsHash *hash = OffTheBooks::new_<KidsHash>();
      82          973294 :     if (!hash || !hash->init(2)) {
      83               0 :         Foreground::delete_(hash);
      84               0 :         return NULL;
      85                 :     }
      86                 : 
      87          973294 :     JS_ALWAYS_TRUE(hash->putNew(kid1, kid1));
      88          973294 :     JS_ALWAYS_TRUE(hash->putNew(kid2, kid2));
      89          973294 :     return hash;
      90                 : }
      91                 : 
      92                 : bool
      93        20299635 : PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child)
      94                 : {
      95        20299635 :     JS_ASSERT(!parent->inDictionary());
      96        20299635 :     JS_ASSERT(!child->parent);
      97        20299635 :     JS_ASSERT(!child->inDictionary());
      98        20299635 :     JS_ASSERT(cx->compartment == compartment);
      99        20299635 :     JS_ASSERT(child->compartment() == parent->compartment());
     100                 : 
     101        20299635 :     KidsPointer *kidp = &parent->kids;
     102                 : 
     103        20299635 :     if (kidp->isNull()) {
     104        17941494 :         child->setParent(parent);
     105        17941494 :         kidp->setShape(child);
     106        17941494 :         return true;
     107                 :     }
     108                 : 
     109         2358141 :     if (kidp->isShape()) {
     110          973294 :         Shape *shape = kidp->toShape();
     111          973294 :         JS_ASSERT(shape != child);
     112          973294 :         JS_ASSERT(!shape->matches(child));
     113                 : 
     114          973294 :         KidsHash *hash = HashChildren(shape, child);
     115          973294 :         if (!hash) {
     116               0 :             JS_ReportOutOfMemory(cx);
     117               0 :             return false;
     118                 :         }
     119          973294 :         kidp->setHash(hash);
     120          973294 :         child->setParent(parent);
     121          973294 :         return true;
     122                 :     }
     123                 : 
     124         1384847 :     if (!kidp->toHash()->putNew(child, child)) {
     125               0 :         JS_ReportOutOfMemory(cx);
     126               0 :         return false;
     127                 :     }
     128                 : 
     129         1384847 :     child->setParent(parent);
     130         1384847 :     return true;
     131                 : }
     132                 : 
     133                 : void
     134          949911 : Shape::removeChild(Shape *child)
     135                 : {
     136          949911 :     JS_ASSERT(!child->inDictionary());
     137                 : 
     138          949911 :     KidsPointer *kidp = &kids;
     139                 : 
     140          949911 :     if (kidp->isShape()) {
     141           18928 :         JS_ASSERT(kidp->toShape() == child);
     142           18928 :         kidp->setNull();
     143           18928 :         return;
     144                 :     }
     145                 : 
     146          930983 :     KidsHash *hash = kidp->toHash();
     147          930983 :     JS_ASSERT(hash->count() >= 2);      /* otherwise kidp->isShape() should be true */
     148                 : 
     149          930983 :     hash->remove(child);
     150                 : 
     151          930983 :     if (hash->count() == 1) {
     152                 :         /* Convert from HASH form back to SHAPE form. */
     153          158260 :         KidsHash::Range r = hash->all();
     154          158260 :         Shape *otherChild = r.front();
     155          158260 :         JS_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
     156          158260 :         kidp->setShape(otherChild);
     157          158260 :         js::UnwantedForeground::delete_(hash);
     158                 :     }
     159                 : }
     160                 : 
     161                 : /*
     162                 :  * We need a read barrier for the shape tree, since these are weak pointers.
     163                 :  */
     164                 : static Shape *
     165        24717519 : ReadBarrier(Shape *shape)
     166                 : {
     167                 : #ifdef JSGC_INCREMENTAL
     168        24717519 :     JSCompartment *comp = shape->compartment();
     169        24717519 :     if (comp->needsBarrier()) {
     170            4524 :         Shape *tmp = shape;
     171            4524 :         MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
     172            4524 :         JS_ASSERT(tmp == shape);
     173                 :     }
     174                 : #endif
     175        24717519 :     return shape;
     176                 : }
     177                 : 
     178                 : Shape *
     179        45017154 : PropertyTree::getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child)
     180                 : {
     181                 :     Shape *shape;
     182                 : 
     183        45017154 :     JS_ASSERT(parent);
     184                 : 
     185                 :     /*
     186                 :      * The property tree has extremely low fan-out below its root in
     187                 :      * popular embeddings with real-world workloads. Patterns such as
     188                 :      * defining closures that capture a constructor's environment as
     189                 :      * getters or setters on the new object that is passed in as
     190                 :      * |this| can significantly increase fan-out below the property
     191                 :      * tree root -- see bug 335700 for details.
     192                 :      */
     193        45017154 :     KidsPointer *kidp = &parent->kids;
     194        45017154 :     if (kidp->isShape()) {
     195        18911690 :         shape = kidp->toShape();
     196        18911690 :         if (shape->matches(child))
     197        17938397 :             return ReadBarrier(shape);
     198        26105464 :     } else if (kidp->isHash()) {
     199         8163974 :         shape = *kidp->toHash()->lookup(child);
     200         8163974 :         if (shape)
     201         6779122 :             return ReadBarrier(shape);
     202                 :     } else {
     203                 :         /* If kidp->isNull(), we always insert. */
     204                 :     }
     205                 : 
     206        40599270 :     RootStackShape childRoot(cx, &child);
     207        40599270 :     RootShape parentRoot(cx, &parent);
     208                 : 
     209        20299635 :     shape = newShape(cx);
     210        20299635 :     if (!shape)
     211               0 :         return NULL;
     212                 : 
     213        20299635 :     new (shape) Shape(child, nfixed);
     214                 : 
     215        20299635 :     if (!insertChild(cx, parent, shape))
     216               0 :         return NULL;
     217                 : 
     218        20299635 :     return shape;
     219                 : }
     220                 : 
     221                 : void
     222        32707277 : Shape::finalize(JSContext *cx, bool background)
     223                 : {
     224        32707277 :     if (!inDictionary()) {
     225        24008833 :         if (parent && parent->isMarked())
     226          949911 :             parent->removeChild(this);
     227                 : 
     228        24008833 :         if (kids.isHash())
     229          814908 :             cx->delete_(kids.toHash());
     230                 :     }
     231        32707277 : }
     232                 : 
     233                 : #ifdef DEBUG
     234                 : 
     235                 : void
     236               0 : KidsPointer::checkConsistency(const Shape *aKid) const
     237                 : {
     238               0 :     if (isShape()) {
     239               0 :         JS_ASSERT(toShape() == aKid);
     240                 :     } else {
     241               0 :         JS_ASSERT(isHash());
     242               0 :         KidsHash *hash = toHash();
     243               0 :         KidsHash::Ptr ptr = hash->lookup(aKid);
     244               0 :         JS_ASSERT(*ptr == aKid);
     245                 :     }
     246               0 : }
     247                 : 
     248                 : void
     249               0 : Shape::dump(JSContext *cx, FILE *fp) const
     250                 : {
     251               0 :     jsid propid = this->propid();
     252                 : 
     253               0 :     JS_ASSERT(!JSID_IS_VOID(propid));
     254                 : 
     255               0 :     if (JSID_IS_INT(propid)) {
     256               0 :         fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
     257               0 :     } else if (JSID_IS_DEFAULT_XML_NAMESPACE(propid)) {
     258               0 :         fprintf(fp, "<default XML namespace>");
     259                 :     } else {
     260                 :         JSLinearString *str;
     261               0 :         if (JSID_IS_ATOM(propid)) {
     262               0 :             str = JSID_TO_ATOM(propid);
     263                 :         } else {
     264               0 :             JS_ASSERT(JSID_IS_OBJECT(propid));
     265               0 :             JSString *s = ToStringSlow(cx, IdToValue(propid));
     266               0 :             fputs("object ", fp);
     267               0 :             str = s ? s->ensureLinear(cx) : NULL;
     268                 :         }
     269               0 :         if (!str)
     270               0 :             fputs("<error>", fp);
     271                 :         else
     272               0 :             FileEscapedString(fp, str, '"');
     273                 :     }
     274                 : 
     275                 :     fprintf(fp, " g/s %p/%p slot %d attrs %x ",
     276               0 :             JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
     277               0 :             JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
     278               0 :             hasSlot() ? slot() : -1, attrs);
     279                 : 
     280               0 :     if (attrs) {
     281               0 :         int first = 1;
     282               0 :         fputs("(", fp);
     283                 : #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0
     284               0 :         DUMP_ATTR(ENUMERATE, enumerate);
     285               0 :         DUMP_ATTR(READONLY, readonly);
     286               0 :         DUMP_ATTR(PERMANENT, permanent);
     287               0 :         DUMP_ATTR(GETTER, getter);
     288               0 :         DUMP_ATTR(SETTER, setter);
     289               0 :         DUMP_ATTR(SHARED, shared);
     290                 : #undef  DUMP_ATTR
     291               0 :         fputs(") ", fp);
     292                 :     }
     293                 : 
     294               0 :     fprintf(fp, "flags %x ", flags);
     295               0 :     if (flags) {
     296               0 :         int first = 1;
     297               0 :         fputs("(", fp);
     298                 : #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0
     299               0 :         DUMP_FLAG(HAS_SHORTID, has_shortid);
     300               0 :         DUMP_FLAG(METHOD, method);
     301               0 :         DUMP_FLAG(IN_DICTIONARY, in_dictionary);
     302                 : #undef  DUMP_FLAG
     303               0 :         fputs(") ", fp);
     304                 :     }
     305                 : 
     306               0 :     fprintf(fp, "shortid %d\n", shortid());
     307               0 : }
     308                 : 
     309                 : void
     310               0 : Shape::dumpSubtree(JSContext *cx, int level, FILE *fp) const
     311                 : {
     312               0 :     if (!parent) {
     313               0 :         JS_ASSERT(level == 0);
     314               0 :         JS_ASSERT(JSID_IS_EMPTY(propid_));
     315               0 :         fprintf(fp, "class %s emptyShape\n", getObjectClass()->name);
     316                 :     } else {
     317               0 :         fprintf(fp, "%*sid ", level, "");
     318               0 :         dump(cx, fp);
     319                 :     }
     320                 : 
     321               0 :     if (!kids.isNull()) {
     322               0 :         ++level;
     323               0 :         if (kids.isShape()) {
     324               0 :             Shape *kid = kids.toShape();
     325               0 :             JS_ASSERT(kid->parent == this);
     326               0 :             kid->dumpSubtree(cx, level, fp);
     327                 :         } else {
     328               0 :             const KidsHash &hash = *kids.toHash();
     329               0 :             for (KidsHash::Range range = hash.all(); !range.empty(); range.popFront()) {
     330               0 :                 Shape *kid = range.front();
     331                 : 
     332               0 :                 JS_ASSERT(kid->parent == this);
     333               0 :                 kid->dumpSubtree(cx, level, fp);
     334                 :             }
     335                 :         }
     336                 :     }
     337               0 : }
     338                 : 
     339                 : void
     340           51092 : js::PropertyTree::dumpShapes(JSContext *cx)
     341                 : {
     342                 :     static bool init = false;
     343                 :     static FILE *dumpfp = NULL;
     344           51092 :     if (!init) {
     345           19810 :         init = true;
     346           19810 :         const char *name = getenv("JS_DUMP_SHAPES_FILE");
     347           19810 :         if (!name)
     348           19810 :             return;
     349               0 :         dumpfp = fopen(name, "a");
     350                 :     }
     351                 : 
     352           31282 :     if (!dumpfp)
     353           31282 :         return;
     354                 : 
     355               0 :     JSRuntime *rt = cx->runtime;
     356               0 :     fprintf(dumpfp, "rt->gcNumber = %lu", (unsigned long)rt->gcNumber);
     357                 : 
     358               0 :     for (CompartmentsIter c(rt); !c.done(); c.next()) {
     359               0 :         if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != c)
     360               0 :             continue;
     361                 : 
     362               0 :         fprintf(dumpfp, "*** Compartment %p ***\n", (void *)c.get());
     363                 : 
     364                 :         /*
     365                 :         typedef JSCompartment::EmptyShapeSet HS;
     366                 :         HS &h = c->emptyShapes;
     367                 :         for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
     368                 :             Shape *empty = r.front();
     369                 :             empty->dumpSubtree(cx, 0, dumpfp);
     370                 :             putc('\n', dumpfp);
     371                 :         }
     372                 :         */
     373                 :     }
     374                 : }
     375                 : #endif

Generated by: LCOV version 1.7