diff options
Diffstat (limited to 'js/src/vm/PropMap-inl.h')
-rw-r--r-- | js/src/vm/PropMap-inl.h | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/js/src/vm/PropMap-inl.h b/js/src/vm/PropMap-inl.h new file mode 100644 index 0000000000..f45e1abb4e --- /dev/null +++ b/js/src/vm/PropMap-inl.h @@ -0,0 +1,251 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef vm_PropMap_inl_h +#define vm_PropMap_inl_h + +#include "vm/PropMap.h" + +#include "gc/Cell.h" +#include "gc/Zone.h" +#include "vm/JSContext.h" + +#include "gc/GCContext-inl.h" + +namespace js { + +inline AutoKeepPropMapTables::AutoKeepPropMapTables(JSContext* cx) + : cx_(cx), prev_(cx->zone()->keepPropMapTables()) { + cx->zone()->setKeepPropMapTables(true); +} + +inline AutoKeepPropMapTables::~AutoKeepPropMapTables() { + cx_->zone()->setKeepPropMapTables(prev_); +} + +// static +MOZ_ALWAYS_INLINE PropMap* PropMap::lookupLinear(uint32_t mapLength, + PropertyKey key, + uint32_t* index) { + MOZ_ASSERT(mapLength > 0); + MOZ_ASSERT(mapLength <= Capacity); + + // This function is very hot, so we use a macro to manually unroll the lookups + // below. Some compilers are able to unroll the equivalent loops, but they're + // not very consistent about this. The code below results in reasonable code + // with all compilers we tested. + + static_assert(PropMap::Capacity == 8, + "Code below needs to change when capacity changes"); + +#define LOOKUP_KEY(idx) \ + if (mapLength > idx && getKey(idx) == key) { \ + *index = idx; \ + return this; \ + } + LOOKUP_KEY(0); + LOOKUP_KEY(1); + LOOKUP_KEY(2); + LOOKUP_KEY(3); + LOOKUP_KEY(4); + LOOKUP_KEY(5); + LOOKUP_KEY(6); + LOOKUP_KEY(7); +#undef LOOKUP_KEY + + PropMap* map = this; + while (map->hasPrevious()) { + map = map->asLinked()->previous(); +#define LOOKUP_KEY(idx) \ + if (map->getKey(idx) == key) { \ + *index = idx; \ + return map; \ + } + LOOKUP_KEY(0); + LOOKUP_KEY(1); + LOOKUP_KEY(2); + LOOKUP_KEY(3); + LOOKUP_KEY(4); + LOOKUP_KEY(5); + LOOKUP_KEY(6); + LOOKUP_KEY(7); +#undef LOOKUP_INDEX + } + + return nullptr; +} + +MOZ_ALWAYS_INLINE PropMap* PropMapTable::lookup(PropMap* map, + uint32_t mapLength, + PropertyKey key, + uint32_t* index) { + JS::AutoCheckCannotGC nogc; + MOZ_ASSERT(map->asLinked()->maybeTable(nogc) == this); + + PropMapAndIndex entry; + if (lookupInCache(key, &entry)) { + if (entry.isNone()) { + return nullptr; + } + } else { + auto p = lookupRaw(key); + addToCache(key, p); + if (!p) { + return nullptr; + } + entry = *p; + } + + // For the last map, only properties in [0, mapLength) are part of the object. + if (entry.map() == map && entry.index() >= mapLength) { + return nullptr; + } + + *index = entry.index(); + return entry.map(); +} + +// static +MOZ_ALWAYS_INLINE PropMap* PropMap::lookupPure(uint32_t mapLength, + PropertyKey key, + uint32_t* index) { + if (canHaveTable()) { + JS::AutoCheckCannotGC nogc; + if (PropMapTable* table = asLinked()->maybeTable(nogc)) { + return table->lookup(this, mapLength, key, index); + } + } + + return lookupLinear(mapLength, key, index); +} + +// static +MOZ_ALWAYS_INLINE PropMap* PropMap::lookup(JSContext* cx, uint32_t mapLength, + PropertyKey key, uint32_t* index) { + if (canHaveTable()) { + JS::AutoCheckCannotGC nogc; + if (PropMapTable* table = asLinked()->ensureTable(cx, nogc); + MOZ_LIKELY(table)) { + return table->lookup(this, mapLength, key, index); + } + // OOM. Do a linear lookup. + cx->recoverFromOutOfMemory(); + } + + return lookupLinear(mapLength, key, index); +} + +// static +inline void SharedPropMap::getPrevious(MutableHandle<SharedPropMap*> map, + uint32_t* mapLength) { + // Update the map/mapLength pointers to "remove" the last property. In most + // cases we can simply decrement *mapLength, but if *mapLength is 1 we have to + // either start at the previous map or set map/mapLength to nullptr/zero + // (if there is just one property). + + MOZ_ASSERT(map); + MOZ_ASSERT(*mapLength > 0); + + if (*mapLength > 1) { + *mapLength -= 1; + return; + } + + if (map->hasPrevious()) { + map.set(map->asNormal()->previous()); + *mapLength = PropMap::Capacity; + return; + } + + map.set(nullptr); + *mapLength = 0; +} + +// static +inline bool PropMap::lookupForRemove(JSContext* cx, PropMap* map, + uint32_t mapLength, PropertyKey key, + const AutoKeepPropMapTables& keep, + PropMap** propMap, uint32_t* propIndex, + PropMapTable** table, + PropMapTable::Ptr* ptr) { + if (map->isDictionary()) { + *table = map->asLinked()->ensureTable(cx, keep); + if (!*table) { + return false; + } + *ptr = (*table)->lookupRaw(key); + *propMap = *ptr ? (*ptr)->map() : nullptr; + *propIndex = *ptr ? (*ptr)->index() : 0; + return true; + } + + *table = nullptr; + *propMap = map->lookup(cx, mapLength, key, propIndex); + return true; +} + +MOZ_ALWAYS_INLINE bool SharedPropMap::shouldConvertToDictionaryForAdd() const { + if (MOZ_LIKELY(numPreviousMaps() < NumPrevMapsConsiderDictionary)) { + return false; + } + if (numPreviousMaps() >= NumPrevMapsAlwaysDictionary) { + return true; + } + + // More heuristics: if one of the last two maps has had a dictionary + // conversion before, or is branchy (indicated by parent != previous), convert + // to dictionary. + const SharedPropMap* curMap = this; + for (size_t i = 0; i < 2; i++) { + if (curMap->hadDictionaryConversion()) { + return true; + } + if (curMap->treeDataRef().parent.map() != curMap->asNormal()->previous()) { + return true; + } + curMap = curMap->asNormal()->previous(); + } + return false; +} + +inline void SharedPropMap::sweep(JS::GCContext* gcx) { + // We detach the child from the parent if the parent is reachable. + // + // This test depends on PropMap arenas not being freed until after we finish + // incrementally sweeping them. If that were not the case the parent pointer + // could point to a marked cell that had been deallocated and then + // reallocated, since allocating a cell in a zone that is being marked will + // set the mark bit for that cell. + + MOZ_ASSERT(zone()->isGCSweeping()); + MOZ_ASSERT_IF(hasPrevious(), asLinked()->previous()->zone() == zone()); + + SharedPropMapAndIndex parent = treeDataRef().parent; + if (!parent.isNone() && TenuredThingIsMarkedAny(parent.map())) { + parent.map()->removeChild(gcx, this); + } +} + +inline void SharedPropMap::finalize(JS::GCContext* gcx) { + if (canHaveTable() && asLinked()->hasTable()) { + asLinked()->purgeTable(gcx); + } + if (hasChildrenSet()) { + SharedChildrenPtr& childrenRef = treeDataRef().children; + gcx->delete_(this, childrenRef.toChildrenSet(), MemoryUse::PropMapChildren); + childrenRef.setNone(); + } +} + +inline void DictionaryPropMap::finalize(JS::GCContext* gcx) { + if (asLinked()->hasTable()) { + asLinked()->purgeTable(gcx); + } +} + +} // namespace js + +#endif /* vm_PropMap_inl_h */ |