js/src/jspropertytree.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jspropertytree.h"
     9 #include "jscntxt.h"
    10 #include "jsgc.h"
    11 #include "jstypes.h"
    13 #include "vm/Shape.h"
    15 #include "jsgcinlines.h"
    17 #include "vm/Shape-inl.h"
    19 using namespace js;
    21 inline HashNumber
    22 ShapeHasher::hash(const Lookup &l)
    23 {
    24     return l.hash();
    25 }
    27 inline bool
    28 ShapeHasher::match(const Key k, const Lookup &l)
    29 {
    30     return k->matches(l);
    31 }
    33 Shape *
    34 PropertyTree::newShape(ExclusiveContext *cx)
    35 {
    36     Shape *shape = js_NewGCShape(cx);
    37     if (!shape)
    38         js_ReportOutOfMemory(cx);
    39     return shape;
    40 }
    42 static KidsHash *
    43 HashChildren(Shape *kid1, Shape *kid2)
    44 {
    45     KidsHash *hash = js_new<KidsHash>();
    46     if (!hash || !hash->init(2)) {
    47         js_delete(hash);
    48         return nullptr;
    49     }
    51     JS_ALWAYS_TRUE(hash->putNew(kid1, kid1));
    52     JS_ALWAYS_TRUE(hash->putNew(kid2, kid2));
    53     return hash;
    54 }
    56 bool
    57 PropertyTree::insertChild(ExclusiveContext *cx, Shape *parent, Shape *child)
    58 {
    59     JS_ASSERT(!parent->inDictionary());
    60     JS_ASSERT(!child->parent);
    61     JS_ASSERT(!child->inDictionary());
    62     JS_ASSERT(child->compartment() == parent->compartment());
    63     JS_ASSERT(cx->isInsideCurrentCompartment(this));
    65     KidsPointer *kidp = &parent->kids;
    67     if (kidp->isNull()) {
    68         child->setParent(parent);
    69         kidp->setShape(child);
    70         return true;
    71     }
    73     if (kidp->isShape()) {
    74         Shape *shape = kidp->toShape();
    75         JS_ASSERT(shape != child);
    76         JS_ASSERT(!shape->matches(child));
    78         KidsHash *hash = HashChildren(shape, child);
    79         if (!hash) {
    80             js_ReportOutOfMemory(cx);
    81             return false;
    82         }
    83         kidp->setHash(hash);
    84         child->setParent(parent);
    85         return true;
    86     }
    88     if (!kidp->toHash()->putNew(child, child)) {
    89         js_ReportOutOfMemory(cx);
    90         return false;
    91     }
    93     child->setParent(parent);
    94     return true;
    95 }
    97 void
    98 Shape::removeChild(Shape *child)
    99 {
   100     JS_ASSERT(!child->inDictionary());
   101     JS_ASSERT(child->parent == this);
   103     KidsPointer *kidp = &kids;
   105     if (kidp->isShape()) {
   106         JS_ASSERT(kidp->toShape() == child);
   107         kidp->setNull();
   108         child->parent = nullptr;
   109         return;
   110     }
   112     KidsHash *hash = kidp->toHash();
   113     JS_ASSERT(hash->count() >= 2);      /* otherwise kidp->isShape() should be true */
   115     hash->remove(child);
   116     child->parent = nullptr;
   118     if (hash->count() == 1) {
   119         /* Convert from HASH form back to SHAPE form. */
   120         KidsHash::Range r = hash->all();
   121         Shape *otherChild = r.front();
   122         JS_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
   123         kidp->setShape(otherChild);
   124         js_delete(hash);
   125     }
   126 }
   128 Shape *
   129 PropertyTree::getChild(ExclusiveContext *cx, Shape *parentArg, StackShape &unrootedChild)
   130 {
   131     RootedShape parent(cx, parentArg);
   132     JS_ASSERT(parent);
   134     Shape *existingShape = nullptr;
   136     /*
   137      * The property tree has extremely low fan-out below its root in
   138      * popular embeddings with real-world workloads. Patterns such as
   139      * defining closures that capture a constructor's environment as
   140      * getters or setters on the new object that is passed in as
   141      * |this| can significantly increase fan-out below the property
   142      * tree root -- see bug 335700 for details.
   143      */
   144     KidsPointer *kidp = &parent->kids;
   145     if (kidp->isShape()) {
   146         Shape *kid = kidp->toShape();
   147         if (kid->matches(unrootedChild))
   148         existingShape = kid;
   149     } else if (kidp->isHash()) {
   150         if (KidsHash::Ptr p = kidp->toHash()->lookup(unrootedChild))
   151         existingShape = *p;
   152     } else {
   153         /* If kidp->isNull(), we always insert. */
   154     }
   156 #ifdef JSGC_INCREMENTAL
   157     if (existingShape) {
   158         JS::Zone *zone = existingShape->zone();
   159         if (zone->needsBarrier()) {
   160             /*
   161              * We need a read barrier for the shape tree, since these are weak
   162              * pointers.
   163              */
   164             Shape *tmp = existingShape;
   165             MarkShapeUnbarriered(zone->barrierTracer(), &tmp, "read barrier");
   166             JS_ASSERT(tmp == existingShape);
   167         } else if (zone->isGCSweeping() && !existingShape->isMarked() &&
   168                    !existingShape->arenaHeader()->allocatedDuringIncremental)
   169         {
   170             /*
   171              * The shape we've found is unreachable and due to be finalized, so
   172              * remove our weak reference to it and don't use it.
   173              */
   174             JS_ASSERT(parent->isMarked());
   175             parent->removeChild(existingShape);
   176             existingShape = nullptr;
   177         }
   178     }
   179 #endif
   181     if (existingShape)
   182         return existingShape;
   184     RootedGeneric<StackShape*> child(cx, &unrootedChild);
   186     Shape *shape = newShape(cx);
   187     if (!shape)
   188         return nullptr;
   190     new (shape) Shape(*child, parent->numFixedSlots());
   192     if (!insertChild(cx, parent, shape))
   193         return nullptr;
   195     return shape;
   196 }
   198 Shape *
   199 PropertyTree::lookupChild(ThreadSafeContext *cx, Shape *parent, const StackShape &child)
   200 {
   201     /* Keep this in sync with the logic of getChild above. */
   202     Shape *shape = nullptr;
   204     JS_ASSERT(parent);
   206     KidsPointer *kidp = &parent->kids;
   207     if (kidp->isShape()) {
   208         Shape *kid = kidp->toShape();
   209         if (kid->matches(child))
   210             shape = kid;
   211     } else if (kidp->isHash()) {
   212         if (KidsHash::Ptr p = kidp->toHash()->readonlyThreadsafeLookup(child))
   213             shape = *p;
   214     } else {
   215         return nullptr;
   216     }
   218 #if defined(JSGC_INCREMENTAL) && defined(DEBUG)
   219     if (shape) {
   220         JS::Zone *zone = shape->arenaHeader()->zone;
   221         JS_ASSERT(!zone->needsBarrier());
   222         JS_ASSERT(!(zone->isGCSweeping() && !shape->isMarked() &&
   223                     !shape->arenaHeader()->allocatedDuringIncremental));
   224     }
   225 #endif
   227     return shape;
   228 }
   230 void
   231 Shape::sweep()
   232 {
   233     if (inDictionary())
   234         return;
   236     /*
   237      * We detach the child from the parent if the parent is reachable.
   238      *
   239      * Note that due to incremental sweeping, the parent pointer may point
   240      * to the original reachable parent, or it may point to a new live
   241      * object allocated in the same cell that used to hold the parent.
   242      *
   243      * There are three cases:
   244      *
   245      * Case 1: parent is not marked - parent is unreachable, may have been
   246      *         finalized, and the cell may subsequently have been
   247      *         reallocated to a compartment that is not being marked (cells
   248      *         are marked when allocated in a compartment that is currenly
   249      *         being marked by the collector).
   250      *
   251      * Case 2: parent is marked and is in a different compartment - parent
   252      *         has been freed and reallocated to compartment that was being
   253      *         marked.
   254      *
   255      * Case 3: parent is marked and is in the same compartment - parent is
   256      *         stil reachable and we need to detach from it.
   257      */
   258     if (parent && parent->isMarked() && parent->compartment() == compartment())
   259         parent->removeChild(this);
   260 }
   262 void
   263 Shape::finalize(FreeOp *fop)
   264 {
   265     if (!inDictionary() && kids.isHash())
   266         fop->delete_(kids.toHash());
   267 }
   269 #ifdef DEBUG
   271 void
   272 KidsPointer::checkConsistency(Shape *aKid) const
   273 {
   274     if (isShape()) {
   275         JS_ASSERT(toShape() == aKid);
   276     } else {
   277         JS_ASSERT(isHash());
   278         KidsHash *hash = toHash();
   279         KidsHash::Ptr ptr = hash->lookup(aKid);
   280         JS_ASSERT(*ptr == aKid);
   281     }
   282 }
   284 void
   285 Shape::dump(JSContext *cx, FILE *fp) const
   286 {
   287     /* This is only used from gdb, so allowing GC here would just be confusing. */
   288     gc::AutoSuppressGC suppress(cx);
   290     jsid propid = this->propid();
   292     JS_ASSERT(!JSID_IS_VOID(propid));
   294     if (JSID_IS_INT(propid)) {
   295         fprintf(fp, "[%ld]", (long) JSID_TO_INT(propid));
   296     } else {
   297         JSLinearString *str;
   298         if (JSID_IS_ATOM(propid)) {
   299             str = JSID_TO_ATOM(propid);
   300         } else {
   301             JS_ASSERT(JSID_IS_OBJECT(propid));
   302             Value v = IdToValue(propid);
   303             JSString *s = ToStringSlow<NoGC>(cx, v);
   304             fputs("object ", fp);
   305             str = s ? s->ensureLinear(cx) : nullptr;
   306         }
   307         if (!str)
   308             fputs("<error>", fp);
   309         else
   310             FileEscapedString(fp, str, '"');
   311     }
   313     fprintf(fp, " g/s %p/%p slot %d attrs %x ",
   314             JS_FUNC_TO_DATA_PTR(void *, base()->rawGetter),
   315             JS_FUNC_TO_DATA_PTR(void *, base()->rawSetter),
   316             hasSlot() ? slot() : -1, attrs);
   318     if (attrs) {
   319         int first = 1;
   320         fputs("(", fp);
   321 #define DUMP_ATTR(name, display) if (attrs & JSPROP_##name) fputs(&(" " #display)[first], fp), first = 0
   322         DUMP_ATTR(ENUMERATE, enumerate);
   323         DUMP_ATTR(READONLY, readonly);
   324         DUMP_ATTR(PERMANENT, permanent);
   325         DUMP_ATTR(GETTER, getter);
   326         DUMP_ATTR(SETTER, setter);
   327         DUMP_ATTR(SHARED, shared);
   328 #undef  DUMP_ATTR
   329         fputs(") ", fp);
   330     }
   332     fprintf(fp, "flags %x ", flags);
   333     if (flags) {
   334         int first = 1;
   335         fputs("(", fp);
   336 #define DUMP_FLAG(name, display) if (flags & name) fputs(&(" " #display)[first], fp), first = 0
   337         DUMP_FLAG(IN_DICTIONARY, in_dictionary);
   338 #undef  DUMP_FLAG
   339         fputs(") ", fp);
   340     }
   341 }
   343 void
   344 Shape::dumpSubtree(JSContext *cx, int level, FILE *fp) const
   345 {
   346     if (!parent) {
   347         JS_ASSERT(level == 0);
   348         JS_ASSERT(JSID_IS_EMPTY(propid_));
   349         fprintf(fp, "class %s emptyShape\n", getObjectClass()->name);
   350     } else {
   351         fprintf(fp, "%*sid ", level, "");
   352         dump(cx, fp);
   353     }
   355     if (!kids.isNull()) {
   356         ++level;
   357         if (kids.isShape()) {
   358             Shape *kid = kids.toShape();
   359             JS_ASSERT(kid->parent == this);
   360             kid->dumpSubtree(cx, level, fp);
   361         } else {
   362             const KidsHash &hash = *kids.toHash();
   363             for (KidsHash::Range range = hash.all(); !range.empty(); range.popFront()) {
   364                 Shape *kid = range.front();
   366                 JS_ASSERT(kid->parent == this);
   367                 kid->dumpSubtree(cx, level, fp);
   368             }
   369         }
   370     }
   371 }
   373 #endif

mercurial