1 // Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.workspaceModel.storage.impl
4 import com.esotericsoftware.kryo.Kryo
5 import com.esotericsoftware.kryo.Serializer
6 import com.esotericsoftware.kryo.io.Input
7 import com.esotericsoftware.kryo.io.Output
8 import com.esotericsoftware.kryo.serializers.DefaultSerializers
9 import com.esotericsoftware.kryo.serializers.FieldSerializer
10 import com.google.common.collect.HashBiMap
11 import com.google.common.collect.HashMultimap
12 import com.intellij.openapi.diagnostic.logger
13 import com.intellij.util.SmartList
14 import com.intellij.util.containers.*
15 import com.intellij.workspaceModel.storage.*
16 import com.intellij.workspaceModel.storage.impl.containers.ImmutableIntIntUniqueBiMap
17 import com.intellij.workspaceModel.storage.impl.containers.ImmutablePositiveIntIntBiMap
18 import com.intellij.workspaceModel.storage.impl.containers.ImmutablePositiveIntIntMultiMap
19 import com.intellij.workspaceModel.storage.impl.containers.LinkedBidirectionalMap
20 import com.intellij.workspaceModel.storage.impl.indices.EntityStorageInternalIndex
21 import com.intellij.workspaceModel.storage.impl.indices.MultimapStorageIndex
22 import com.intellij.workspaceModel.storage.impl.indices.VirtualFileIndex
23 import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap
24 import org.jetbrains.annotations.TestOnly
25 import org.objenesis.instantiator.ObjectInstantiator
26 import org.objenesis.strategy.StdInstantiatorStrategy
27 import java.io.InputStream
28 import java.io.OutputStream
30 import java.util.HashMap
31 import kotlin.collections.ArrayList
32 import kotlin.reflect.KVisibility
33 import kotlin.reflect.full.memberProperties
34 import kotlin.reflect.jvm.jvmName
36 class EntityStorageSerializerImpl(private val typesResolver: EntityTypesResolver,
37 private val virtualFileManager: VirtualFileUrlManager) : EntityStorageSerializer {
38 private val KRYO_BUFFER_SIZE = 64 * 1024
41 override var serializerDataFormatVersion: String = "v1"
43 private fun createKryo(): Kryo {
46 kryo.isRegistrationRequired = StrictMode.enabled
47 kryo.instantiatorStrategy = StdInstantiatorStrategy()
49 kryo.register(VirtualFileUrl::class.java, object : Serializer<VirtualFileUrl>(false, true) {
50 override fun write(kryo: Kryo, output: Output, obj: VirtualFileUrl) {
51 // TODO Write IDs only
52 output.writeString(obj.url)
55 override fun read(kryo: Kryo, input: Input, type: Class<VirtualFileUrl>): VirtualFileUrl =
56 virtualFileManager.fromUrl(input.readString())
59 kryo.register(EntityId::class.java, object : Serializer<EntityId>(false, true) {
60 override fun write(kryo: Kryo, output: Output, `object`: EntityId) {
61 output.writeInt(`object`.arrayId)
62 val typeClass = `object`.clazz.findEntityClass<WorkspaceEntity>()
63 val typeInfo = TypeInfo(typeClass.name, typesResolver.getPluginId(typeClass))
64 kryo.writeClassAndObject(output, typeInfo)
67 override fun read(kryo: Kryo, input: Input, type: Class<EntityId>): EntityId {
68 val arrayId = input.readInt()
69 val clazzInfo = kryo.readClassAndObject(input) as TypeInfo
70 val clazz = typesResolver.resolveClass(clazzInfo.name, clazzInfo.pluginId)
71 return EntityId(arrayId, clazz.toClassId())
75 kryo.register(HashMultimap::class.java, object : Serializer<HashMultimap<*, *>>(false, true) {
76 override fun write(kryo: Kryo, output: Output, `object`: HashMultimap<*, *>) {
77 val res = HashMap<Any, Collection<Any>>()
78 `object`.asMap().forEach { (key, values) ->
79 res[key] = ArrayList(values)
81 kryo.writeClassAndObject(output, res)
84 override fun read(kryo: Kryo, input: Input, type: Class<HashMultimap<*, *>>): HashMultimap<*, *> {
85 val res = HashMultimap.create<Any, Any>()
86 val map = kryo.readClassAndObject(input) as HashMap<*, Collection<*>>
87 map.forEach { (key, values) ->
88 res.putAll(key, values)
94 kryo.register(ConnectionId::class.java, object : Serializer<ConnectionId>(false, true) {
95 override fun write(kryo: Kryo, output: Output, `object`: ConnectionId) {
96 val parentClassType = `object`.parentClass.findEntityClass<WorkspaceEntity>()
97 val childClassType = `object`.childClass.findEntityClass<WorkspaceEntity>()
98 val parentTypeInfo = TypeInfo(parentClassType.name, typesResolver.getPluginId(parentClassType))
99 val childTypeInfo = TypeInfo(childClassType.name, typesResolver.getPluginId(childClassType))
101 kryo.writeClassAndObject(output, parentTypeInfo)
102 kryo.writeClassAndObject(output, childTypeInfo)
103 output.writeString(`object`.connectionType.name)
104 output.writeBoolean(`object`.isParentNullable)
105 output.writeBoolean(`object`.isChildNullable)
108 override fun read(kryo: Kryo, input: Input, type: Class<ConnectionId>): ConnectionId {
109 val parentClazzInfo = kryo.readClassAndObject(input) as TypeInfo
110 val childClazzInfo = kryo.readClassAndObject(input) as TypeInfo
112 val parentClass = typesResolver.resolveClass(parentClazzInfo.name, parentClazzInfo.pluginId) as Class<WorkspaceEntity>
113 val childClass = typesResolver.resolveClass(childClazzInfo.name, childClazzInfo.pluginId) as Class<WorkspaceEntity>
115 val connectionType = ConnectionId.ConnectionType.valueOf(input.readString())
116 val parentNullable = input.readBoolean()
117 val childNullable = input.readBoolean()
118 return ConnectionId.create(parentClass, childClass, connectionType, parentNullable, childNullable)
122 kryo.register(ImmutableEntitiesBarrel::class.java, object : Serializer<ImmutableEntitiesBarrel>(false, true) {
123 override fun write(kryo: Kryo, output: Output, `object`: ImmutableEntitiesBarrel) {
124 val res = HashMap<TypeInfo, EntityFamily<*>>()
125 `object`.entityFamilies.forEachIndexed { i, v ->
126 if (v == null) return@forEachIndexed
127 val clazz = i.findEntityClass<WorkspaceEntity>()
128 val typeInfo = TypeInfo(clazz.name, typesResolver.getPluginId(clazz))
131 kryo.writeClassAndObject(output, res)
134 override fun read(kryo: Kryo, input: Input, type: Class<ImmutableEntitiesBarrel>): ImmutableEntitiesBarrel {
135 val mutableBarrel = MutableEntitiesBarrel.create()
136 val families = kryo.readClassAndObject(input) as HashMap<TypeInfo, EntityFamily<*>>
137 for ((typeInfo, family) in families) {
138 val classId = typesResolver.resolveClass(typeInfo.name, typeInfo.pluginId).toClassId()
139 mutableBarrel.fillEmptyFamilies(classId)
140 mutableBarrel.entityFamilies[classId] = family
142 return mutableBarrel.toImmutable()
146 kryo.register(TypeInfo::class.java)
148 // TODO Dedup with OCSerializers
149 // TODO Reuse OCSerializer.registerUtilitySerializers ?
150 // TODO Scan OCSerializer for useful kryo settings and tricks
151 kryo.register(java.util.ArrayList::class.java).instantiator = ObjectInstantiator { ArrayList<Any>() }
152 kryo.register(HashMap::class.java).instantiator = ObjectInstantiator { HashMap<Any, Any>() }
153 kryo.register(SmartList::class.java).instantiator = ObjectInstantiator { SmartList<Any>() }
154 kryo.register(LinkedHashMap::class.java).instantiator = ObjectInstantiator { LinkedHashMap<Any, Any>() }
155 kryo.register(BidirectionalMap::class.java).instantiator = ObjectInstantiator { BidirectionalMap<Any, Any>() }
156 kryo.register(HashSet::class.java).instantiator = ObjectInstantiator { HashSet<Any>() }
157 kryo.register(BidirectionalMultiMap::class.java).instantiator = ObjectInstantiator { BidirectionalMultiMap<Any, Any>() }
158 kryo.register(HashBiMap::class.java).instantiator = ObjectInstantiator { HashBiMap.create<Any, Any>() }
159 kryo.register(LinkedBidirectionalMap::class.java).instantiator = ObjectInstantiator { LinkedBidirectionalMap<Any, Any>() }
160 kryo.register(Int2IntOpenHashMap::class.java).instantiator = ObjectInstantiator { Int2IntOpenHashMap() }
162 @Suppress("ReplaceJavaStaticMethodWithKotlinAnalog")
163 kryo.register(Arrays.asList("a").javaClass).instantiator = ObjectInstantiator { java.util.ArrayList<Any>() }
165 kryo.register(ByteArray::class.java)
166 kryo.register(ImmutableEntityFamily::class.java)
167 kryo.register(RefsTable::class.java)
168 kryo.register(ImmutablePositiveIntIntBiMap::class.java)
169 kryo.register(ImmutableIntIntUniqueBiMap::class.java)
170 kryo.register(VirtualFileIndex::class.java)
171 kryo.register(EntityStorageInternalIndex::class.java)
172 kryo.register(ImmutablePositiveIntIntMultiMap.ByList::class.java)
173 kryo.register(IntArray::class.java)
174 kryo.register(Pair::class.java)
175 kryo.register(MultimapStorageIndex::class.java)
176 kryo.register(VirtualFileIndex.VirtualFileUrlInfo::class.java)
178 registerFieldSerializer(kryo, Collections.unmodifiableCollection<Any>(emptySet()).javaClass) {
179 Collections.unmodifiableCollection(emptySet())
181 registerFieldSerializer(kryo, Collections.unmodifiableSet<Any>(emptySet()).javaClass) { Collections.unmodifiableSet(emptySet()) }
182 registerFieldSerializer(kryo, Collections.unmodifiableList<Any>(emptyList()).javaClass) { Collections.unmodifiableList(emptyList()) }
183 registerFieldSerializer(kryo, Collections.unmodifiableMap<Any, Any>(emptyMap()).javaClass) { Collections.unmodifiableMap(emptyMap()) }
185 kryo.register(Collections.EMPTY_LIST.javaClass, DefaultSerializers.CollectionsEmptyListSerializer())
186 kryo.register(Collections.EMPTY_MAP.javaClass, DefaultSerializers.CollectionsEmptyMapSerializer())
187 kryo.register(Collections.EMPTY_SET.javaClass, DefaultSerializers.CollectionsEmptySetSerializer())
188 kryo.register(listOf(null).javaClass, DefaultSerializers.CollectionsSingletonListSerializer())
189 kryo.register(Collections.singletonMap<Any, Any>(null, null).javaClass, DefaultSerializers.CollectionsSingletonMapSerializer())
190 kryo.register(setOf(null).javaClass, DefaultSerializers.CollectionsSingletonSetSerializer())
192 registerSingletonSerializer(kryo) { ContainerUtil.emptyList<Any>() }
193 registerSingletonSerializer(kryo) { MostlySingularMultiMap.emptyMap<Any, Any>() }
194 registerSingletonSerializer(kryo) { MultiMap.empty<Any, Any>() }
196 registerSingletonSerializer(kryo) { emptyMap<Any, Any>() }
197 registerSingletonSerializer(kryo) { emptyList<Any>() }
198 registerSingletonSerializer(kryo) { emptySet<Any>() }
203 // TODO Dedup with OCSerializer
204 private inline fun <reified T : Any> registerFieldSerializer(kryo: Kryo, type: Class<T> = T::class.java, crossinline create: () -> T) =
205 registerSerializer(kryo, type, FieldSerializer(kryo, type), ObjectInstantiator { create() })
208 private inline fun registerSingletonSerializer(kryo: Kryo, crossinline getter: () -> Any) {
209 val getter1 = ObjectInstantiator { getter() }
210 registerSerializer(kryo, getter1.newInstance().javaClass, EmptySerializer, getter1)
213 object EmptySerializer : Serializer<Any>(false, true) {
214 override fun write(kryo: Kryo?, output: Output?, `object`: Any?) {}
215 override fun read(kryo: Kryo, input: Input?, type: Class<Any>): Any? = kryo.newInstance(type)
218 private fun <T : Any> registerSerializer(kryo: Kryo, type: Class<T>, serializer: Serializer<in T>, initializer: ObjectInstantiator<T>) {
219 kryo.register(type, serializer).apply { instantiator = initializer }
223 * Collect all classes existing in entity data.
224 * [simpleClasses] - set of classes
225 * [objectClasses] - set of kotlin objects
227 private fun recursiveClassFinder(kryo: Kryo, entity: Any, simpleClasses: MutableSet<TypeInfo>, objectClasses: MutableSet<TypeInfo>) {
228 val kClass = entity::class
229 val typeInfo = TypeInfo(kClass.jvmName, typesResolver.getPluginId(kClass.java))
230 if (kryo.classResolver.getRegistration(kClass.java) != null) return
232 val objectInstance = kClass.objectInstance
233 if (objectInstance != null) {
234 objectClasses += typeInfo
237 simpleClasses += typeInfo
240 kClass.memberProperties.forEach {
241 val retType = (it.returnType as Any).toString()
243 if ((retType.startsWith("kotlin") || retType.startsWith("java"))
244 && !retType.startsWith("kotlin.collections.List")
245 && !retType.startsWith("java.util.List")
248 if (it.visibility != KVisibility.PUBLIC) return@forEach
249 val property = it.getter.call(entity) ?: return@forEach
250 recursiveClassFinder(kryo, property, simpleClasses, objectClasses)
252 if (property is List<*>) {
253 property.filterNotNull().forEach { listItem ->
254 recursiveClassFinder(kryo, listItem, simpleClasses, objectClasses)
260 override fun serializeCache(stream: OutputStream, storage: WorkspaceEntityStorage) {
261 storage as WorkspaceEntityStorageImpl
262 storage.assertConsistencyInStrictMode()
264 val output = Output(stream, KRYO_BUFFER_SIZE)
266 val kryo = createKryo()
269 output.writeString(serializerDataFormatVersion)
271 // Collect all classes existing in entity data
272 val simpleClasses = HashSet<TypeInfo>()
273 val objectClasses = HashSet<TypeInfo>()
274 storage.entitiesByType.entityFamilies.filterNotNull().forEach { family ->
275 family.entities.filterNotNull().forEach { recursiveClassFinder(kryo, it, simpleClasses, objectClasses) }
278 // Serialize and register types of kotlin objects
279 output.writeVarInt(objectClasses.size, true)
280 objectClasses.forEach {
281 kryo.register(typesResolver.resolveClass(it.name, it.pluginId))
282 kryo.writeClassAndObject(output, it)
285 // Serialize and register all types existing in entity data
286 output.writeVarInt(simpleClasses.size, true)
287 simpleClasses.forEach {
288 kryo.register(typesResolver.resolveClass(it.name, it.pluginId))
289 kryo.writeClassAndObject(output, it)
292 // Serialize and register persistent ids
293 val persistentIds = storage.indexes.persistentIdIndex.entries().toSet()
294 output.writeVarInt(persistentIds.size, true)
295 persistentIds.forEach {
296 val typeInfo = TypeInfo(it::class.jvmName, typesResolver.getPluginId(it::class.java))
297 kryo.register(it::class.java)
298 kryo.writeClassAndObject(output, typeInfo)
301 // Write entity data and references
302 kryo.writeClassAndObject(output, storage.entitiesByType)
303 kryo.writeClassAndObject(output, storage.refs)
306 kryo.writeClassAndObject(output, storage.indexes.softLinks)
307 kryo.writeClassAndObject(output, storage.indexes.virtualFileIndex)
308 kryo.writeClassAndObject(output, storage.indexes.entitySourceIndex)
309 kryo.writeClassAndObject(output, storage.indexes.persistentIdIndex)
316 override fun deserializeCache(stream: InputStream): WorkspaceEntityStorageBuilder? {
317 Input(stream, KRYO_BUFFER_SIZE).use { input ->
318 val kryo = createKryo()
321 val cacheVersion = input.readString()
322 if (cacheVersion != serializerDataFormatVersion) {
323 logger.info("Cache isn't loaded. Current version of cache: $serializerDataFormatVersion, version of cache file: $cacheVersion")
327 // Read and register all kotlin objects
328 val objectCount = input.readVarInt(true)
329 repeat(objectCount) {
330 val objectClass = kryo.readClassAndObject(input) as TypeInfo
331 registerSingletonSerializer(kryo) { typesResolver.resolveClass(objectClass.name, objectClass.pluginId).kotlin.objectInstance!! }
334 // Read and register all types in entity data
335 val nonObjectCount = input.readVarInt(true)
336 repeat(nonObjectCount) {
337 val objectClass = kryo.readClassAndObject(input) as TypeInfo
338 kryo.register(typesResolver.resolveClass(objectClass.name, objectClass.pluginId))
341 // Read and register persistent ids
342 val persistentIdCount = input.readVarInt(true)
343 repeat(persistentIdCount) {
344 val objectClass = kryo.readClassAndObject(input) as TypeInfo
345 kryo.register(typesResolver.resolveClass(objectClass.name, objectClass.pluginId))
348 // Read entity data and references
349 val entitiesBarrel = kryo.readClassAndObject(input) as ImmutableEntitiesBarrel
350 val refsTable = kryo.readClassAndObject(input) as RefsTable
353 val softLinks = kryo.readClassAndObject(input) as MultimapStorageIndex
354 val virtualFileIndex = kryo.readClassAndObject(input) as VirtualFileIndex
355 val entitySourceIndex = kryo.readClassAndObject(input) as EntityStorageInternalIndex<EntitySource>
356 val persistentIdIndex = kryo.readClassAndObject(input) as EntityStorageInternalIndex<PersistentEntityId<*>>
357 val storageIndexes = StorageIndexes(softLinks, virtualFileIndex, entitySourceIndex, persistentIdIndex)
360 val storage = WorkspaceEntityStorageImpl(entitiesBarrel, refsTable, storageIndexes)
361 storage.assertConsistencyInStrictMode()
362 val builder = WorkspaceEntityStorageBuilderImpl.from(storage)
364 builder.entitiesByType.entityFamilies.forEach { family ->
365 family?.entities?.asSequence()?.filterNotNull()?.forEach { entityData -> builder.createAddEvent(entityData) }
372 private data class TypeInfo(val name: String, val pluginId: String?)
375 val logger = logger<EntityStorageSerializerImpl>()