LCOV - code coverage report
Current view: top level - js/src/vm - Shape-inl.h (source / functions) Hit Total Coverage
Test: output.info Lines: 145 159 91.2 %
Date: 2017-07-14 16:53:18 Functions: 27 28 96.4 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #ifndef vm_Shape_inl_h
       8             : #define vm_Shape_inl_h
       9             : 
      10             : #include "vm/Shape.h"
      11             : 
      12             : #include "mozilla/TypeTraits.h"
      13             : 
      14             : #include "jsobj.h"
      15             : 
      16             : #include "gc/Allocator.h"
      17             : #include "vm/Interpreter.h"
      18             : #include "vm/TypedArrayObject.h"
      19             : 
      20             : #include "jsatominlines.h"
      21             : #include "jscntxtinlines.h"
      22             : #include "jsgcinlines.h"
      23             : 
      24             : namespace js {
      25             : 
      26             : inline
      27      127230 : AutoKeepShapeTables::AutoKeepShapeTables(JSContext* cx)
      28             :   : cx_(cx),
      29      127230 :     prev_(cx->zone()->keepShapeTables())
      30             : {
      31      127233 :     cx->zone()->setKeepShapeTables(true);
      32      127233 : }
      33             : 
      34             : inline
      35      254464 : AutoKeepShapeTables::~AutoKeepShapeTables()
      36             : {
      37      127232 :     cx_->zone()->setKeepShapeTables(prev_);
      38      127232 : }
      39             : 
      40             : inline
      41        9010 : StackBaseShape::StackBaseShape(JSContext* cx, const Class* clasp, uint32_t objectFlags)
      42             :   : flags(objectFlags),
      43        9010 :     clasp(clasp)
      44        9010 : {}
      45             : 
      46             : MOZ_ALWAYS_INLINE Shape*
      47      314580 : Shape::search(JSContext* cx, jsid id)
      48             : {
      49      314580 :     return search(cx, this, id);
      50             : }
      51             : 
      52             : MOZ_ALWAYS_INLINE bool
      53      427129 : Shape::maybeCreateTableForLookup(JSContext* cx)
      54             : {
      55      427129 :     if (hasTable())
      56      162522 :         return true;
      57             : 
      58      264606 :     if (!inDictionary() && numLinearSearches() < LINEAR_SEARCHES_MAX) {
      59      109448 :         incrementNumLinearSearches();
      60      109448 :         return true;
      61             :     }
      62             : 
      63      155166 :     if (!isBigEnoughForAShapeTable())
      64      153397 :         return true;
      65             : 
      66        1769 :     return Shape::hashify(cx, this);
      67             : }
      68             : 
      69             : template<MaybeAdding Adding>
      70             : /* static */ inline bool
      71        9809 : Shape::search(JSContext* cx, Shape* start, jsid id, const AutoKeepShapeTables& keep,
      72             :               Shape** pshape, ShapeTable::Entry** pentry)
      73             : {
      74        9809 :     if (start->inDictionary()) {
      75         853 :         ShapeTable* table = start->ensureTableForDictionary(cx, keep);
      76         853 :         if (!table)
      77           0 :             return false;
      78         853 :         *pentry = &table->search<Adding>(id, keep);
      79         853 :         *pshape = (*pentry)->shape();
      80         853 :         return true;
      81             :     }
      82             : 
      83        8956 :     *pentry = nullptr;
      84        8956 :     *pshape = Shape::search<Adding>(cx, start, id);
      85        8956 :     return true;
      86             : }
      87             : 
      88             : template<MaybeAdding Adding>
      89             : /* static */ MOZ_ALWAYS_INLINE Shape*
      90      427129 : Shape::search(JSContext* cx, Shape* start, jsid id)
      91             : {
      92      427129 :     if (start->maybeCreateTableForLookup(cx)) {
      93      689975 :         JS::AutoCheckCannotGC nogc;
      94      427133 :         if (ShapeTable* table = start->maybeTable(nogc)) {
      95      164291 :             ShapeTable::Entry& entry = table->search<Adding>(id, nogc);
      96      164291 :             return entry.shape();
      97             :         }
      98             :     } else {
      99             :         // Just do a linear search.
     100           0 :         cx->recoverFromOutOfMemory();
     101             :     }
     102             : 
     103      262845 :     return start->searchLinear(id);
     104             : }
     105             : 
     106             : inline Shape*
     107       72598 : Shape::new_(JSContext* cx, Handle<StackShape> other, uint32_t nfixed)
     108             : {
     109       72598 :     Shape* shape = other.isAccessorShape()
     110       72598 :                    ? js::Allocate<AccessorShape>(cx)
     111       72598 :                    : js::Allocate<Shape>(cx);
     112       72598 :     if (!shape) {
     113           0 :         ReportOutOfMemory(cx);
     114           0 :         return nullptr;
     115             :     }
     116             : 
     117       72598 :     if (other.isAccessorShape())
     118       15935 :         new (shape) AccessorShape(other, nfixed);
     119             :     else
     120       56663 :         new (shape) Shape(other, nfixed);
     121             : 
     122       72596 :     return shape;
     123             : }
     124             : 
     125             : inline void
     126           0 : Shape::updateBaseShapeAfterMovingGC()
     127             : {
     128           0 :     BaseShape* base = base_;
     129           0 :     if (IsForwarded(base))
     130           0 :         base_.unsafeSet(Forwarded(base));
     131           0 : }
     132             : 
     133             : template<class ObjectSubclass>
     134             : /* static */ inline bool
     135         951 : EmptyShape::ensureInitialCustomShape(JSContext* cx, Handle<ObjectSubclass*> obj)
     136             : {
     137             :     static_assert(mozilla::IsBaseOf<JSObject, ObjectSubclass>::value,
     138             :                   "ObjectSubclass must be a subclass of JSObject");
     139             : 
     140             :     // If the provided object has a non-empty shape, it was given the cached
     141             :     // initial shape when created: nothing to do.
     142         951 :     if (!obj->empty())
     143         886 :         return true;
     144             : 
     145             :     // If no initial shape was assigned, do so.
     146         130 :     RootedShape shape(cx, ObjectSubclass::assignInitialShape(cx, obj));
     147          65 :     if (!shape)
     148           0 :         return false;
     149          65 :     MOZ_ASSERT(!obj->empty());
     150             : 
     151             :     // If the object is a standard prototype -- |RegExp.prototype|,
     152             :     // |String.prototype|, |RangeError.prototype|, &c. -- GlobalObject.cpp's
     153             :     // |CreateBlankProto| marked it as a delegate.  These are the only objects
     154             :     // of this class that won't use the standard prototype, and there's no
     155             :     // reason to pollute the initial shape cache with entries for them.
     156          65 :     if (obj->isDelegate())
     157          48 :         return true;
     158             : 
     159             :     // Cache the initial shape for non-prototype objects, however, so that
     160             :     // future instances will begin life with that shape.
     161          34 :     RootedObject proto(cx, obj->staticPrototype());
     162          17 :     EmptyShape::insertInitialShape(cx, shape, proto);
     163          17 :     return true;
     164             : }
     165             : 
     166             : inline
     167       19963 : AutoRooterGetterSetter::Inner::Inner(JSContext* cx, uint8_t attrs,
     168       19963 :                                      GetterOp* pgetter_, SetterOp* psetter_)
     169             :   : CustomAutoRooter(cx), attrs(attrs),
     170       19963 :     pgetter(pgetter_), psetter(psetter_)
     171       19963 : {}
     172             : 
     173             : inline
     174      131523 : AutoRooterGetterSetter::AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
     175             :                                                GetterOp* pgetter, SetterOp* psetter
     176      131523 :                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     177             : {
     178      131523 :     if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
     179       19659 :         inner.emplace(cx, attrs, pgetter, psetter);
     180      131523 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     181      131523 : }
     182             : 
     183             : inline
     184        8106 : AutoRooterGetterSetter::AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
     185             :                                                JSNative* pgetter, JSNative* psetter
     186        8106 :                                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
     187             : {
     188        8106 :     if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
     189         608 :         inner.emplace(cx, attrs, reinterpret_cast<GetterOp*>(pgetter),
     190         912 :                       reinterpret_cast<SetterOp*>(psetter));
     191             :     }
     192        8106 :     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     193        8106 : }
     194             : 
     195             : static inline uint8_t
     196       13151 : GetPropertyAttributes(JSObject* obj, PropertyResult prop)
     197             : {
     198       13151 :     MOZ_ASSERT(obj->isNative());
     199             : 
     200       13151 :     if (prop.isDenseOrTypedArrayElement()) {
     201           0 :         if (obj->is<TypedArrayObject>())
     202           0 :             return JSPROP_ENUMERATE | JSPROP_PERMANENT;
     203           0 :         return obj->as<NativeObject>().getElementsHeader()->elementAttributes();
     204             :     }
     205             : 
     206       13151 :     return prop.shape()->attributes();
     207             : }
     208             : 
     209             : /*
     210             :  * Double hashing needs the second hash code to be relatively prime to table
     211             :  * size, so we simply make hash2 odd.
     212             :  */
     213             : MOZ_ALWAYS_INLINE HashNumber
     214      306519 : Hash1(HashNumber hash0, uint32_t shift)
     215             : {
     216      306519 :     return hash0 >> shift;
     217             : }
     218             : 
     219             : MOZ_ALWAYS_INLINE HashNumber
     220      111440 : Hash2(HashNumber hash0, uint32_t log2, uint32_t shift)
     221             : {
     222      111440 :     return ((hash0 << log2) >> shift) | 1;
     223             : }
     224             : 
     225             : template<MaybeAdding Adding>
     226             : MOZ_ALWAYS_INLINE ShapeTable::Entry&
     227      306499 : ShapeTable::searchUnchecked(jsid id)
     228             : {
     229      306499 :     MOZ_ASSERT(entries_);
     230      306499 :     MOZ_ASSERT(!JSID_IS_EMPTY(id));
     231             : 
     232             :     /* Compute the primary hash address. */
     233      306499 :     HashNumber hash0 = HashId(id);
     234      306499 :     HashNumber hash1 = Hash1(hash0, hashShift_);
     235      306499 :     Entry* entry = &getEntry(hash1);
     236             : 
     237             :     /* Miss: return space for a new entry. */
     238      306499 :     if (entry->isFree())
     239      118051 :         return *entry;
     240             : 
     241             :     /* Hit: return entry. */
     242      188448 :     Shape* shape = entry->shape();
     243      188449 :     if (shape && shape->propidRaw() == id)
     244       77016 :         return *entry;
     245             : 
     246             :     /* Collision: double hash. */
     247      111433 :     uint32_t sizeLog2 = HASH_BITS - hashShift_;
     248      111433 :     HashNumber hash2 = Hash2(hash0, sizeLog2, hashShift_);
     249      111433 :     uint32_t sizeMask = JS_BITMASK(sizeLog2);
     250             : 
     251             :     /* Save the first removed entry pointer so we can recycle it if adding. */
     252             :     Entry* firstRemoved;
     253             :     if (Adding == MaybeAdding::Adding) {
     254       19869 :         if (entry->isRemoved()) {
     255         124 :             firstRemoved = entry;
     256             :         } else {
     257       19745 :             firstRemoved = nullptr;
     258       19745 :             if (!entry->hadCollision())
     259       14778 :                 entry->flagCollision();
     260             :         }
     261             :     }
     262             : 
     263             : #ifdef DEBUG
     264      111433 :     bool collisionFlag = true;
     265      111433 :     if (!entry->isRemoved())
     266      111040 :         collisionFlag = entry->hadCollision();
     267             : #endif
     268             : 
     269             :     while (true) {
     270      318531 :         hash1 -= hash2;
     271      214982 :         hash1 &= sizeMask;
     272      214982 :         entry = &getEntry(hash1);
     273             : 
     274      214982 :         if (entry->isFree())
     275       81664 :             return (Adding == MaybeAdding::Adding && firstRemoved) ? *firstRemoved : *entry;
     276             : 
     277      133318 :         shape = entry->shape();
     278      133318 :         if (shape && shape->propidRaw() == id) {
     279       29769 :             MOZ_ASSERT(collisionFlag);
     280       29769 :             return *entry;
     281             :         }
     282             : 
     283             :         if (Adding == MaybeAdding::Adding) {
     284       11637 :             if (entry->isRemoved()) {
     285          12 :                 if (!firstRemoved)
     286          12 :                     firstRemoved = entry;
     287             :             } else {
     288       11625 :                 if (!entry->hadCollision())
     289        8136 :                     entry->flagCollision();
     290             :             }
     291             :         }
     292             : 
     293             : #ifdef DEBUG
     294      103549 :         if (!entry->isRemoved())
     295      103510 :             collisionFlag &= entry->hadCollision();
     296             : #endif
     297             :     }
     298             : 
     299             :     MOZ_CRASH("Shape::search failed to find an expected entry.");
     300             : }
     301             : 
     302             : template<MaybeAdding Adding>
     303             : MOZ_ALWAYS_INLINE ShapeTable::Entry&
     304        4032 : ShapeTable::search(jsid id, const AutoKeepShapeTables&)
     305             : {
     306        4032 :     return searchUnchecked<Adding>(id);
     307             : }
     308             : 
     309             : template<MaybeAdding Adding>
     310             : MOZ_ALWAYS_INLINE ShapeTable::Entry&
     311      238229 : ShapeTable::search(jsid id, const JS::AutoCheckCannotGC&)
     312             : {
     313      238229 :     return searchUnchecked<Adding>(id);
     314             : }
     315             : 
     316             : /*
     317             :  * Keep this function in sync with search. It neither hashifies the start
     318             :  * shape nor increments linear search count.
     319             :  */
     320             : MOZ_ALWAYS_INLINE Shape*
     321      200283 : Shape::searchNoHashify(Shape* start, jsid id)
     322             : {
     323             :     /*
     324             :      * If we have a table, search in the shape table, else do a linear
     325             :      * search. We never hashify into a table in parallel.
     326             :      */
     327      400572 :     JS::AutoCheckCannotGC nogc;
     328      200286 :     if (ShapeTable* table = start->maybeTable(nogc)) {
     329       55999 :         ShapeTable::Entry& entry = table->search<MaybeAdding::NotAdding>(id, nogc);
     330       55999 :         return entry.shape();
     331             :     }
     332             : 
     333      144289 :     return start->searchLinear(id);
     334             : }
     335             : 
     336             : /* static */ MOZ_ALWAYS_INLINE Shape*
     337      117419 : NativeObject::addProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
     338             :                           GetterOp getter, SetterOp setter, uint32_t slot, unsigned attrs,
     339             :                           unsigned flags, bool allowDictionary)
     340             : {
     341      117419 :     MOZ_ASSERT(!JSID_IS_VOID(id));
     342      117420 :     MOZ_ASSERT(getter != JS_PropertyStub);
     343      117420 :     MOZ_ASSERT(setter != JS_StrictPropertyStub);
     344      117420 :     MOZ_ASSERT(obj->uninlinedNonProxyIsExtensible());
     345      117420 :     MOZ_ASSERT(!obj->containsPure(id));
     346             : 
     347      234840 :     AutoKeepShapeTables keep(cx);
     348      117422 :     ShapeTable::Entry* entry = nullptr;
     349      117422 :     if (obj->inDictionaryMode()) {
     350        2659 :         ShapeTable* table = obj->lastProperty()->ensureTableForDictionary(cx, keep);
     351        2659 :         if (!table)
     352           0 :             return nullptr;
     353        2659 :         entry = &table->search<MaybeAdding::Adding>(id, keep);
     354             :     }
     355             : 
     356      117422 :     return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, entry,
     357      117422 :                                allowDictionary, keep);
     358             : }
     359             : 
     360             : } /* namespace js */
     361             : 
     362             : #endif /* vm_Shape_inl_h */

Generated by: LCOV version 1.13