LCOV - code coverage report
Current view: directory - js/src - jspropertytree.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 172 93 54.1 %
Date: 2012-04-07 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 "jsgcinlines.h"
      51                 : #include "jsobjinlines.h"
      52                 : #include "jsscopeinlines.h"
      53                 : 
      54                 : using namespace js;
      55                 : 
      56                 : inline HashNumber
      57         6236301 : ShapeHasher::hash(const Lookup &l)
      58                 : {
      59         6236301 :     return l.hash();
      60                 : }
      61                 : 
      62                 : inline bool
      63         4136813 : ShapeHasher::match(const Key k, const Lookup &l)
      64                 : {
      65         4136813 :     return k->matches(l);
      66                 : }
      67                 : 
      68                 : Shape *
      69        15359492 : PropertyTree::newShape(JSContext *cx)
      70                 : {
      71        15359492 :     Shape *shape = js_NewGCShape(cx);
      72        15359492 :     if (!shape) {
      73               0 :         JS_ReportOutOfMemory(cx);
      74               0 :         return NULL;
      75                 :     }
      76        15359492 :     return shape;
      77                 : }
      78                 : 
      79                 : static KidsHash *
      80          708487 : HashChildren(Shape *kid1, Shape *kid2)
      81                 : {
      82          708487 :     KidsHash *hash = OffTheBooks::new_<KidsHash>();
      83          708487 :     if (!hash || !hash->init(2)) {
      84               0 :         Foreground::delete_(hash);
      85               0 :         return NULL;
      86                 :     }
      87                 : 
      88          708487 :     JS_ALWAYS_TRUE(hash->putNew(kid1, kid1));
      89          708487 :     JS_ALWAYS_TRUE(hash->putNew(kid2, kid2));
      90          708487 :     return hash;
      91                 : }
      92                 : 
      93                 : bool
      94        13499442 : PropertyTree::insertChild(JSContext *cx, Shape *parent, Shape *child)
      95                 : {
      96        13499442 :     JS_ASSERT(!parent->inDictionary());
      97        13499442 :     JS_ASSERT(!child->parent);
      98        13499442 :     JS_ASSERT(!child->inDictionary());
      99        13499442 :     JS_ASSERT(cx->compartment == compartment);
     100        13499442 :     JS_ASSERT(child->compartment() == parent->compartment());
     101                 : 
     102        13499442 :     KidsPointer *kidp = &parent->kids;
     103                 : 
     104        13499442 :     if (kidp->isNull()) {
     105        12449698 :         child->setParent(parent);
     106        12449698 :         kidp->setShape(child);
     107        12449698 :         return true;
     108                 :     }
     109                 : 
     110         1049744 :     if (kidp->isShape()) {
     111          708487 :         Shape *shape = kidp->toShape();
     112          708487 :         JS_ASSERT(shape != child);
     113          708487 :         JS_ASSERT(!shape->matches(child));
     114                 : 
     115          708487 :         KidsHash *hash = HashChildren(shape, child);
     116          708487 :         if (!hash) {
     117               0 :             JS_ReportOutOfMemory(cx);
     118               0 :             return false;
     119                 :         }
     120          708487 :         kidp->setHash(hash);
     121          708487 :         child->setParent(parent);
     122          708487 :         return true;
     123                 :     }
     124                 : 
     125          341257 :     if (!kidp->toHash()->putNew(child, child)) {
     126               0 :         JS_ReportOutOfMemory(cx);
     127               0 :         return false;
     128                 :     }
     129                 : 
     130          341257 :     child->setParent(parent);
     131          341257 :     return true;
     132                 : }
     133                 : 
     134                 : void
     135           44584 : Shape::removeChild(Shape *child)
     136                 : {
     137           44584 :     JS_ASSERT(!child->inDictionary());
     138                 : 
     139           44584 :     KidsPointer *kidp = &kids;
     140                 : 
     141           44584 :     if (kidp->isShape()) {
     142            3372 :         JS_ASSERT(kidp->toShape() == child);
     143            3372 :         kidp->setNull();
     144            3372 :         return;
     145                 :     }
     146                 : 
     147           41212 :     KidsHash *hash = kidp->toHash();
     148           41212 :     JS_ASSERT(hash->count() >= 2);      /* otherwise kidp->isShape() should be true */
     149                 : 
     150           41212 :     hash->remove(child);
     151                 : 
     152           41212 :     if (hash->count() == 1) {
     153                 :         /* Convert from HASH form back to SHAPE form. */
     154           26111 :         KidsHash::Range r = hash->all();
     155           26111 :         Shape *otherChild = r.front();
     156           26111 :         JS_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
     157           26111 :         kidp->setShape(otherChild);
     158           26111 :         js::UnwantedForeground::delete_(hash);
     159                 :     }
     160                 : }
     161                 : 
     162                 : /*
     163                 :  * We need a read barrier for the shape tree, since these are weak pointers.
     164                 :  */
     165                 : static Shape *
     166        19719003 : ReadBarrier(Shape *shape)
     167                 : {
     168                 : #ifdef JSGC_INCREMENTAL
     169        19719003 :     JSCompartment *comp = shape->compartment();
     170        19719003 :     if (comp->needsBarrier()) {
     171            1059 :         Shape *tmp = shape;
     172            1059 :         MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
     173            1059 :         JS_ASSERT(tmp == shape);
     174                 :     }
     175                 : #endif
     176        19719003 :     return shape;
     177                 : }
     178                 : 
     179                 : Shape *
     180        33218445 : PropertyTree::getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child)
     181                 : {
     182                 :     Shape *shape;
     183                 : 
     184        33218445 :     JS_ASSERT(parent);
     185                 : 
     186                 :     /*
     187                 :      * The property tree has extremely low fan-out below its root in
     188                 :      * popular embeddings with real-world workloads. Patterns such as
     189                 :      * defining closures that capture a constructor's environment as
     190                 :      * getters or setters on the new object that is passed in as
     191                 :      * |this| can significantly increase fan-out below the property
     192                 :      * tree root -- see bug 335700 for details.
     193                 :      */
     194        33218445 :     KidsPointer *kidp = &parent->kids;
     195        33218445 :     if (kidp->isShape()) {
     196        16331889 :         shape = kidp->toShape();
     197        16331889 :         if (shape->matches(child))
     198        15623402 :             return ReadBarrier(shape);
     199        16886556 :     } else if (kidp->isHash()) {
     200         4436858 :         shape = *kidp->toHash()->lookup(child);
     201         4436858 :         if (shape)
     202         4095601 :             return ReadBarrier(shape);
     203                 :     } else {
     204                 :         /* If kidp->isNull(), we always insert. */
     205                 :     }
     206                 : 
     207        26998884 :     RootStackShape childRoot(cx, &child);
     208        26998884 :     RootShape parentRoot(cx, &parent);
     209                 : 
     210        13499442 :     shape = newShape(cx);
     211        13499442 :     if (!shape)
     212               0 :         return NULL;
     213                 : 
     214        13499442 :     new (shape) Shape(child, nfixed);
     215                 : 
     216        13499442 :     if (!insertChild(cx, parent, shape))
     217               0 :         return NULL;
     218                 : 
     219        13499442 :     return shape;
     220                 : }
     221                 : 
     222                 : void
     223        20149251 : Shape::finalize(FreeOp *fop)
     224                 : {
     225        20149251 :     if (!inDictionary()) {
     226        15359492 :         if (parent && parent->isMarked())
     227           44584 :             parent->removeChild(this);
     228                 : 
     229        15359492 :         if (kids.isHash())
     230          682376 :             fop->delete_(kids.toHash());
     231                 :     }
     232        20149251 : }
     233                 : 
     234                 : #ifdef DEBUG
     235                 : 
     236                 : void
     237               0 : KidsPointer::checkConsistency(const Shape *aKid) const
     238                 : {
     239               0 :     if (isShape()) {
     240               0 :         JS_ASSERT(toShape() == aKid);
     241                 :     } else {
     242               0 :         JS_ASSERT(isHash());
     243               0 :         KidsHash *hash = toHash();
     244               0 :         KidsHash::Ptr ptr = hash->lookup(aKid);
     245               0 :         JS_ASSERT(*ptr == aKid);
     246                 :     }
     247               0 : }
     248                 : 
     249                 : void
     250               0 : Shape::dump(JSContext *cx, FILE *fp) const
     251                 : {
     252               0 :     jsid propid = this->propid();
     253                 : 
     254               0 :     JS_ASSERT(!JSID_IS_VOID(propid));
     255                 : 
     256               0 :     if (JSID_IS_INT(propid)) {
     257               0 :         fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
     258               0 :     } else if (JSID_IS_DEFAULT_XML_NAMESPACE(propid)) {
     259               0 :         fprintf(fp, "<default XML namespace>");
     260                 :     } else {
     261                 :         JSLinearString *str;
     262               0 :         if (JSID_IS_ATOM(propid)) {
     263               0 :             str = JSID_TO_ATOM(propid);
     264                 :         } else {
     265               0 :             JS_ASSERT(JSID_IS_OBJECT(propid));
     266               0 :             JSString *s = ToStringSlow(cx, IdToValue(propid));
     267               0 :             fputs("object ", fp);
     268               0 :             str = s ? s->ensureLinear(cx) : NULL;
     269                 :         }
     270               0 :         if (!str)
     271               0 :             fputs("<error>", fp);
     272                 :         else
     273               0 :             FileEscapedString(fp, str, '"');
     274                 :     }
     275                 : 
     276                 :     fprintf(fp, " g/s %p/%p slot %d attrs %x ",
     277               0 :             JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
     278               0 :             JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
     279               0 :             hasSlot() ? slot() : -1, attrs);
     280                 : 
     281               0 :     if (attrs) {
     282               0 :         int first = 1;
     283               0 :         fputs("(", fp);
     284                 : #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0
     285               0 :         DUMP_ATTR(ENUMERATE, enumerate);
     286               0 :         DUMP_ATTR(READONLY, readonly);
     287               0 :         DUMP_ATTR(PERMANENT, permanent);
     288               0 :         DUMP_ATTR(GETTER, getter);
     289               0 :         DUMP_ATTR(SETTER, setter);
     290               0 :         DUMP_ATTR(SHARED, shared);
     291                 : #undef  DUMP_ATTR
     292               0 :         fputs(") ", fp);
     293                 :     }
     294                 : 
     295               0 :     fprintf(fp, "flags %x ", flags);
     296               0 :     if (flags) {
     297               0 :         int first = 1;
     298               0 :         fputs("(", fp);
     299                 : #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0
     300               0 :         DUMP_FLAG(HAS_SHORTID, has_shortid);
     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           38427 : js::PropertyTree::dumpShapes(JSRuntime *rt)
     341                 : {
     342                 :     static bool init = false;
     343                 :     static FILE *dumpfp = NULL;
     344           38427 :     if (!init) {
     345           18667 :         init = true;
     346           18667 :         const char *name = getenv("JS_DUMP_SHAPES_FILE");
     347           18667 :         if (!name)
     348           18667 :             return;
     349               0 :         dumpfp = fopen(name, "a");
     350                 :     }
     351                 : 
     352           19760 :     if (!dumpfp)
     353           19760 :         return;
     354                 : 
     355               0 :     fprintf(dumpfp, "rt->gcNumber = %lu", (unsigned long)rt->gcNumber);
     356                 : 
     357               0 :     for (gc::GCCompartmentsIter c(rt); !c.done(); c.next()) {
     358               0 :         fprintf(dumpfp, "*** Compartment %p ***\n", (void *)c.get());
     359                 : 
     360                 :         /*
     361                 :         typedef JSCompartment::EmptyShapeSet HS;
     362                 :         HS &h = c->emptyShapes;
     363                 :         for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
     364                 :             Shape *empty = r.front();
     365                 :             empty->dumpSubtree(rt, 0, dumpfp);
     366                 :             putc('\n', dumpfp);
     367                 :         }
     368                 :         */
     369                 :     }
     370                 : }
     371                 : #endif

Generated by: LCOV version 1.7