/* * Copyright (C) 2013-2015 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StructureInlines_h #define StructureInlines_h #include "JSArrayBufferView.h" #include "JSCJSValueInlines.h" #include "JSGlobalObject.h" #include "PropertyMapHashTable.h" #include "Structure.h" #include "StructureChain.h" namespace JSC { inline Structure* Structure::create(VM& vm, JSGlobalObject* globalObject, JSValue prototype, const TypeInfo& typeInfo, const ClassInfo* classInfo, IndexingType indexingType, unsigned inlineCapacity) { ASSERT(vm.structureStructure); ASSERT(classInfo); Structure* structure = new (NotNull, allocateCell(vm.heap)) Structure(vm, globalObject, prototype, typeInfo, classInfo, indexingType, inlineCapacity); structure->finishCreation(vm); return structure; } inline Structure* Structure::createStructure(VM& vm) { ASSERT(!vm.structureStructure); Structure* structure = new (NotNull, allocateCell(vm.heap)) Structure(vm); structure->finishCreation(vm, CreatingEarlyCell); return structure; } inline Structure* Structure::create(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred) { ASSERT(vm.structureStructure); Structure* newStructure = new (NotNull, allocateCell(vm.heap)) Structure(vm, structure, deferred); newStructure->finishCreation(vm); return newStructure; } inline JSObject* Structure::storedPrototypeObject() const { JSValue value = m_prototype.get(); if (value.isNull()) return nullptr; return asObject(value); } inline Structure* Structure::storedPrototypeStructure() const { JSObject* object = storedPrototypeObject(); if (!object) return nullptr; return object->structure(); } ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName) { unsigned attributes; bool hasInferredType; return get(vm, propertyName, attributes, hasInferredType); } ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes) { bool hasInferredType; return get(vm, propertyName, attributes, hasInferredType); } ALWAYS_INLINE PropertyOffset Structure::get(VM& vm, PropertyName propertyName, unsigned& attributes, bool& hasInferredType) { ASSERT(!isCompilationThread()); ASSERT(structure()->classInfo() == info()); PropertyTable* propertyTable; materializePropertyMapIfNecessary(vm, propertyTable); if (!propertyTable) return invalidOffset; PropertyMapEntry* entry = propertyTable->get(propertyName.uid()); if (!entry) return invalidOffset; attributes = entry->attributes; hasInferredType = entry->hasInferredType; return entry->offset; } template void Structure::forEachPropertyConcurrently(const Functor& functor) { Vector structures; Structure* structure; PropertyTable* table; findStructuresAndMapForMaterialization(structures, structure, table); if (table) { for (auto& entry : *table) { if (!functor(entry)) { structure->m_lock.unlock(); return; } } structure->m_lock.unlock(); } for (unsigned i = structures.size(); i--;) { structure = structures[i]; if (!structure->m_nameInPrevious) continue; if (!functor(PropertyMapEntry(structure->m_nameInPrevious.get(), structure->m_offset, structure->attributesInPrevious()))) return; } } inline PropertyOffset Structure::getConcurrently(UniquedStringImpl* uid) { unsigned attributesIgnored; return getConcurrently(uid, attributesIgnored); } inline bool Structure::hasIndexingHeader(const JSCell* cell) const { if (hasIndexedProperties(indexingType())) return true; if (!isTypedView(m_classInfo->typedArrayStorageType)) return false; return jsCast(cell)->mode() == WastefulTypedArray; } inline bool Structure::masqueradesAsUndefined(JSGlobalObject* lexicalGlobalObject) { return typeInfo().masqueradesAsUndefined() && globalObject() == lexicalGlobalObject; } inline bool Structure::transitivelyTransitionedFrom(Structure* structureToFind) { for (Structure* current = this; current; current = current->previousID()) { if (current == structureToFind) return true; } return false; } inline JSValue Structure::prototypeForLookup(JSGlobalObject* globalObject) const { if (isObject()) return m_prototype.get(); if (typeInfo().type() == SymbolType) return globalObject->symbolPrototype(); ASSERT(typeInfo().type() == StringType); return globalObject->stringPrototype(); } inline JSValue Structure::prototypeForLookup(ExecState* exec) const { return prototypeForLookup(exec->lexicalGlobalObject()); } inline StructureChain* Structure::prototypeChain(VM& vm, JSGlobalObject* globalObject) const { // We cache our prototype chain so our clients can share it. if (!isValid(globalObject, m_cachedPrototypeChain.get())) { JSValue prototype = prototypeForLookup(globalObject); m_cachedPrototypeChain.set(vm, this, StructureChain::create(vm, prototype.isNull() ? 0 : asObject(prototype)->structure())); } return m_cachedPrototypeChain.get(); } inline StructureChain* Structure::prototypeChain(ExecState* exec) const { return prototypeChain(exec->vm(), exec->lexicalGlobalObject()); } inline bool Structure::isValid(JSGlobalObject* globalObject, StructureChain* cachedPrototypeChain) const { if (!cachedPrototypeChain) return false; JSValue prototype = prototypeForLookup(globalObject); WriteBarrier* cachedStructure = cachedPrototypeChain->head(); while (*cachedStructure && !prototype.isNull()) { if (asObject(prototype)->structure() != cachedStructure->get()) return false; ++cachedStructure; prototype = asObject(prototype)->getPrototypeDirect(); } return prototype.isNull() && !*cachedStructure; } inline bool Structure::isValid(ExecState* exec, StructureChain* cachedPrototypeChain) const { return isValid(exec->lexicalGlobalObject(), cachedPrototypeChain); } inline bool Structure::putWillGrowOutOfLineStorage() { checkOffsetConsistency(); ASSERT(outOfLineCapacity() >= outOfLineSize()); if (!propertyTable()) { unsigned currentSize = numberOfOutOfLineSlotsForLastOffset(m_offset); ASSERT(outOfLineCapacity() >= currentSize); return currentSize == outOfLineCapacity(); } ASSERT(totalStorageCapacity() >= propertyTable()->propertyStorageSize()); if (propertyTable()->hasDeletedOffset()) return false; ASSERT(totalStorageCapacity() >= propertyTable()->size()); return propertyTable()->size() == totalStorageCapacity(); } ALWAYS_INLINE WriteBarrier& Structure::propertyTable() { ASSERT(!globalObject() || (!globalObject()->vm().heap.isCollecting() || globalObject()->vm().heap.isHeapSnapshotting())); return m_propertyTableUnsafe; } inline void Structure::didReplaceProperty(PropertyOffset offset) { if (LIKELY(!hasRareData())) return; StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get(); if (LIKELY(!map)) return; WatchpointSet* set = map->get(offset); if (LIKELY(!set)) return; set->fireAll("Property did get replaced"); } inline WatchpointSet* Structure::propertyReplacementWatchpointSet(PropertyOffset offset) { ConcurrentJITLocker locker(m_lock); if (!hasRareData()) return nullptr; WTF::loadLoadFence(); StructureRareData::PropertyWatchpointMap* map = rareData()->m_replacementWatchpointSets.get(); if (!map) return nullptr; return map->get(offset); } ALWAYS_INLINE bool Structure::checkOffsetConsistency() const { PropertyTable* propertyTable = m_propertyTableUnsafe.get(); if (!propertyTable) { ASSERT(!isPinnedPropertyTable()); return true; } // We cannot reliably assert things about the property table in the concurrent // compilation thread. It is possible for the table to be stolen and then have // things added to it, which leads to the offsets being all messed up. We could // get around this by grabbing a lock here, but I think that would be overkill. if (isCompilationThread()) return true; RELEASE_ASSERT(numberOfSlotsForLastOffset(m_offset, m_inlineCapacity) == propertyTable->propertyStorageSize()); unsigned totalSize = propertyTable->propertyStorageSize(); RELEASE_ASSERT((totalSize < inlineCapacity() ? 0 : totalSize - inlineCapacity()) == numberOfOutOfLineSlotsForLastOffset(m_offset)); return true; } inline size_t nextOutOfLineStorageCapacity(size_t currentCapacity) { if (!currentCapacity) return initialOutOfLineCapacity; return currentCapacity * outOfLineGrowthFactor; } inline size_t Structure::suggestedNewOutOfLineStorageCapacity() { return nextOutOfLineStorageCapacity(outOfLineCapacity()); } inline void Structure::setObjectToStringValue(ExecState* exec, VM& vm, JSString* value, PropertySlot toStringTagSymbolSlot) { if (!hasRareData()) allocateRareData(vm); rareData()->setObjectToStringValue(exec, vm, this, value, toStringTagSymbolSlot); } } // namespace JSC #endif // StructureInlines_h