reduce memory usage — no need to wrap into PropertyBinding

GitOrigin-RevId: 53b9d1a8e6ca43eb47a2ed6925dcfe58524c1a70
This commit is contained in:
Vladimir Krivosheev
2019-05-17 11:03:13 +03:00
committed by intellij-monorepo-bot
parent cfe1470ff2
commit 6ffab79f8d
13 changed files with 93 additions and 140 deletions
@@ -19,8 +19,8 @@ private val structReaderBuilder by lazy {
private const val ID_FIELD_NAME = "@id"
internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), RootBinding {
private lateinit var bindings: Array<NestedBinding>
internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Binding {
private lateinit var bindings: Array<Binding>
private lateinit var nameToBindingIndex: ObjectIntHashMap<String>
private lateinit var properties: List<MutableAccessor>
+38 -18
View File
@@ -1,27 +1,22 @@
// Copyright 2000-2019 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.
package com.intellij.serialization
import com.amazon.ion.IonType
import java.lang.reflect.Type
interface Binding {
}
internal interface Binding {
fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) {
write(hostObject, property, context) {
serialize(it, context)
}
}
/**
* Binding that can read and write data of object properties.
*
* Each NestedBinding also is a [RootBinding] not only because of ability to serialize any type as root (including primitives),
* but because of support for non-default class constructors - for this case BeanBinding will use nested binding as root binding (read value without passing property).
*/
interface NestedBinding : RootBinding {
fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext)
fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) {
read(hostObject, property, context) {
deserialize(context)
}
}
fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext)
}
/**
* Binding that can read and write data of root object.
*/
interface RootBinding : Binding {
fun createCacheKey(aClass: Class<*>, type: Type = aClass) = type
fun init(originalType: Type, context: BindingInitializationContext) {
@@ -32,10 +27,35 @@ interface RootBinding : Binding {
fun deserialize(context: ReadContext): Any
}
interface BindingInitializationContext {
internal interface BindingInitializationContext {
val propertyCollector: PropertyCollector
val bindingProducer: BindingProducer
val isResolveConstructorOnInit: Boolean
get() = false
}
internal inline fun write(hostObject: Any, accessor: MutableAccessor, context: WriteContext, write: ValueWriter.(value: Any) -> Unit) {
val value = accessor.readUnsafe(hostObject)
if (context.filter.isSkipped(value)) {
return
}
val writer = context.writer
writer.setFieldName(accessor.name)
if (value == null) {
writer.writeNull()
}
else {
writer.write(value)
}
}
internal inline fun read(hostObject: Any, property: MutableAccessor, context: ReadContext, read: ValueReader.() -> Any) {
if (context.reader.type == IonType.NULL) {
property.set(hostObject, null)
}
else {
property.set(hostObject, context.reader.read())
}
}
@@ -9,8 +9,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
abstract class BindingProducer : BindingInitializationContext {
private val cache: MutableMap<Type, RootBinding> = THashMap()
internal abstract class BindingProducer : BindingInitializationContext {
private val cache: MutableMap<Type, Binding> = THashMap()
private val cacheLock = ReentrantReadWriteLock()
@get:TestOnly
@@ -22,10 +22,10 @@ abstract class BindingProducer : BindingInitializationContext {
override val isResolveConstructorOnInit = SystemProperties.`is`("idea.serializer.resolve.ctor.on.init")
abstract fun getNestedBinding(accessor: MutableAccessor): NestedBinding
abstract fun getNestedBinding(accessor: MutableAccessor): Binding
fun getRootBinding(aClass: Class<*>, type: Type = aClass): RootBinding {
fun getByTypeOrByClass(): RootBinding? {
fun getRootBinding(aClass: Class<*>, type: Type = aClass): Binding {
fun getByTypeOrByClass(): Binding? {
var result = cache.get(type)
if (result == null && aClass !== type) {
result = cache.get(aClass)
@@ -57,7 +57,7 @@ abstract class BindingProducer : BindingInitializationContext {
}
}
protected abstract fun createRootBinding(aClass: Class<*>, type: Type): RootBinding
protected abstract fun createRootBinding(aClass: Class<*>, type: Type): Binding
@Suppress("unused")
fun clearBindingCache() {
@@ -10,16 +10,9 @@ import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.function.Consumer
// CollectionBinding implements NestedBinding directly because can mutate property value directly
internal abstract class BaseCollectionBinding(itemType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding {
internal abstract class BaseCollectionBinding(itemType: Type, context: BindingInitializationContext) : Binding {
private val itemBinding = createElementBindingByType(itemType, context)
final override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) {
write(hostObject, property, context) {
serialize(it, context)
}
}
protected fun createItemConsumer(context: WriteContext): Consumer<Any?> {
val writer = context.writer
return Consumer {
@@ -120,12 +113,6 @@ internal class ArrayBinding(private val itemClass: Class<*>, context: BindingIni
writer.stepOut()
}
override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) {
read(hostObject, property, context) {
readArray(context)
}
}
private fun readArray(context: ReadContext): Array<out Any> {
val list = ArrayList<Any?>()
readInto(list, context)
@@ -2,7 +2,7 @@
package com.intellij.serialization
@Suppress("ArrayInDataClass")
internal data class InterfacePropertyBinding(private val allowedTypes: Array<Class<*>>) : NestedBinding {
internal data class InterfacePropertyBinding(private val allowedTypes: Array<Class<*>>) : Binding {
override fun serialize(obj: Any, context: WriteContext) = throw IllegalStateException("InterfacePropertyBinding cannot be used as root binding")
override fun deserialize(context: ReadContext) = throw IllegalStateException("InterfacePropertyBinding cannot be used as root binding")
@@ -1,7 +1,6 @@
// Copyright 2000-2019 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.
package com.intellij.serialization
import com.amazon.ion.IonType
import com.amazon.ion.Timestamp
import com.intellij.util.containers.ContainerUtil
import gnu.trove.THashMap
@@ -14,8 +13,8 @@ import java.nio.file.FileSystems
import java.nio.file.Path
import java.util.*
internal typealias NestedBindingFactory = (accessor: MutableAccessor) -> NestedBinding
internal typealias RootBindingFactory = () -> RootBinding
internal typealias NestedBindingFactory = (accessor: MutableAccessor) -> Binding
internal typealias RootBindingFactory = () -> Binding
internal class IonBindingProducer(override val propertyCollector: PropertyCollector) : BindingProducer() {
companion object {
@@ -37,13 +36,13 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
registerPrimitiveBindings(classToRootBindingFactory, classToNestedBindingFactory)
classToRootBindingFactory.forEachEntry { key, factory ->
classToNestedBindingFactory.put(key) { PropertyBinding(factory()) }
classToNestedBindingFactory.put(key) { factory() }
true
}
}
}
override fun createRootBinding(aClass: Class<*>, type: Type): RootBinding {
override fun createRootBinding(aClass: Class<*>, type: Type): Binding {
val customFactory = classToRootBindingFactory.get(aClass)
if (customFactory != null) {
return customFactory.invoke()
@@ -67,6 +66,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> {
PolymorphicBinding(aClass)
}
java.lang.Number::class.java.isAssignableFrom(aClass) -> NumberAsObjectBinding()
aClass is Proxy -> {
throw SerializationException("$aClass class is not supported")
}
@@ -81,7 +81,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
}
// note about field name - Ion binary writer interns string automatically, no need to intern (text writer doesn't support symbol tables)
override fun getNestedBinding(accessor: MutableAccessor): NestedBinding {
override fun getNestedBinding(accessor: MutableAccessor): Binding {
val type = accessor.genericType
val aClass = ClassUtil.typeToClass(type)
@@ -92,13 +92,11 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
}
return when {
aClass.isArray -> ArrayBinding(aClass.componentType, this)
java.lang.Number::class.java.isAssignableFrom(aClass) -> PropertyBinding(NumberAsObjectBinding())
aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> {
val annotation = accessor.getAnnotation(Property::class.java)
if (annotation == null) {
// todo respect configuration
return PropertyBinding(getRootBinding(aClass, type))
return getRootBinding(aClass, type)
// throw SerializationException("Allowed types are not specified", linkedMapOf("accessor" to accessor))
}
@@ -110,17 +108,13 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
InterfacePropertyBinding(allowedTypes)
}
else -> {
val binding = getRootBinding(aClass, type)
when (binding) {
is NestedBinding -> binding
else -> PropertyBinding(binding)
}
getRootBinding(aClass, type)
}
}
}
}
private class FileBinding : RootBinding {
private class FileBinding : Binding {
override fun deserialize(context: ReadContext): Any {
return File(context.reader.stringValue())
}
@@ -130,7 +124,7 @@ private class FileBinding : RootBinding {
}
}
private class PathBinding : RootBinding {
private class PathBinding : Binding {
override fun deserialize(context: ReadContext): Any {
return FileSystems.getDefault().getPath(context.reader.stringValue())
}
@@ -140,7 +134,7 @@ private class PathBinding : RootBinding {
}
}
private class EnumBinding(private val valueClass: Class<out Enum<*>>) : RootBinding {
private class EnumBinding(private val valueClass: Class<out Enum<*>>) : Binding {
override fun deserialize(context: ReadContext): Any {
val enumConstants = valueClass.enumConstants
val value = context.reader.stringValue()
@@ -154,7 +148,7 @@ private class EnumBinding(private val valueClass: Class<out Enum<*>>) : RootBind
}
}
private class DateBinding : RootBinding {
private class DateBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeTimestamp(Timestamp.forDateZ(obj as Date))
}
@@ -164,33 +158,7 @@ private class DateBinding : RootBinding {
}
}
internal inline fun write(hostObject: Any, accessor: MutableAccessor, context: WriteContext, write: ValueWriter.(value: Any) -> Unit) {
val value = accessor.readUnsafe(hostObject)
if (context.filter.isSkipped(value)) {
return
}
val writer = context.writer
writer.setFieldName(accessor.name)
if (value == null) {
writer.writeNull()
}
else {
writer.write(value)
}
}
internal inline fun read(hostObject: Any, property: MutableAccessor, context: ReadContext, read: ValueReader.() -> Any) {
val type = context.reader.type
if (type == IonType.NULL) {
property.set(hostObject, null)
}
else {
property.set(hostObject, context.reader.read())
}
}
private class ByteArrayBinding : RootBinding {
private class ByteArrayBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeBlob(obj as ByteArray)
}
@@ -200,6 +168,6 @@ private class ByteArrayBinding : RootBinding {
}
}
internal fun createElementBindingByType(type: Type, context: BindingInitializationContext): RootBinding {
internal fun createElementBindingByType(type: Type, context: BindingInitializationContext): Binding {
return context.bindingProducer.getRootBinding(ClassUtil.typeToClass(type), type)
}
+2 -8
View File
@@ -7,18 +7,12 @@ import gnu.trove.THashMap
import java.lang.reflect.Type
import java.util.*
internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitializationContext) : RootBinding, NestedBinding {
internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitializationContext) : Binding {
private val keyBinding = createElementBindingByType(keyType, context)
private val valueBinding = createElementBindingByType(valueType, context)
private val isKeyComparable = Comparable::class.java.isAssignableFrom(ClassUtil.typeToClass(keyType))
override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) {
write(hostObject, property, context) {
serialize(it, context)
}
}
override fun serialize(obj: Any, context: WriteContext) {
val writer = context.writer
val map = obj as Map<*, *>
@@ -103,7 +97,7 @@ internal class MapBinding(keyType: Type, valueType: Type, context: BindingInitia
reader.stepOut()
}
private fun read(type: IonType, binding: RootBinding, context: ReadContext): Any? {
private fun read(type: IonType, binding: Binding, context: ReadContext): Any? {
return when (type) {
IonType.NULL -> null
else -> binding.deserialize(context)
@@ -1,7 +1,7 @@
// Copyright 2000-2019 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.
package com.intellij.serialization
internal class PolymorphicBinding(private val superClass: Class<*>) : RootBinding {
internal class PolymorphicBinding(private val superClass: Class<*>) : Binding {
override fun serialize(obj: Any, context: WriteContext) {
val valueClass = obj.javaClass
val writer = context.writer
@@ -1,22 +0,0 @@
// Copyright 2000-2019 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.
package com.intellij.serialization
internal data class PropertyBinding(private val binding: RootBinding) : NestedBinding {
override fun serialize(obj: Any, context: WriteContext) {
binding.serialize(obj, context)
}
override fun deserialize(context: ReadContext) = binding.deserialize(context)
override fun serialize(hostObject: Any, property: MutableAccessor, context: WriteContext) {
write(hostObject, property, context) {
binding.serialize(it, context)
}
}
override fun deserialize(hostObject: Any, property: MutableAccessor, context: ReadContext) {
read(hostObject, property, context) {
binding.deserialize(context)
}
}
}
+6 -6
View File
@@ -14,13 +14,13 @@ data class WriteConfiguration(val binary: Boolean = true,
val orderMapEntriesByKeys: Boolean = false,
val allowAnySubTypes: Boolean = false)
data class WriteContext(val writer: ValueWriter,
val filter: SerializationFilter,
val objectIdWriter: ObjectIdWriter?,
val configuration: WriteConfiguration,
val bindingProducer: BindingProducer)
internal data class WriteContext(val writer: ValueWriter,
val filter: SerializationFilter,
val objectIdWriter: ObjectIdWriter?,
val configuration: WriteConfiguration,
val bindingProducer: BindingProducer)
interface ReadContext {
internal interface ReadContext {
val reader: ValueReader
val objectIdReader: ObjectIdReader
val bindingProducer: BindingProducer
@@ -1,7 +1,9 @@
// Copyright 2000-2019 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.
package com.intellij.serialization
private fun resolved(binding: NestedBinding): NestedBindingFactory = { binding }
import java.lang.reflect.Type
private fun resolved(binding: Binding): NestedBindingFactory = { binding }
internal fun registerPrimitiveBindings(classToRootBindingFactory: MutableMap<Class<*>, RootBindingFactory>, classToNestedBindingFactory: MutableMap<Class<*>, NestedBindingFactory>) {
classToRootBindingFactory.put(java.lang.String::class.java) { StringBinding() }
@@ -34,7 +36,7 @@ internal fun registerPrimitiveBindings(classToRootBindingFactory: MutableMap<Cla
classToNestedBindingFactory.put(java.lang.Byte::class.java, byte)
}
private class FloatAsObjectBinding : RootBinding {
private class FloatAsObjectBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeFloat((obj as Float).toDouble())
}
@@ -42,7 +44,7 @@ private class FloatAsObjectBinding : RootBinding {
override fun deserialize(context: ReadContext) = context.reader.doubleValue().toFloat()
}
private class DoubleAsObjectBinding : RootBinding {
private class DoubleAsObjectBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeFloat(obj as Double)
}
@@ -50,7 +52,9 @@ private class DoubleAsObjectBinding : RootBinding {
override fun deserialize(context: ReadContext) = context.reader.doubleValue()
}
internal class NumberAsObjectBinding : RootBinding {
internal class NumberAsObjectBinding : Binding {
override fun createCacheKey(aClass: Class<*>, type: Type) = aClass
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeInt((obj as Number).toLong())
}
@@ -58,7 +62,7 @@ internal class NumberAsObjectBinding : RootBinding {
override fun deserialize(context: ReadContext) = context.reader.intValue()
}
private class BooleanAsObjectBinding : RootBinding {
private class BooleanAsObjectBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeBool(obj as Boolean)
}
@@ -66,7 +70,7 @@ private class BooleanAsObjectBinding : RootBinding {
override fun deserialize(context: ReadContext) = context.reader.booleanValue()
}
private class BooleanBinding : NestedBinding, RootBinding {
private class BooleanBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeBool(obj as Boolean)
}
@@ -84,7 +88,7 @@ private class BooleanBinding : NestedBinding, RootBinding {
}
}
private open class IntBinding : NestedBinding, RootBinding {
private open class IntBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeInt(obj as Long)
}
@@ -108,7 +112,7 @@ private class ShortBinding : IntBinding() {
}
}
private class LongBinding : NestedBinding, RootBinding {
private class LongBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeInt(obj as Long)
}
@@ -126,7 +130,7 @@ private class LongBinding : NestedBinding, RootBinding {
}
}
private class FloatBinding : NestedBinding, RootBinding {
private class FloatBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeFloat(obj as Double)
}
@@ -144,7 +148,7 @@ private class FloatBinding : NestedBinding, RootBinding {
}
}
private class DoubleBinding : NestedBinding, RootBinding {
private class DoubleBinding : Binding {
override fun serialize(obj: Any, context: WriteContext) {
context.writer.writeFloat(obj as Double)
}
@@ -162,7 +166,7 @@ private class DoubleBinding : NestedBinding, RootBinding {
}
}
private class StringBinding : RootBinding {
private class StringBinding : Binding {
override fun deserialize(context: ReadContext): Any {
return context.reader.stringValue()
}
@@ -1,6 +1,8 @@
// Copyright 2000-2019 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.
package com.intellij.serialization
fun getBindingProducer(serializer: ObjectSerializer): BindingProducer = serializer.serializer.bindingProducer
fun getBinding(aClass: Class<*>, serializer: ObjectSerializer): Any = serializer.serializer.bindingProducer.getRootBinding(aClass)
fun getBindingCount(producer: BindingProducer) = producer.bindingCount
fun getBindingProducer(serializer: ObjectSerializer): Any = serializer.serializer.bindingProducer
fun getBindingCount(producer: Any) = (producer as BindingProducer).bindingCount
@@ -23,9 +23,9 @@ class ObjectSerializerTest {
@Test
fun `same bean binding regardless of type parameters`() {
val bindingProducer = getBindingProducer(ObjectSerializer())
bindingProducer.getRootBinding(TestGenericBean::class.java)
assertThat(getBindingCount(bindingProducer)).isEqualTo(1)
val serializer = ObjectSerializer()
getBinding(TestGenericBean::class.java, serializer)
assertThat(getBindingCount(getBindingProducer(serializer))).isEqualTo(1)
}
@Test