diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ApplyChangesFromOperation.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ApplyChangesFromOperation.kt index 765299077fc1..900b222e3686 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ApplyChangesFromOperation.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ApplyChangesFromOperation.kt @@ -5,6 +5,7 @@ import com.google.common.collect.HashBiMap import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.diagnostic.trace import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId import com.intellij.platform.workspace.storage.impl.exceptions.ApplyChangesFromException import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi import java.util.* @@ -195,7 +196,7 @@ internal class ApplyChangesFromOperation(val target: MutableEntityStorageImpl, v target.indexes.updateIndices(sourceEntityId.id, newTargetEntityData, diff) val newEntityId = newTargetEntityData.createEntityId() - val oldSymbolicId = target.entityDataById(newEntityId)?.symbolicId() + val oldSymbolicId = (target.entityDataById(newEntityId)?.createEntity(target) as? WorkspaceEntityWithSymbolicId)?.symbolicId /// Replace entity data. id should not be changed target.entitiesByType.replaceById(newTargetEntityData, sourceEntityId.id.clazz) @@ -414,7 +415,7 @@ internal class ApplyChangesFromOperation(val target: MutableEntityStorageImpl, v } private fun checkSymbolicId(entityData: WorkspaceEntityData, newEntityId: EntityId?) { - val newSymbolicId = entityData.symbolicId() + val newSymbolicId = (entityData.createEntity(diff) as? WorkspaceEntityWithSymbolicId)?.symbolicId if (newSymbolicId != null) { val existingIds = target.indexes.symbolicIdIndex.getIdsByEntry(newSymbolicId) if (existingIds != null) { diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ConsistencyChecker.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ConsistencyChecker.kt index 35f84fa3663b..14ab7216a499 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ConsistencyChecker.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ConsistencyChecker.kt @@ -16,7 +16,7 @@ public fun EntityStorage.assertConsistency() { internal fun AbstractEntityStorage.assertConsistency() { AbstractEntityStorage.LOG.trace { "Checking consistency of $this" } - entitiesByType.assertConsistency() + entitiesByType.assertConsistency(this) // Rules: // 1) Refs should not have links without a corresponding entity // 1.1) For abstract containers: EntityId has the class of ConnectionId diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Entities.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Entities.kt index 60fb08922e4e..561f19e7519b 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Entities.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Entities.kt @@ -670,9 +670,4 @@ public abstract class WorkspaceEntityData : Cloneable, Seri public abstract class WithCalculableSymbolicId : WorkspaceEntityData() { public abstract fun symbolicId(): SymbolicEntityId<*> } -} - -internal fun WorkspaceEntityData<*>.symbolicId(): SymbolicEntityId<*>? = when (this) { - is WorkspaceEntityData.WithCalculableSymbolicId -> this.symbolicId() - else -> null -} +} \ No newline at end of file diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/EntitiesBarrel.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/EntitiesBarrel.kt index a141a922e97b..8277caeae4fe 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/EntitiesBarrel.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/EntitiesBarrel.kt @@ -2,9 +2,12 @@ package com.intellij.platform.workspace.storage.impl import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.platform.workspace.storage.EntityStorage import com.intellij.platform.workspace.storage.SymbolicEntityId import com.intellij.platform.workspace.storage.WorkspaceEntity import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentation +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi internal open class ImmutableEntitiesBarrel internal constructor( override val entityFamilies: List?> @@ -117,7 +120,8 @@ internal sealed class EntitiesBarrel { fun size() = entityFamilies.size - fun assertConsistency() { + @OptIn(EntityStorageInstrumentationApi::class) + fun assertConsistency(storage: EntityStorageInstrumentation) { val symbolicIds = HashSet>() entityFamilies.forEachIndexed { i, family -> if (family == null) return@forEachIndexed @@ -135,7 +139,7 @@ internal sealed class EntitiesBarrel { // Assert unique of persistent id if (hasSymbolicId) { - val symbolicId = entityData.symbolicId() + val symbolicId = (entityData.createEntity(storage) as? WorkspaceEntityWithSymbolicId)?.symbolicId assert(symbolicId != null) { "Symbolic id expected for $clazz" } assert(symbolicId !in symbolicIds) { "Duplicated symbolic ids: $symbolicId" } symbolicIds.add(symbolicId!!) diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ImmutableEntityStorageImpl.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ImmutableEntityStorageImpl.kt index 64beb5d8deb9..4e6b526c652c 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ImmutableEntityStorageImpl.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/ImmutableEntityStorageImpl.kt @@ -270,9 +270,11 @@ internal class MutableEntityStorageImpl( lockWrite() val newEntityData = entity.getEntityData() + val immutableEntity = newEntityData.createEntity(this) + val symbolicId = (immutableEntity as? WorkspaceEntityWithSymbolicId)?.symbolicId // Check for persistent id uniqueness - assertUniqueSymbolicId(newEntityData) + assertUniqueSymbolicId(newEntityData, symbolicId) entitiesByType.add(newEntityData, entity.getEntityClass().toClassId()) @@ -280,24 +282,24 @@ internal class MutableEntityStorageImpl( changeLog.addAddEvent(newEntityData.createEntityId(), newEntityData) // Update indexes - indexes.entityAdded(newEntityData) + indexes.entityAdded(newEntityData, symbolicId) } finally { unlockWrite() } } - private fun assertUniqueSymbolicId(pEntityData: WorkspaceEntityData) { - pEntityData.symbolicId()?.let { symbolicId -> - val ids = indexes.symbolicIdIndex.getIdsByEntry(symbolicId) - if (ids != null) { - // Oh, oh. This symbolic id exists already - // Fallback strategy: remove existing entity with all it's references - val existingEntityData = entityDataByIdOrDie(ids) - val existingEntity = existingEntityData.createEntity(this) - removeEntity(existingEntity) - LOG.error( - """ + private fun assertUniqueSymbolicId(pEntityData: WorkspaceEntityData, symbolicId: SymbolicEntityId<*>?) { + if (symbolicId == null) return + val ids = indexes.symbolicIdIndex.getIdsByEntry(symbolicId) + if (ids != null) { + // Oh, oh. This symbolic id exists already + // Fallback strategy: remove existing entity with all it's references + val existingEntityData = entityDataByIdOrDie(ids) + val existingEntity = existingEntityData.createEntity(this) + removeEntity(existingEntity) + LOG.error( + """ addEntity: symbolic id already exists. Replacing entity with the new one. Symbolic id: $symbolicId @@ -306,8 +308,7 @@ internal class MutableEntityStorageImpl( Broken consistency: $brokenConsistency """.trimIndent(), SymbolicIdAlreadyExistsException(symbolicId) - ) - } + ) } } @@ -339,7 +340,8 @@ internal class MutableEntityStorageImpl( // Check for persistent id uniqueness if (beforeSymbolicId != null) { - val newSymbolicId = copiedData.symbolicId() + val immutableEntity = modifiableEntity.getEntityData().createEntity(this) + val newSymbolicId = if (immutableEntity is WorkspaceEntityWithSymbolicId) immutableEntity.symbolicId else null if (newSymbolicId != null) { val ids = indexes.symbolicIdIndex.getIdsByEntry(newSymbolicId) if (beforeSymbolicId != newSymbolicId && ids != null) { diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/StorageIndexes.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/StorageIndexes.kt index 73aa7a774489..5591055290ba 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/StorageIndexes.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/StorageIndexes.kt @@ -107,18 +107,21 @@ internal open class StorageIndexes( } */ + @OptIn(EntityStorageInstrumentationApi::class) private fun assertSymbolicIdIndex(storage: AbstractEntityStorage) { var expectedSize = 0 storage.entitiesByType.entityFamilies.forEachIndexed { i, family -> if (family == null) return@forEachIndexed - if (family.entities.firstOrNull { it != null }?.symbolicId() == null) return@forEachIndexed + val entityData = family.entities.firstOrNull { it != null }?.createEntity(storage) as? WorkspaceEntityWithSymbolicId + if (entityData?.symbolicId == null) return@forEachIndexed var mutableId = createEntityId(0, i) family.entities.forEach { data -> if (data == null) return@forEach mutableId = mutableId.copy(arrayId = data.id) val expectedSymbolicId = symbolicIdIndex.getEntryById(mutableId) - assert(expectedSymbolicId == data.symbolicId()) { "Entity $data isn't found in persistent id index. SymbolicId: ${data.symbolicId()}, Id: $mutableId. Expected entity source: $expectedSymbolicId" } + val symbolicId = (data.createEntity(storage) as? WorkspaceEntityWithSymbolicId)?.symbolicId + assert(expectedSymbolicId == symbolicId) { "Entity $data isn't found in persistent id index. SymbolicId: $symbolicId, Id: $mutableId. Expected entity source: $expectedSymbolicId" } expectedSize++ } } @@ -155,7 +158,7 @@ internal class MutableStorageIndexes( override val externalMappings: MutableMap, MutableExternalEntityMappingImpl<*>> ) : StorageIndexes(softLinks, virtualFileIndex, entitySourceIndex, symbolicIdIndex, externalMappings) { - fun entityAdded(entityData: WorkspaceEntityData) { + fun entityAdded(entityData: WorkspaceEntityData, symbolicId: SymbolicEntityId<*>?) { val pid = entityData.createEntityId() // Update soft links index @@ -166,7 +169,7 @@ internal class MutableStorageIndexes( val entitySource = entityData.entitySource entitySourceIndex.index(pid, entitySource) - entityData.symbolicId()?.let { symbolicId -> + if (symbolicId != null) { symbolicIdIndex.index(pid, symbolicId) } @@ -266,7 +269,7 @@ internal class MutableStorageIndexes( for (entityId in idsWithSoftRef) { val originalEntityData = builder.getOriginalEntityData(entityId) as WorkspaceEntityData val entity = builder.entitiesByType.getEntityDataForModification(entityId) as WorkspaceEntityData - val editingBeforeSymbolicId = entity.symbolicId() + val editingBeforeSymbolicId = (entity.createEntity(builder) as? WorkspaceEntityWithSymbolicId)?.symbolicId (entity as SoftLinkable).updateLink(beforeSymbolicId, newSymbolicId) // Add an entry to changelog diff --git a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Utils.kt b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Utils.kt index 55cefb45a9f0..5764eb3c3b69 100644 --- a/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Utils.kt +++ b/platform/workspace/storage/src/com/intellij/platform/workspace/storage/impl/Utils.kt @@ -4,6 +4,8 @@ package com.intellij.platform.workspace.storage.impl import com.intellij.platform.workspace.storage.EntityStorage import com.intellij.platform.workspace.storage.MutableEntityStorage import com.intellij.platform.workspace.storage.WorkspaceEntity +import com.intellij.platform.workspace.storage.WorkspaceEntityWithSymbolicId +import com.intellij.platform.workspace.storage.instrumentation.EntityStorageInstrumentationApi import com.intellij.platform.workspace.storage.instrumentation.Modification // Just a wrapper for entity id in THIS store @@ -36,11 +38,12 @@ internal fun loadClassByName(name: String, classLoader: ClassLoader): Class<*> { * This function checks if we try to add an entity as a child to itself. * It can't verify a circular dependency, and it's performed via a third entity */ +@OptIn(EntityStorageInstrumentationApi::class) internal fun checkCircularDependency(connectionId: ConnectionId, childId: Int, parentId: Int, storage: AbstractEntityStorage) { if (connectionId.parentClass == connectionId.childClass && childId == parentId) { val parentEntityId = createEntityId(parentId, connectionId.parentClass) val entityData = storage.entityDataByIdOrDie(parentEntityId) - val entityPresentation = entityData.symbolicId()?.toString() ?: entityData.toString() + val entityPresentation = (entityData.createEntity(storage) as? WorkspaceEntityWithSymbolicId)?.symbolicId?.toString() ?: entityData.toString() error("""Trying to make a circular dependency in entities by setting an entity as a child of itself. |Entity class: ${connectionId.parentClass.findWorkspaceEntity()} |Entity: $entityPresentation @@ -52,10 +55,11 @@ internal fun checkCircularDependency(connectionId: ConnectionId, childId: Int, p * This function checks if we try to add an entity as a child to itself. * It can't verify a circular dependency, and it's performed via a third entity */ +@OptIn(EntityStorageInstrumentationApi::class) internal fun checkCircularDependency(childId: EntityId, parentId: EntityId, storage: AbstractEntityStorage) { if (childId == parentId) { val entityData = storage.entityDataByIdOrDie(parentId) - val entityPresentation = entityData.symbolicId()?.toString() ?: entityData.toString() + val entityPresentation = (entityData.createEntity(storage) as? WorkspaceEntityWithSymbolicId)?.symbolicId?.toString() ?: entityData.toString() error("""Trying to make a circular dependency in entities by setting an entity as a child of itself. |Entity class: ${parentId.clazz.findWorkspaceEntity()} |Entity: $entityPresentation