mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
reduce memory usage — same map binding for the same type and annotation set
GitOrigin-RevId: 695fc10b114200ee67f8e10914318511856f7622
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d05ecab4d8
commit
3e9153596a
@@ -28,6 +28,9 @@ internal class BeanBinding(beanClass: Class<*>) : BaseBeanBinding(beanClass), Ro
|
||||
computeNonDefaultConstructorInfo(beanClass)
|
||||
}
|
||||
|
||||
// type parameters for bean binding doesn't play any role, should be the only binding for such class
|
||||
override fun createCacheKey(aClass: Class<*>, type: Type) = aClass
|
||||
|
||||
override fun init(originalType: Type, context: BindingInitializationContext) {
|
||||
val list = context.propertyCollector.collect(beanClass)
|
||||
properties = list
|
||||
|
||||
@@ -22,6 +22,8 @@ interface NestedBinding : RootBinding {
|
||||
* 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) {
|
||||
}
|
||||
|
||||
|
||||
@@ -25,18 +25,27 @@ abstract class BindingProducer : BindingInitializationContext {
|
||||
abstract fun getNestedBinding(accessor: MutableAccessor): NestedBinding
|
||||
|
||||
fun getRootBinding(aClass: Class<*>, type: Type = aClass): RootBinding {
|
||||
val cacheKey = createCacheKey(aClass, type)
|
||||
return cacheLock.read {
|
||||
// create cache only under write lock
|
||||
cache.get(cacheKey)
|
||||
} ?: cacheLock.write {
|
||||
cache.get(cacheKey)?.let {
|
||||
fun getByTypeOrByClass(): RootBinding? {
|
||||
var result = cache.get(type)
|
||||
if (result == null && aClass !== type) {
|
||||
result = cache.get(aClass)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
cacheLock.read {
|
||||
getByTypeOrByClass()?.let {
|
||||
return it
|
||||
}
|
||||
}
|
||||
|
||||
cacheLock.write {
|
||||
getByTypeOrByClass()?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
createRootBinding(aClass, type, cacheKey)
|
||||
val binding = createRootBinding(aClass, type, cacheKey)
|
||||
cache.put(cacheKey, binding)
|
||||
val binding = createRootBinding(aClass, type)
|
||||
cache.put(binding.createCacheKey(aClass, type), binding)
|
||||
try {
|
||||
binding.init(type, this)
|
||||
}
|
||||
@@ -48,9 +57,7 @@ abstract class BindingProducer : BindingInitializationContext {
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun createCacheKey(aClass: Class<*>, type: Type) = type
|
||||
|
||||
protected abstract fun createRootBinding(aClass: Class<*>, type: Type, cacheKey: Type): RootBinding
|
||||
protected abstract fun createRootBinding(aClass: Class<*>, type: Type): RootBinding
|
||||
|
||||
@Suppress("unused")
|
||||
fun clearBindingCache() {
|
||||
|
||||
@@ -10,6 +10,7 @@ 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 {
|
||||
private val itemBinding = createElementBindingByType(itemType, context)
|
||||
|
||||
|
||||
@@ -43,46 +43,39 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
|
||||
}
|
||||
}
|
||||
|
||||
// type parameters for bean binding doesn't play any role, should be the only binding for such class
|
||||
override fun createCacheKey(aClass: Class<*>, type: Type): Type {
|
||||
when {
|
||||
aClass !== type && !Collection::class.java.isAssignableFrom(aClass) && !classToRootBindingFactory.contains(aClass) -> return aClass
|
||||
else -> return type
|
||||
}
|
||||
}
|
||||
|
||||
override fun createRootBinding(aClass: Class<*>, type: Type, cacheKey: Type): RootBinding {
|
||||
override fun createRootBinding(aClass: Class<*>, type: Type): RootBinding {
|
||||
val customFactory = classToRootBindingFactory.get(aClass)
|
||||
if (customFactory != null) {
|
||||
return customFactory.invoke()
|
||||
}
|
||||
|
||||
return when {
|
||||
Collection::class.java.isAssignableFrom(aClass) -> CollectionBinding(type as ParameterizedType, this)
|
||||
Collection::class.java.isAssignableFrom(aClass) -> {
|
||||
CollectionBinding(type as ParameterizedType, this)
|
||||
}
|
||||
Map::class.java.isAssignableFrom(aClass) -> {
|
||||
val typeArguments = (type as ParameterizedType).actualTypeArguments
|
||||
MapBinding(typeArguments[0], typeArguments[1], this)
|
||||
}
|
||||
aClass.isArray -> ArrayBinding(aClass.componentType, this)
|
||||
aClass.isArray -> {
|
||||
ArrayBinding(aClass.componentType, this)
|
||||
}
|
||||
aClass.isEnum -> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
EnumBinding(aClass as Class<out Enum<*>>)
|
||||
}
|
||||
aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> {
|
||||
PolymorphicBinding(aClass)
|
||||
}
|
||||
aClass is Proxy -> {
|
||||
throw SerializationException("$aClass class is not supported")
|
||||
}
|
||||
aClass == Class::class.java -> {
|
||||
// Class can be supported, but it will be implemented only when will be a real use case
|
||||
throw SerializationException("$aClass class is not supported")
|
||||
}
|
||||
else -> {
|
||||
assert(cacheKey === aClass)
|
||||
if (aClass.isInterface || Modifier.isAbstract(aClass.modifiers)) {
|
||||
PolymorphicBinding(aClass)
|
||||
}
|
||||
else if (aClass is Proxy) {
|
||||
throw SerializationException("$aClass class is not supported")
|
||||
}
|
||||
else if (aClass == Class::class.java) {
|
||||
// Class can be supported, but it will be implemented only when will be a real use case
|
||||
throw SerializationException("$aClass class is not supported")
|
||||
}
|
||||
else {
|
||||
BeanBinding(aClass)
|
||||
}
|
||||
BeanBinding(aClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,12 +91,7 @@ internal class IonBindingProducer(override val propertyCollector: PropertyCollec
|
||||
return it(accessor)
|
||||
}
|
||||
|
||||
// CollectionBinding implements NestedBinding directly because can mutate property value directly
|
||||
return when {
|
||||
Map::class.java.isAssignableFrom(aClass) -> {
|
||||
val typeArguments = (type as ParameterizedType).actualTypeArguments
|
||||
MapBinding(typeArguments[0], typeArguments[1], this)
|
||||
}
|
||||
aClass.isArray -> ArrayBinding(aClass.componentType, this)
|
||||
java.lang.Number::class.java.isAssignableFrom(aClass) -> PropertyBinding(NumberAsObjectBinding())
|
||||
aClass.isInterface || Modifier.isAbstract(aClass.modifiers) -> {
|
||||
|
||||
@@ -6,6 +6,7 @@ import gnu.trove.THashMap
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TestName
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.util.*
|
||||
|
||||
class MapTest {
|
||||
@@ -17,6 +18,22 @@ class MapTest {
|
||||
return test(bean, testName, writeConfiguration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `same map binding for the same type and annotation set`() {
|
||||
val serializer = ObjectSerializer()
|
||||
val bindingProducer = getBindingProducer(serializer)
|
||||
|
||||
@Suppress("unused")
|
||||
class TestBean {
|
||||
@JvmField
|
||||
val a: MutableMap<String, String> = THashMap()
|
||||
val b: MutableMap<String, String> = THashMap()
|
||||
}
|
||||
|
||||
serializer.write(TestBean(), ByteArrayOutputStream())
|
||||
assertThat(getBindingCount(bindingProducer)).isEqualTo(3 /* TestBean/String/Map */)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun map() {
|
||||
val bean = TestMapBean()
|
||||
|
||||
Reference in New Issue
Block a user