// In future we may want to cache this transition. Structure* Structure::sealTransition(VM& vm, Structure* structure) { Structure* transition = preventExtensionsTransition(vm, structure); if (transition->propertyTable()) { PropertyTable::iterator end = transition->propertyTable()->end(); for (PropertyTable::iterator iter = transition->propertyTable()->begin(); iter != end; ++iter) iter->attributes |= DontDelete; } transition->checkOffsetConsistency(); return transition; }
Structure* Structure::nonPropertyTransition(JSGlobalData& globalData, Structure* structure, NonPropertyTransition transitionKind) { unsigned attributes = toAttributes(transitionKind); IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { if (globalObject->isOriginalArrayStructure(structure)) { Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); if (result->indexingTypeIncludingHistory() == indexingType) { structure->notifyTransitionFromThisStructure(); return result; } } } if (Structure* existingTransition = structure->m_transitionTable.get(0, attributes)) { ASSERT(existingTransition->m_attributesInPrevious == attributes); ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); return existingTransition; } Structure* transition = create(globalData, structure); transition->setPreviousID(globalData, transition, structure); transition->m_attributesInPrevious = attributes; transition->m_indexingType = indexingType; transition->propertyTable().set(globalData, transition, structure->takePropertyTableOrCloneIfPinned(globalData, transition)); transition->m_offset = structure->m_offset; checkOffset(transition->m_offset, transition->inlineCapacity()); structure->m_transitionTable.add(globalData, transition); transition->checkOffsetConsistency(); return transition; }
// In future we may want to cache this transition. Structure* Structure::freezeTransition(JSGlobalData& globalData, Structure* structure) { Structure* transition = preventExtensionsTransition(globalData, structure); if (transition->propertyTable()) { PropertyTable::iterator iter = transition->propertyTable()->begin(); PropertyTable::iterator end = transition->propertyTable()->end(); if (iter != end) transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true; for (; iter != end; ++iter) iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); } transition->checkOffsetConsistency(); return transition; }
// In future we may want to cache this transition. Structure* Structure::freezeTransition(VM& vm, Structure* structure) { Structure* transition = preventExtensionsTransition(vm, structure); if (transition->propertyTable()) { PropertyTable::iterator iter = transition->propertyTable()->begin(); PropertyTable::iterator end = transition->propertyTable()->end(); if (iter != end) transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); for (; iter != end; ++iter) iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly); } ASSERT(transition->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); ASSERT(transition->hasGetterSetterProperties() || !transition->classInfo()->hasStaticSetterOrReadonlyProperties()); transition->checkOffsetConsistency(); return transition; }
void Structure::dumpStatistics() { #if DUMP_STRUCTURE_ID_STATISTICS unsigned numberLeaf = 0; unsigned numberUsingSingleSlot = 0; unsigned numberSingletons = 0; unsigned numberWithPropertyMaps = 0; unsigned totalPropertyMapsSize = 0; HashSet<Structure*>::const_iterator end = liveStructureSet.end(); for (HashSet<Structure*>::const_iterator it = liveStructureSet.begin(); it != end; ++it) { Structure* structure = *it; switch (structure->m_transitionTable.size()) { case 0: ++numberLeaf; if (!structure->previousID()) ++numberSingletons; break; case 1: ++numberUsingSingleSlot; break; } if (structure->propertyTable()) { ++numberWithPropertyMaps; totalPropertyMapsSize += structure->propertyTable()->sizeInMemory(); } } dataLogF("Number of live Structures: %d\n", liveStructureSet.size()); dataLogF("Number of Structures using the single item optimization for transition map: %d\n", numberUsingSingleSlot); dataLogF("Number of Structures that are leaf nodes: %d\n", numberLeaf); dataLogF("Number of Structures that singletons: %d\n", numberSingletons); dataLogF("Number of Structures with PropertyMaps: %d\n", numberWithPropertyMaps); dataLogF("Size of a single Structures: %d\n", static_cast<unsigned>(sizeof(Structure))); dataLogF("Size of sum of all property maps: %d\n", totalPropertyMapsSize); dataLogF("Size of average of all property maps: %f\n", static_cast<double>(totalPropertyMapsSize) / static_cast<double>(liveStructureSet.size())); #else dataLogF("Dumping Structure statistics is not enabled.\n"); #endif }
Structure* Structure::changePrototypeTransition(JSGlobalData& globalData, Structure* structure, JSValue prototype) { Structure* transition = create(globalData, structure); transition->m_prototype.set(globalData, transition, prototype); structure->materializePropertyMapIfNecessary(globalData); transition->propertyTable().set(globalData, transition, structure->copyPropertyTableForPinning(globalData, transition)); transition->m_offset = structure->m_offset; transition->pin(); transition->checkOffsetConsistency(); return transition; }
Structure* Structure::changePrototypeTransition(VM& vm, Structure* structure, JSValue prototype) { Structure* transition = create(vm, structure); transition->m_prototype.set(vm, transition, prototype); DeferGC deferGC(vm.heap); structure->materializePropertyMapIfNecessary(vm, deferGC); transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); transition->m_offset = structure->m_offset; transition->pin(); transition->checkOffsetConsistency(); return transition; }
// In future we may want to cache this transition. Structure* Structure::preventExtensionsTransition(JSGlobalData& globalData, Structure* structure) { Structure* transition = create(globalData, structure); // Don't set m_offset, as one can not transition to this. structure->materializePropertyMapIfNecessary(globalData); transition->propertyTable().set(globalData, transition, structure->copyPropertyTableForPinning(globalData, transition)); transition->m_offset = structure->m_offset; transition->m_preventExtensions = true; transition->pin(); transition->checkOffsetConsistency(); return transition; }
Structure* Structure::toDictionaryTransition(JSGlobalData& globalData, Structure* structure, DictionaryKind kind) { ASSERT(!structure->isUncacheableDictionary()); Structure* transition = create(globalData, structure); structure->materializePropertyMapIfNecessary(globalData); transition->propertyTable().set(globalData, transition, structure->copyPropertyTableForPinning(globalData, transition)); transition->m_offset = structure->m_offset; transition->m_dictionaryKind = kind; transition->pin(); transition->checkOffsetConsistency(); return transition; }
void Structure::materializePropertyMap(JSGlobalData& globalData) { ASSERT(structure()->classInfo() == &s_info); ASSERT(!propertyTable()); Vector<Structure*, 8> structures; structures.append(this); Structure* structure = this; // Search for the last Structure with a property table. while ((structure = structure->previousID())) { if (structure->m_isPinnedPropertyTable) { ASSERT(structure->propertyTable()); ASSERT(!structure->previousID()); propertyTable().set(globalData, this, structure->propertyTable()->copy(globalData, 0, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity))); break; } structures.append(structure); } if (!propertyTable()) createPropertyMap(globalData, numberOfSlotsForLastOffset(m_offset, m_inlineCapacity)); for (ptrdiff_t i = structures.size() - 1; i >= 0; --i) { structure = structures[i]; if (!structure->m_nameInPrevious) continue; PropertyMapEntry entry(globalData, this, structure->m_nameInPrevious.get(), structure->m_offset, structure->m_attributesInPrevious, structure->m_specificValueInPrevious.get()); propertyTable()->add(entry, m_offset, PropertyTable::PropertyOffsetMustNotChange); } checkOffsetConsistency(); }
Structure* Structure::toDictionaryTransition(VM& vm, Structure* structure, DictionaryKind kind) { ASSERT(!structure->isUncacheableDictionary()); Structure* transition = create(vm, structure); DeferGC deferGC(vm.heap); structure->materializePropertyMapIfNecessary(vm, deferGC); transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); transition->m_offset = structure->m_offset; transition->setDictionaryKind(kind); transition->pin(); transition->checkOffsetConsistency(); return transition; }
// In future we may want to cache this transition. Structure* Structure::preventExtensionsTransition(VM& vm, Structure* structure) { Structure* transition = create(vm, structure); // Don't set m_offset, as one can not transition to this. DeferGC deferGC(vm.heap); structure->materializePropertyMapIfNecessary(vm, deferGC); transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); transition->m_offset = structure->m_offset; transition->setPreventExtensions(true); transition->pin(); transition->checkOffsetConsistency(); return transition; }
Structure* Structure::addPropertyTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, unsigned attributes, JSCell* specificValue, PropertyOffset& offset) { // If we have a specific function, we may have got to this point if there is // already a transition with the correct property name and attributes, but // specialized to a different function. In this case we just want to give up // and despecialize the transition. // In this case we clear the value of specificFunction which will result // in us adding a non-specific transition, and any subsequent lookup in // Structure::addPropertyTransitionToExistingStructure will just use that. if (specificValue && structure->m_transitionTable.contains(propertyName.uid(), attributes)) specificValue = 0; ASSERT(!structure->isDictionary()); ASSERT(structure->isObject()); ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, specificValue, offset)); if (structure->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) specificValue = 0; if (structure->transitionCount() > s_maxTransitionLength) { Structure* transition = toCacheableDictionaryTransition(globalData, structure); ASSERT(structure != transition); offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue); return transition; } Structure* transition = create(globalData, structure); transition->m_cachedPrototypeChain.setMayBeNull(globalData, transition, structure->m_cachedPrototypeChain.get()); transition->setPreviousID(globalData, transition, structure); transition->m_nameInPrevious = propertyName.uid(); transition->m_attributesInPrevious = attributes; transition->m_specificValueInPrevious.setMayBeNull(globalData, transition, specificValue); transition->propertyTable().set(globalData, transition, structure->takePropertyTableOrCloneIfPinned(globalData, transition)); transition->m_offset = structure->m_offset; offset = transition->putSpecificValue(globalData, propertyName, attributes, specificValue); checkOffset(transition->m_offset, transition->inlineCapacity()); structure->m_transitionTable.add(globalData, transition); transition->checkOffsetConsistency(); structure->checkOffsetConsistency(); return transition; }
Structure* Structure::attributeChangeTransition(JSGlobalData& globalData, Structure* structure, PropertyName propertyName, unsigned attributes) { if (!structure->isUncacheableDictionary()) { Structure* transition = create(globalData, structure); structure->materializePropertyMapIfNecessary(globalData); transition->propertyTable().set(globalData, transition, structure->copyPropertyTableForPinning(globalData, transition)); transition->m_offset = structure->m_offset; transition->pin(); structure = transition; } ASSERT(structure->propertyTable()); PropertyMapEntry* entry = structure->propertyTable()->find(propertyName.uid()).first; ASSERT(entry); entry->attributes = attributes; structure->checkOffsetConsistency(); return structure; }
Structure* Structure::attributeChangeTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes) { DeferGC deferGC(vm.heap); if (!structure->isUncacheableDictionary()) { Structure* transition = create(vm, structure); structure->materializePropertyMapIfNecessary(vm, deferGC); transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); transition->m_offset = structure->m_offset; transition->pin(); structure = transition; } ASSERT(structure->propertyTable()); PropertyMapEntry* entry = structure->propertyTable()->get(propertyName.uid()); ASSERT(entry); entry->attributes = attributes; structure->checkOffsetConsistency(); return structure; }
Structure* Structure::despecifyFunctionTransition(JSGlobalData& globalData, Structure* structure, PropertyName replaceFunction) { ASSERT(structure->m_specificFunctionThrashCount < maxSpecificFunctionThrashCount); Structure* transition = create(globalData, structure); ++transition->m_specificFunctionThrashCount; structure->materializePropertyMapIfNecessary(globalData); transition->propertyTable().set(globalData, transition, structure->copyPropertyTableForPinning(globalData, transition)); transition->m_offset = structure->m_offset; transition->pin(); if (transition->m_specificFunctionThrashCount == maxSpecificFunctionThrashCount) transition->despecifyAllFunctions(globalData); else { bool removed = transition->despecifyFunction(globalData, replaceFunction); ASSERT_UNUSED(removed, removed); } transition->checkOffsetConsistency(); return transition; }
Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind) { unsigned attributes = toAttributes(transitionKind); IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { if (globalObject->isOriginalArrayStructure(structure)) { Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); if (result->indexingTypeIncludingHistory() == indexingType) { structure->didTransitionFromThisStructure(); return result; } } } Structure* existingTransition; if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) { ASSERT(existingTransition->attributesInPrevious() == attributes); ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); return existingTransition; } Structure* transition = create(vm, structure); transition->setAttributesInPrevious(attributes); transition->m_blob.setIndexingType(indexingType); transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); transition->m_offset = structure->m_offset; checkOffset(transition->m_offset, transition->inlineCapacity()); if (structure->isDictionary()) transition->pin(); else { ConcurrentJITLocker locker(structure->m_lock); structure->m_transitionTable.add(vm, transition); } transition->checkOffsetConsistency(); return transition; }
Structure* Structure::addNewPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, unsigned attributes, PropertyOffset& offset, PutPropertySlot::Context context, DeferredStructureTransitionWatchpointFire* deferred) { ASSERT(!structure->isDictionary()); ASSERT(structure->isObject()); ASSERT(!Structure::addPropertyTransitionToExistingStructure(structure, propertyName, attributes, offset)); int maxTransitionLength; if (context == PutPropertySlot::PutById) maxTransitionLength = s_maxTransitionLengthForNonEvalPutById; else maxTransitionLength = s_maxTransitionLength; if (structure->transitionCount() > maxTransitionLength) { Structure* transition = toCacheableDictionaryTransition(vm, structure, deferred); ASSERT(structure != transition); offset = transition->add(vm, propertyName, attributes); return transition; } Structure* transition = create(vm, structure, deferred); transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get()); transition->m_nameInPrevious = propertyName.uid(); transition->setAttributesInPrevious(attributes); transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); transition->m_offset = structure->m_offset; transition->m_inferredTypeTable.setMayBeNull(vm, transition, structure->m_inferredTypeTable.get()); offset = transition->add(vm, propertyName, attributes); checkOffset(transition->m_offset, transition->inlineCapacity()); { ConcurrentJITLocker locker(structure->m_lock); structure->m_transitionTable.add(vm, transition); } transition->checkOffsetConsistency(); structure->checkOffsetConsistency(); return transition; }
Structure* Structure::nonPropertyTransition(VM& vm, Structure* structure, NonPropertyTransition transitionKind) { unsigned attributes = toAttributes(transitionKind); IndexingType indexingType = newIndexingType(structure->indexingTypeIncludingHistory(), transitionKind); if (changesIndexingType(transitionKind)) { if (JSGlobalObject* globalObject = structure->m_globalObject.get()) { if (globalObject->isOriginalArrayStructure(structure)) { Structure* result = globalObject->originalArrayStructureForIndexingType(indexingType); if (result->indexingTypeIncludingHistory() == indexingType) { structure->didTransitionFromThisStructure(); return result; } } } } Structure* existingTransition; if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) { ASSERT(existingTransition->attributesInPrevious() == attributes); ASSERT(existingTransition->indexingTypeIncludingHistory() == indexingType); return existingTransition; } DeferGC deferGC(vm.heap); Structure* transition = create(vm, structure); transition->setAttributesInPrevious(attributes); transition->m_blob.setIndexingType(indexingType); if (preventsExtensions(transitionKind)) transition->setDidPreventExtensions(true); if (setsDontDeleteOnAllProperties(transitionKind) || setsReadOnlyOnNonAccessorProperties(transitionKind)) { // We pin the property table on transitions that do wholesale editing of the property // table, since our logic for walking the property transition chain to rematerialize the // table doesn't know how to take into account such wholesale edits. structure->materializePropertyMapIfNecessary(vm, deferGC); transition->propertyTable().set(vm, transition, structure->copyPropertyTableForPinning(vm)); transition->m_offset = structure->m_offset; transition->pinForCaching(); if (transition->propertyTable()) { for (auto& entry : *transition->propertyTable().get()) { if (setsDontDeleteOnAllProperties(transitionKind)) entry.attributes |= DontDelete; if (setsReadOnlyOnNonAccessorProperties(transitionKind) && !(entry.attributes & Accessor)) entry.attributes |= ReadOnly; } } } else { transition->propertyTable().set(vm, transition, structure->takePropertyTableOrCloneIfPinned(vm)); transition->m_offset = structure->m_offset; checkOffset(transition->m_offset, transition->inlineCapacity()); } if (setsReadOnlyOnNonAccessorProperties(transitionKind) && transition->propertyTable() && !transition->propertyTable()->isEmpty()) transition->setHasReadOnlyOrGetterSetterPropertiesExcludingProto(true); if (structure->isDictionary()) transition->pin(); else { ConcurrentJITLocker locker(structure->m_lock); structure->m_transitionTable.add(vm, transition); } transition->checkOffsetConsistency(); return transition; }